Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions core-api/src/core_api/openapi_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,15 @@ class MemoryContradictionsResponse(BaseModel):
memory_id: str
status: str | None
superseded_by: SupersessionPeer | None = Field(
description="The newer memory that superseded this one; null when none is live."
description=(
"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: list[SupersessionPeer] = Field(
description="Newer memories that superseded this one, each with supersedes_id pointing here."
)
superseded_memories: list[SupersessionPeer]
detection_status: str = Field(description="completed or pending.")
contradictions: list[ContradictionEntry]

Expand Down
28 changes: 28 additions & 0 deletions tests/test_contradictions_superseded_by_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""The published schema says which way ``superseded_by`` points (M-101).

``GET /memories/{id}/contradictions`` fills ``superseded_by`` from this memory's
``supersedes_id``: the OLDER row this memory replaced. The route's own comment
says so, and MCP ``caura_manage op=lineage`` returns the same key the same way.
The OpenAPI description said the opposite ("the newer memory that superseded
this one"), so a client built from the published schema walked the chain
backwards: it read the live winner as stale and the retired row as its
correction. ``tests/test_api_contradictions.py`` pins the behaviour; this pins
the contract text to it.
"""

from core_api.app import app


def _properties() -> dict:
schemas = app.openapi()["components"]["schemas"]
return schemas["MemoryContradictionsResponse"]["properties"]


def test_superseded_by_is_documented_as_the_older_memory():
description = _properties()["superseded_by"].get("description", "")
assert description.startswith("The older memory this one superseded"), description


def test_superseded_memories_is_documented_as_the_newer_ones():
description = _properties()["superseded_memories"].get("description", "")
assert description.startswith("Newer memories that superseded this"), description
Loading