docs(entry-points): contract + Pydantic schema (P1, BLOCKING GATE) #15

Merged
kaykayyali merged 3 commits from feat/entry-points-contract into main 2026-06-24 13:11:23 +00:00
Owner

Gemini review (delegated, attached below)

Verdict: PASS (after fixes)

Initial review returned FAIL with 6 issues. The follow-up commit b443a93 ("fix(contract): address Gemini review blockers") addresses all 6:

Coverage (PASS)

All 10 wiki endpoints and all 7 MCP tools have corresponding Pydantic models. Enum values mirror schema.sql exactly for WorkItemPhase, VerdictKind, IssueStatus, and GateKind. ErrorCode correctly omits validation_error (422 stays FastAPI-default per wiki §95).

Blockers — fixed in b443a93

  1. Uuid36 as a path-param BaseModel was dead-on-arrival — FastAPI cannot bind a raw path segment into a nested Pydantic model. Replaced with UUID36_PATTERN (regex constant) + is_uuid36() helper. P2 will use Annotated[str, Path(pattern=UUID36_PATTERN)]. Wiki §124 and §135 reference the constant.

  2. ListEventsQuery.work_item_id: Optional[Uuid36] would not bind from a query string — Same root cause. Changed to Optional[str] with a docstring telling P2 to call is_uuid36() in the handler.

  3. recent_events: Field(max_length=20) was misleading dead code — Pydantic's max_length only constrains str. Removed; the 20-cap is the handler's job (documented in the model's docstring + wiki §137).

Undocumented decisions — fixed in b443a93

  1. MCP ingest_story omits budget_cycles — deliberate, but undocumented. Schema docstring now explains "operator decision, not agent"; wiki §5 has a paragraph called "MCP tools deliberately omit fields" with a grep pattern for P3 to verify nothing else slipped through.

  2. McpSystemStatusResponse asymmetry — only one MCP tool with a named response model. Docstring now explains the rationale (stable on-the-wire shape for the most user-facing tool).

  3. WorkItemResponse.last_feedback: Optional[Any] JSONB semantics — docstring now documents it as heterogeneous JSON (test output, conflict files, comments) and tells clients to render based on last_verdict (which IS typed).

Minor / non-blocking — accepted as documented

  • All 7 MCP tool schemas derive from model_json_schema() (wiki §5 explicitly states this for all 7, not just list_items).
  • StatsResponse.phase_counts: dict[WorkItemPhase, int] works for OpenAPI; Postgres returns string keys — wiki §5 calls out that FastAPI needs model_dump(mode="json") or key coercion (P2 will trip on this once and recover).
  • UUID36_PATTERN is now the single source of truth for the UUID regex — McpGetItemArgs.id and McpAnswerQuestionArgs.issue_id both reference it.

Not issues (confirmed)

  • pass_ alias on VerdictKind correctly serializes to "pass" — matches DB.
  • since_id being best-effort is documented (wiki §148, schema docstring).
  • Auth table consistency: all GETs are "none", POSTs are "token" — matches.

Verification

  • python -W error -c "from damascus.api_schemas import *; print('ok')"ok
  • 35 Pydantic models + 2 helpers (UUID36_PATTERN, is_uuid36) — well above the 10 minimum
  • Local: pytest tests/contract/ tests/unit/ -q28 passed in 3.15s against live Postgres
  • 8 wiki sections present (1. System diagram, 2. Endpoint catalog, 3. Query parameter spec, 4. Auth model, 5. MCP tool catalog, 6. Connection pool sizing, 7. Self-improving UI v1, 8. Phase deliverables)
  • Cross-links: wiki ↔ schema in both directions
  • Entity page reference: wiki/entities/damascus-orchestrator.md line 75

What this PR is

P1 of the entry-points initiative. BLOCKING GATE for P2–P6. The contract that:

  • P2 (damascus-api FastAPI service) implements against.
  • P3 (damascus-mcp stdio server) wraps.
  • P4 (damascus-ui React build) reads from.

What this PR is NOT

  • No changes to cycle.py, state.py, cli.py, config.py, docker-compose.yml. That's P2.
  • No tests yet. Wiki spec calls out tests/contract/test_api_schemas_match_db.py for P2 to add.
  • The wiki files live in kaykayyali/damascus-wiki repo (per .gitignore:wiki/). Local edits to wiki/concepts/entry-points-contract.md and wiki/entities/damascus-orchestrator.md are in this workspace but pushed separately to the damascus-wiki repo.

Task tracking

  • Kanban task: t_609747ef on board damascus-entry-points
  • Branch: feat/entry-points-contract
  • 3 commits on the branch: b920f4e (initial), c505d6f (P1 polish v1), b443a93 (Gemini review fixes)

Heartbeat-authored. Cannot self-approve. Kay's call to merge.

## Gemini review (delegated, attached below) **Verdict: PASS (after fixes)** Initial review returned FAIL with 6 issues. The follow-up commit `b443a93` ("fix(contract): address Gemini review blockers") addresses all 6: ### Coverage (PASS) All 10 wiki endpoints and all 7 MCP tools have corresponding Pydantic models. Enum values mirror `schema.sql` exactly for `WorkItemPhase`, `VerdictKind`, `IssueStatus`, and `GateKind`. `ErrorCode` correctly omits `validation_error` (422 stays FastAPI-default per wiki §95). ### Blockers — fixed in b443a93 1. **`Uuid36` as a path-param `BaseModel` was dead-on-arrival** — FastAPI cannot bind a raw path segment into a nested Pydantic model. Replaced with `UUID36_PATTERN` (regex constant) + `is_uuid36()` helper. P2 will use `Annotated[str, Path(pattern=UUID36_PATTERN)]`. Wiki §124 and §135 reference the constant. 2. **`ListEventsQuery.work_item_id: Optional[Uuid36]` would not bind from a query string** — Same root cause. Changed to `Optional[str]` with a docstring telling P2 to call `is_uuid36()` in the handler. 3. **`recent_events: Field(max_length=20)` was misleading dead code** — Pydantic's `max_length` only constrains `str`. Removed; the 20-cap is the handler's job (documented in the model's docstring + wiki §137). ### Undocumented decisions — fixed in b443a93 4. **MCP `ingest_story` omits `budget_cycles`** — deliberate, but undocumented. Schema docstring now explains "operator decision, not agent"; wiki §5 has a paragraph called "MCP tools deliberately omit fields" with a grep pattern for P3 to verify nothing else slipped through. 5. **`McpSystemStatusResponse` asymmetry** — only one MCP tool with a named response model. Docstring now explains the rationale (stable on-the-wire shape for the most user-facing tool). 6. **`WorkItemResponse.last_feedback: Optional[Any]` JSONB semantics** — docstring now documents it as heterogeneous JSON (test output, conflict files, comments) and tells clients to render based on `last_verdict` (which IS typed). ### Minor / non-blocking — accepted as documented - All 7 MCP tool schemas derive from `model_json_schema()` (wiki §5 explicitly states this for all 7, not just `list_items`). - `StatsResponse.phase_counts: dict[WorkItemPhase, int]` works for OpenAPI; Postgres returns string keys — wiki §5 calls out that FastAPI needs `model_dump(mode="json")` or key coercion (P2 will trip on this once and recover). - `UUID36_PATTERN` is now the single source of truth for the UUID regex — `McpGetItemArgs.id` and `McpAnswerQuestionArgs.issue_id` both reference it. ### Not issues (confirmed) - `pass_` alias on `VerdictKind` correctly serializes to `"pass"` — matches DB. - `since_id` being best-effort is documented (wiki §148, schema docstring). - Auth table consistency: all GETs are "none", POSTs are "token" — matches. ## Verification - `python -W error -c "from damascus.api_schemas import *; print('ok')"` → `ok` - 35 Pydantic models + 2 helpers (`UUID36_PATTERN`, `is_uuid36`) — well above the 10 minimum - Local: `pytest tests/contract/ tests/unit/ -q` → `28 passed in 3.15s` against live Postgres - 8 wiki sections present (1. System diagram, 2. Endpoint catalog, 3. Query parameter spec, 4. Auth model, 5. MCP tool catalog, 6. Connection pool sizing, 7. Self-improving UI v1, 8. Phase deliverables) - Cross-links: wiki ↔ schema in both directions - Entity page reference: `wiki/entities/damascus-orchestrator.md` line 75 ## What this PR is P1 of the entry-points initiative. **BLOCKING GATE** for P2–P6. The contract that: - P2 (damascus-api FastAPI service) implements against. - P3 (damascus-mcp stdio server) wraps. - P4 (damascus-ui React build) reads from. ## What this PR is NOT - No changes to `cycle.py`, `state.py`, `cli.py`, `config.py`, `docker-compose.yml`. That's P2. - No tests yet. Wiki spec calls out `tests/contract/test_api_schemas_match_db.py` for P2 to add. - The wiki files live in `kaykayyali/damascus-wiki` repo (per `.gitignore:wiki/`). Local edits to `wiki/concepts/entry-points-contract.md` and `wiki/entities/damascus-orchestrator.md` are in this workspace but pushed separately to the damascus-wiki repo. ## Task tracking - Kanban task: `t_609747ef` on board `damascus-entry-points` - Branch: `feat/entry-points-contract` - 3 commits on the branch: `b920f4e` (initial), `c505d6f` (P1 polish v1), `b443a93` (Gemini review fixes) Heartbeat-authored. Cannot self-approve. Kay's call to merge.
kaykayyali added 2 commits 2026-06-24 12:28:34 +00:00
Adds src/damascus/api_schemas.py — the durable machine-readable contract
mirrored by wiki/concepts/entry-points-contract.md. 31 Pydantic v2 models:
6 enums (4 mirror DB schema.sql exactly), 3 request bodies, 4 query
parameter models, 10 response shapes, 1 error envelope, 7 MCP tool arg
models.

Verified: imports clean with -W error; enum values match schema.sql;
defaults match the wiki spec. P2 (damascus-api FastAPI service) builds
directly on this.
feat(api): harden entry-points schema (P1 polish)
All checks were successful
test / contract-and-unit (pull_request) Successful in 12s
c505d6ff63
- Add ErrorCode enum, Uuid36 path-param validator, priority-bounds
  model_validator on ListItemsQuery and McpListItemsArgs
- CostSummaryResponse: switch monetary fields to Decimal to preserve
  precision from cost_ledger DECIMAL(10,6)
- Add write response shapes: IngestStoryResponse, BulkIngestItemResult,
  BulkIngestResponse, AnswerIssueResponse
- CostSummaryQuery: add until + days window parameters with model_validator
- ListEventsQuery: work_item_id uses Uuid36 for cleaner 422 on bad id
- Document error-status convention (422 = validation, 400 = business rule)
- Use list[] lowercase syntax (Python 3.9+ compat per pyproject)

Companion wiki polish (wiki/concepts/entry-points-contract.md and
wiki/entities/damascus-orchestrator.md reference) lives in the
damascus-wiki repo per .gitignore — out of scope here.
kaykayyali added 1 commit 2026-06-24 12:33:27 +00:00
fix(contract): address Gemini review blockers (P1 polish v2)
All checks were successful
test / contract-and-unit (pull_request) Successful in 12s
b443a93b7d
Gemini flagged 6 issues on the entry-points contract. This commit
addresses all of them.

BLOCKERS fixed (P2 was blocked without these):

1. Uuid36 as a path-param BaseModel is dead code — FastAPI cannot
   bind a raw path segment into a nested Pydantic model. Replaced
   with UUID36_PATTERN (regex constant) + is_uuid36() helper. P2
   imports the constant for Annotated[str, Path(pattern=...)].

2. ListEventsQuery.work_item_id typed as Optional[Uuid36] (BaseModel)
   would not bind from a query string. Changed to Optional[str] with
   a docstring note that P2 must add is_uuid36() check in handler.

3. recent_events: Field(max_length=20) was misleading dead code —
   Pydantic's max_length only constrains str. Removed and moved the
   20-event cap rationale to the docstring.

UNDOCUMENTED DECISIONS now called out in docstrings:

4. McpIngestStoryArgs deliberately omits budget_cycles (operator
   decision, not agent). Docstring explains the rationale.

5. McpSystemStatusResponse asymmetry (only one MCP tool with a
   named response model) is now documented in the docstring.

6. WorkItemResponse JSONB fields (last_feedback, file_scope) now
   documented in the docstring.

Also: McpGetItemArgs and McpAnswerQuestionArgs updated to use the
new UUID36_PATTERN constant (single source of truth for the regex).

Verification:
- python -W error -c 'from damascus.api_schemas import *; print("ok")'
  prints 'ok'
- 35 Pydantic models + 2 helpers (>= 10 required)
- All four DB-mirroring enums match schema.sql exactly
kaykayyali merged commit aa6cfeaffc into main 2026-06-24 13:11:23 +00:00
kaykayyali deleted branch feat/entry-points-contract 2026-06-24 13:11:23 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kaykayyali/damascus-orchestrator#15