Repository navigation
fix(api): align ConflictOut OpenAPI response with runtime schema fields - #1536
Conversation
…ds (Closes caura-ai#1440) The D11 conflict OpenAPI response model (ConflictOut) was missing 7 runtime fields present in the actual conflict payload: fleet_id, relationship_confidence, diagnosis_confidence, evidence_strength, audit_reason, created_by, created_at. Clients generated from the spec could not read these fields back. - Add the 7 fields to core-api/src/core_api/openapi_responses.py:ConflictOut matching the runtime model in core_api/schemas.py. - Add tests/test_d11_conflict_openapi_parity.py to catch future field drift. Signed-off-by: wylovelyi <wylovelyi@users.noreply.github.com>
93c9882 to
41842e5
Compare
|
Hi maintainers 👋 Brought this onto the latest What this PR does
Added Closes #1440. 2 files, +50 / -0. CI status — all green:
(The The branch is a merge of |
erni-a
left a comment
There was a problem hiding this comment.
Thanks @wylovelyi, and sorry this sat ten days. Verified against main: the seven fields match the runtime ConflictOut in schemas.py (float for the two confidences, str for the rest, created_at as an ISO string like resolved_at already is in this spec-only model), and the parity test lives in the root tests/ suite that CI runs. Merging; it will be behind main by the time its turn comes, so I will update the branch myself.
🤖 I have created a release *beep* *boop* --- <details><summary>backend: 3.20.0</summary> ## [3.20.0](backend-v3.19.2...backend-v3.20.0) (2026-09-26) ### Features * **llm:** add Atlas Cloud provider ([#1510](#1510)) ([0ee34f4](0ee34f4)) ### Bug Fixes * **api:** align ConflictOut OpenAPI response with runtime schema fields ([#1536](#1536)) ([1f929dc](1f929dc)) * **client-python:** ship the Apache-2.0 LICENSE in the published package ([#1031](#1031)) ([820d60b](820d60b)) * **client-ts:** ship the Apache-2.0 LICENSE in the npm package ([#1032](#1032)) ([26231d4](26231d4)) * **contradiction:** give the forward chain-edge writes the CAS their comments claimed ([#1727](#1727)) ([4599871](4599871)) * **plugin:** stop re-requesting agent keys after the provision route 404s ([#1718](#1718)) ([b5fe391](b5fe391)) * **worker:** stop warning that the async path does not fan out — it has since A70 (oss-0924-m-03) ([#1724](#1724)) ([cb84511](cb84511)) </details> <details><summary>plugin: 2.23.3</summary> ## [2.23.3](plugin-v2.23.2...plugin-v2.23.3) (2026-09-26) ### Bug Fixes * **plugin:** stop re-requesting agent keys after the provision route 404s ([#1718](#1718)) ([b5fe391](b5fe391)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Signed-off-by: release-please[bot] <release-please[bot]@users.noreply.github.com> Signed-off-by: Eldad Caura <eldad@caura.ai> Co-authored-by: caura-deploy-bot[bot] <265395343+caura-deploy-bot[bot]@users.noreply.github.com> Co-authored-by: Eldad Caura <eldad@caura.ai> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1813) ## Audit finding **M-101**, from batch B20 (contradiction verdict correctness). The OpenAPI description of `MemoryContradictionsResponse.superseded_by` points the wrong way. - **The description:** "The newer memory that superseded this one; null when none is live." - **What the route returns:** `GET /memories/{id}/contradictions` builds the field from this memory's `supersedes_id`, the older row this memory replaced. The route's comment says so: "Despite the field name, memory.supersedes_id points at the OLDER memory". MCP `caura_manage op=lineage` returns the key the same way. - **Where the newer rows are:** in `superseded_memories`, and in `contradictions[direction=superseded_by]`. - **Why it matters:** `docs/public-api-stability.md` makes OpenAPI response shapes part of the contract. A client built from the published schema walks the chain backwards: when `superseded_by` is set it marks this memory, the live winner, as stale, and treats the retired predecessor as its correction. This is the same class of bug as caura PR #1536, on a different field. ## Change - **`superseded_by`:** "The older memory this one superseded (via supersedes_id); null when there is none or it was deleted. The field name is kept for back-compat; newer memories that superseded this one are in superseded_memories and in contradictions with direction superseded_by." - **`superseded_memories`:** gains its own description: "Newer memories that superseded this one, each with supersedes_id pointing here." - No behaviour change. The served MCP `caura_manage` description is unchanged. Rewording it would mean regenerating `tests/fixtures/tools_list_baseline_v1.json` and `plugin/tools.json`, and spending part of the token budget, which has 22 tokens of headroom. The MCP handler docstring already states the meaning. ## Verification - `tests/test_contradictions_superseded_by_contract.py` reads both descriptions from `app.openapi()`. `tests/test_api_contradictions.py` already pins that the route returns the older row under `superseded_by`. - Tests first: the tests-only push `8755ffbe` ([run 37179852102](https://github.com/caura-ai/caura/actions/runs/37179852102)) failed both new tests against `main`, with 8678 passing. The schema served the old "newer memory" text, and no description for `superseded_memories`. The fix is now amended into the same single commit. - No repository scripts, dependency installs, tests or builds were run on Eldad's Mac under its safety hold. CI runs the suites. One signed-off commit. Do not merge or enable auto-merge; Eldad merges after review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: eldad-caura <eldad@caura.ai> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Closes #1440.
The conflict-review routes (
GET /api/v1/conflicts,GET /api/v1/conflicts/{conflict_id},PATCH /api/v1/conflicts/{conflict_id}/resolve) serialize the runtimecore_api.schemas.ConflictOut, but theirresponses={200: {"model": ...}}decorators point at the smaller spec-onlycore_api.openapi_responses.ConflictOut, which omitted 7 fields the handler actually emits.This is a spec-only change — the OpenAPI response model is documentational (it is not used as
response_model=, so no wire/runtime serialization changes). It now declares the same fields the runtime model does:fleet_idrelationship_confidencediagnosis_confidenceevidence_strengthaudit_reasoncreated_bycreated_atTypes follow the existing spec-only model's conventions (
strfor ids/timestamps,float | Nonefor the confidence scores), matching what_MEMORY_CONFLICT_FIELDS/core_api.schemas.ConflictOutcarry.Regression test
Added
tests/test_d11_conflict_openapi_parity.py, which asserts the spec-onlyConflictOutdocuments every field the runtimeConflictOutserializes. This ratchets against future drift between the two models — the exact cause of #1440.Notes