Major documentation restructure to improve discoverability and reduce duplication. ## Changes **Deleted (Archived/Consolidated)**: - Removed duplicate getting started guides - Archived outdated planning documents - Consolidated corpus and configuration docs - Removed obsolete vision/spec files (superseded by vision.md) - Cleaned up scrapyard and old PDFs **New Structure**: - docs/about/ - Project overview and introduction - docs/guides/ - User guides (moved from root) - docs/specs/ - Technical specifications - docs/sdk/ - SDK documentation (Go) - docs/references/ - API references - docs/archive/ - Archived historical docs - applications/aphoria/docs/advanced/ - Advanced topics - applications/aphoria/docs/reference/ - CLI reference - applications/aphoria/docs/archive/ - Archived aphoria docs **Updated**: - README.md - New root README with clear navigation - CONTRIBUTING.md - Contribution guidelines - CLAUDE.md - Updated paths to new structure - roadmap.md - Added recent completions ## Files Changed - 57 files changed - 1,977 insertions(+) - 961 deletions(-) **Net change**: +1,016 lines (added CONTRIBUTING.md, README.md, reorganized content) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
449 lines
14 KiB
Markdown
449 lines
14 KiB
Markdown
# Aphoria Claims API — Sidecar Service
|
|
|
|
> **Goal:** A lightweight HTTP API that exposes the same claim operations the `aphoria-claims` Claude Code skill performs — review diffs, identify claimable patterns, check existing claims, suggest new claims, create/update/verify claims — so any tool, agent, or CI pipeline can build claim authoring flows.
|
|
>
|
|
> **Key insight:** The skill is just an LLM calling CLI commands. This API replaces the CLI calls with HTTP endpoints and replaces the Claude skill prompt with a Gemini call for the reasoning. Same workflow, any client.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌──────────────────────────┐
|
|
│ Any Client │
|
|
│ (CI, IDE, ADK agent, │
|
|
│ custom UI, webhook) │
|
|
└───────────┬──────────────┘
|
|
│ HTTP
|
|
▼
|
|
┌──────────────────────────┐
|
|
│ aphoria-claims-api │
|
|
│ (Rust, axum, port 18189) │
|
|
│ │
|
|
│ ┌─────────┐ ┌─────────┐ │
|
|
│ │ Claims │ │ Gemini │ │
|
|
│ │ Engine │ │ Client │ │
|
|
│ └────┬────┘ └────┬────┘ │
|
|
│ │ │ │
|
|
│ ┌────▼───────────▼────┐ │
|
|
│ │ aphoria lib crate │ │
|
|
│ │ (ClaimsFile, │ │
|
|
│ │ verify_claims, │ │
|
|
│ │ extract_claims, │ │
|
|
│ │ run_scan) │ │
|
|
│ └─────────────────────┘ │
|
|
└──────────────────────────┘
|
|
│
|
|
┌───────────▼──────────────┐
|
|
│ .aphoria/claims.toml │
|
|
│ (project claim store) │
|
|
└──────────────────────────┘
|
|
```
|
|
|
|
The sidecar calls `aphoria` as a library crate (not shelling out to CLI). It links against the same types: `ClaimsFile`, `AuthoredClaim`, `Observation`, `verify_claims()`, `extract_claims()`.
|
|
|
|
For the reasoning parts (identifying claimable patterns in diffs, suggesting claims), it calls Gemini via the HTTP API.
|
|
|
|
---
|
|
|
|
## Why a Sidecar, Not Extending stemedb-api
|
|
|
|
Aphoria claims are file-based (`.aphoria/claims.toml`) and project-scoped. The StemeDB API serves the knowledge graph (assertions, lenses, queries). These are different concerns:
|
|
|
|
| | stemedb-api (18180) | aphoria-claims-api (18189) |
|
|
|---|---|---|
|
|
| **Data** | Episteme assertions (append-only DAG) | Authored claims (TOML file) |
|
|
| **Scope** | Cluster-wide knowledge | Single project |
|
|
| **Storage** | WAL + KV | `.aphoria/claims.toml` |
|
|
| **Auth** | API keys, per-agent | Local only (or simple token) |
|
|
|
|
The sidecar runs alongside a project checkout. It needs filesystem access to the project root (for claims.toml, extractors, git).
|
|
|
|
---
|
|
|
|
## API Surface
|
|
|
|
### Claims CRUD
|
|
|
|
These mirror `aphoria claims create|list|explain|update|supersede|deprecate`:
|
|
|
|
```
|
|
POST /v1/claims Create a claim
|
|
GET /v1/claims List claims (filter by ?category=&status=&format=json)
|
|
GET /v1/claims/:id Get a single claim
|
|
PATCH /v1/claims/:id Update claim fields
|
|
POST /v1/claims/:id/supersede Create superseding claim
|
|
DELETE /v1/claims/:id Deprecate a claim (body: {reason})
|
|
```
|
|
|
|
**Request/response types use the existing `AuthoredClaim` struct** serialized as JSON. The API reads/writes `.aphoria/claims.toml` via `ClaimsFile`.
|
|
|
|
### Verification
|
|
|
|
Mirrors `aphoria verify run`:
|
|
|
|
```
|
|
POST /v1/verify Run verification (claims vs observations)
|
|
GET /v1/verify/map Show claim-to-extractor mapping
|
|
```
|
|
|
|
**POST /v1/verify** body:
|
|
```json
|
|
{
|
|
"path": ".",
|
|
"show_unclaimed": true,
|
|
"categories": ["safety", "imports"],
|
|
"claims": ["wallet-seqcst-001"]
|
|
}
|
|
```
|
|
|
|
**Response:** `VerifyReport` as JSON — per-claim verdicts (pass/conflict/missing/unclaimed) + summary counts.
|
|
|
|
### Coverage (A5.1)
|
|
|
|
```
|
|
GET /v1/coverage Coverage metrics per module
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"project": "maxwell",
|
|
"summary": {
|
|
"total_observations": 67,
|
|
"total_claims": 12,
|
|
"claimed_percentage": 45.2,
|
|
"unclaimed_count": 37
|
|
},
|
|
"modules": [
|
|
{
|
|
"module_path": "wallet/atomics",
|
|
"observation_count": 5,
|
|
"claim_count": 3,
|
|
"density": 0.6
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Diff Review (A5.3 — the reasoning endpoint)
|
|
|
|
This is the one that calls Gemini. It does what the `aphoria-claims` skill does:
|
|
|
|
```
|
|
POST /v1/review Review a diff for claimable patterns
|
|
```
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"diff": "... unified diff text ...",
|
|
"context": {
|
|
"repo": "maxwell",
|
|
"branch": "feat/new-ordering"
|
|
}
|
|
}
|
|
```
|
|
|
|
Internally:
|
|
1. Load existing claims from `.aphoria/claims.toml`
|
|
2. Run extractors on changed files to get observations
|
|
3. Run `verify_claims()` to check for violations
|
|
4. Send diff + existing claims + observations to Gemini with the claim-identification prompt
|
|
5. Return structured suggestions
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"violations": [
|
|
{
|
|
"claim_id": "wallet-seqcst-001",
|
|
"invariant": "All wallet atomics MUST use SeqCst",
|
|
"violation": "Ordering::Relaxed at sync.rs:42",
|
|
"action": "fix_code_or_supersede"
|
|
}
|
|
],
|
|
"suggestions": [
|
|
{
|
|
"observation": {
|
|
"file": "src/pool.rs",
|
|
"line": 15,
|
|
"matched_text": "const MAX_POOL_SIZE: u32 = 50;"
|
|
},
|
|
"suggested_claim": {
|
|
"id": "maxwell-pool-max-001",
|
|
"concept_path": "maxwell/db/pool/max_size",
|
|
"predicate": "max_value",
|
|
"value": "50",
|
|
"category": "constants",
|
|
"invariant": "Database pool size MUST NOT exceed 50",
|
|
"consequence": "OOM under sustained load",
|
|
"authority_tier": "observational"
|
|
},
|
|
"reason": "New constant with non-obvious value. Similar to 2 existing claims about pool configuration.",
|
|
"confidence": 0.85
|
|
}
|
|
],
|
|
"no_claim_needed": [
|
|
{
|
|
"pattern": "whitespace change in types.rs",
|
|
"reason": "Internal refactor, no behavioral change"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Docs Generation (A5.2)
|
|
|
|
```
|
|
POST /v1/docs/generate Generate claims-explained documentation
|
|
```
|
|
|
|
**Response:** Markdown string or JSON with full provenance chains, verification status, coverage gaps.
|
|
|
|
### Onboarding (A5.4)
|
|
|
|
```
|
|
GET /v1/explain Narrative project overview from claims
|
|
```
|
|
|
|
**Response:** Markdown narrative: architectural boundaries, safety invariants, key constants with provenance, coverage gaps.
|
|
|
|
---
|
|
|
|
## Gemini Integration
|
|
|
|
The reasoning endpoints (`/v1/review`, docs generation, onboarding narrative) call Gemini for LLM tasks.
|
|
|
|
**Config:**
|
|
```toml
|
|
# In aphoria.toml or env vars
|
|
[claims_api]
|
|
gemini_model = "gemini-2.5-flash"
|
|
```
|
|
|
|
```
|
|
GEMINI_API_KEY=AIzaSy... # env var, never in config files
|
|
```
|
|
|
|
**Gemini calls are structured, not conversational.** Each call has:
|
|
1. A system prompt (the same logic from the `aphoria-claims` SKILL.md — claimability rules, category reference, authority tier guide)
|
|
2. Structured input (diff text, existing claims as JSON, observations as JSON)
|
|
3. Structured output (JSON schema for suggestions)
|
|
|
|
The prompt is essentially the skill document converted to a system prompt, with the human-in-the-loop parts replaced by structured JSON output.
|
|
|
|
### Prompt Structure for `/v1/review`
|
|
|
|
```
|
|
System: You are an expert at identifying architectural decisions, safety
|
|
invariants, and policy requirements in code changes.
|
|
|
|
Given:
|
|
- A unified diff
|
|
- Existing authored claims (JSON)
|
|
- Observations extracted from changed files (JSON)
|
|
|
|
Identify:
|
|
1. Violations: Does the diff contradict any existing claim?
|
|
2. Suggestions: What new claims should be authored? (Only if a violation
|
|
would break something, a new team member would need to know, or there's
|
|
a non-obvious reason for the choice)
|
|
3. No-claim-needed: What patterns don't need claims and why?
|
|
|
|
For each suggestion, provide:
|
|
- id, concept_path, predicate, value, category
|
|
- invariant (what MUST be true)
|
|
- consequence (what breaks if violated)
|
|
- authority_tier (regulatory/clinical/observational/expert/community)
|
|
- reason (why this needs a claim)
|
|
- confidence (0.0-1.0)
|
|
|
|
Respond in JSON matching this schema: { ... }
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation
|
|
|
|
### Crate Structure
|
|
|
|
New binary in `applications/aphoria-claims-api/`:
|
|
|
|
```
|
|
applications/aphoria-claims-api/
|
|
Cargo.toml # depends on aphoria (lib), axum, reqwest, serde_json
|
|
src/
|
|
main.rs # axum server setup, routes
|
|
routes/
|
|
claims.rs # CRUD endpoints
|
|
verify.rs # verification endpoint
|
|
coverage.rs # coverage metrics
|
|
review.rs # diff review (calls Gemini)
|
|
docs.rs # docs generation
|
|
explain.rs # onboarding narrative
|
|
gemini/
|
|
client.rs # Gemini API client (generateContent)
|
|
prompts.rs # Prompt templates for each reasoning task
|
|
types.rs # Request/response types for Gemini API
|
|
state.rs # AppState (project_root, config, gemini client)
|
|
error.rs # Error types -> axum responses
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
- `aphoria` (path dependency) — all the domain logic
|
|
- `axum` — HTTP framework (already used by stemedb-api)
|
|
- `reqwest` — Gemini API calls
|
|
- `serde_json` — JSON serialization
|
|
- `tokio` — async runtime
|
|
- `tower-http` — CORS middleware
|
|
- `tracing` — structured logging
|
|
|
|
### Key Design Decisions
|
|
|
|
**Library, not shell-out.** The API imports `aphoria` as a crate and calls `ClaimsFile::load()`, `verify_claims()`, `extract_claims()` directly. No `Command::new("aphoria")`.
|
|
|
|
**Stateless per request.** Each request reads `.aphoria/claims.toml` fresh. No in-memory cache of claims (the file is small, TOML parsing is fast). This means multiple clients can't corrupt each other's state.
|
|
|
|
**File locking for writes.** `POST /v1/claims` and `PATCH /v1/claims/:id` acquire a file lock on `claims.toml` before read-modify-write. Use `fs2::FileExt` or `fd-lock`.
|
|
|
|
**Gemini is optional.** The CRUD, verify, and coverage endpoints work without Gemini. Only `/v1/review`, `/v1/docs/generate`, and `/v1/explain` need LLM reasoning. If `GEMINI_API_KEY` is not set, these return 503 with a clear message.
|
|
|
|
---
|
|
|
|
## Port Assignment
|
|
|
|
Following the existing 181XX scheme:
|
|
|
|
| Offset | Service | Port |
|
|
|--------|---------|------|
|
|
| +9 | Claims API | 18189 |
|
|
|
|
Env var: `APHORIA_CLAIMS_API_BIND_ADDR` (default `127.0.0.1:18189`)
|
|
|
|
---
|
|
|
|
## Example Flows
|
|
|
|
### CI Pipeline: Block PR if Claims Violated
|
|
|
|
```bash
|
|
# In CI script
|
|
DIFF=$(git diff origin/main...HEAD)
|
|
|
|
RESULT=$(curl -s -X POST http://localhost:18189/v1/review \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"diff\": $(echo "$DIFF" | jq -Rs .)}")
|
|
|
|
VIOLATIONS=$(echo "$RESULT" | jq '.violations | length')
|
|
if [ "$VIOLATIONS" -gt 0 ]; then
|
|
echo "Claims violated:"
|
|
echo "$RESULT" | jq '.violations[]'
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### IDE Extension: Suggest Claims on Save
|
|
|
|
```typescript
|
|
// VS Code extension pseudocode
|
|
const diff = await getDiffSinceLastSave();
|
|
const response = await fetch('http://localhost:18189/v1/review', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ diff })
|
|
});
|
|
const { suggestions } = await response.json();
|
|
|
|
for (const s of suggestions) {
|
|
showInlineHint(s.observation.file, s.observation.line,
|
|
`Claim suggested: ${s.suggested_claim.invariant}`);
|
|
}
|
|
```
|
|
|
|
### ADK-Go Agent: Claims-Aware Code Generation
|
|
|
|
```go
|
|
// Before generating code, check constraints via claims
|
|
resp, _ := http.Post("http://localhost:18189/v1/verify",
|
|
"application/json",
|
|
bytes.NewReader([]byte(`{"show_unclaimed": false}`)))
|
|
|
|
var report VerifyReport
|
|
json.NewDecoder(resp.Body).Decode(&report)
|
|
|
|
if report.Summary.Conflict > 0 {
|
|
// Don't generate code that conflicts with claims
|
|
return fmt.Errorf("existing claims would be violated")
|
|
}
|
|
```
|
|
|
|
### New Developer Onboarding
|
|
|
|
```bash
|
|
curl -s http://localhost:18189/v1/explain | less
|
|
```
|
|
|
|
Gets the narrative: what this codebase claims about itself, why, and where to find evidence.
|
|
|
|
---
|
|
|
|
## Gemini API Integration Details
|
|
|
|
### Endpoint
|
|
|
|
```
|
|
POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$GEMINI_API_KEY
|
|
```
|
|
|
|
### Request Shape
|
|
|
|
```json
|
|
{
|
|
"contents": [{
|
|
"parts": [{
|
|
"text": "System prompt + structured input"
|
|
}]
|
|
}],
|
|
"generationConfig": {
|
|
"responseMimeType": "application/json",
|
|
"responseSchema": { ... }
|
|
}
|
|
}
|
|
```
|
|
|
|
Using `responseMimeType: "application/json"` with a schema forces Gemini to return structured output matching our types. No parsing needed.
|
|
|
|
### Cost Estimate
|
|
|
|
Diff review for a typical PR (~500 lines):
|
|
- Input: ~2K tokens (prompt) + ~1K (diff) + ~1K (existing claims JSON) = ~4K tokens
|
|
- Output: ~500 tokens (suggestions JSON)
|
|
- At Gemini 2.5 Flash pricing: ~$0.001 per review
|
|
|
|
Negligible. Run it on every PR.
|
|
|
|
---
|
|
|
|
## What This Enables
|
|
|
|
1. **Any LLM can author claims.** Not just Claude Code. Gemini, GPT, local models — they call the API.
|
|
2. **CI enforcement.** Block PRs that violate claims. No human needs to remember.
|
|
3. **IDE integration.** Inline suggestions as you type, not just at review time.
|
|
4. **ADK-Go agents.** Agents that generate code can check claims before writing, and author claims after.
|
|
5. **Custom dashboards.** Coverage metrics as a web service. Build whatever UI you want.
|
|
6. **The flywheel without the skill.** The `aphoria-claims` skill is great for Claude Code users. This API is for everyone else.
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
1. **Claims CRUD endpoints** — Wrap `ClaimsFile` in axum routes. No LLM needed. Test with curl.
|
|
2. **Verify endpoint** — Call `verify_claims()`, return JSON. No LLM needed.
|
|
3. **Coverage endpoint** — Compute from verify report. No LLM needed.
|
|
4. **Gemini client** — `reqwest` + structured output schema.
|
|
5. **Review endpoint** — The reasoning endpoint. Diff + claims + observations -> Gemini -> suggestions.
|
|
6. **Docs + Explain endpoints** — Narrative generation via Gemini.
|
|
|
|
Steps 1-3 are pure engineering (wrap existing Rust functions in HTTP). Steps 4-6 add the Gemini reasoning layer.
|