feat: add fast forge work-record reconciliation
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c
This commit is contained in:
parent
a65cef02cf
commit
34f5cb3fc3
22 changed files with 799 additions and 162 deletions
|
|
@ -1648,7 +1648,7 @@ class TestC06WorkstreamCreation:
|
|||
assert "state_hub_workstream_id" not in patched
|
||||
assert "state_hub_task_id" not in patched
|
||||
assert any("not the identifier registrar" in fix for fix in report.fixes_applied)
|
||||
assert any("rmgr registrar-reconcile" in fix for fix in report.fixes_applied)
|
||||
assert any("rmgr sync" in fix for fix in report.fixes_applied)
|
||||
assert not any("set STATEHUB_REGISTRAR=1" in fix for fix in report.fixes_applied)
|
||||
assert any(issue.check_id == "C-06" for issue in report.issues)
|
||||
|
||||
|
|
|
|||
148
tests/test_work_record_projection_api.py
Normal file
148
tests/test_work_record_projection_api.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from api.services import forge_projection as fp
|
||||
from tests.conftest import create_test_domain, create_test_repo
|
||||
|
||||
|
||||
def _derived(commit: str) -> fp.DerivedProjection:
|
||||
workplan_id = "TEST-WP-ADHOC-2026-08-30"
|
||||
task_id = f"{workplan_id}-T01"
|
||||
return fp.DerivedProjection(
|
||||
repo_slug="projection-test",
|
||||
commit=commit,
|
||||
workplans=[
|
||||
fp.DerivedWorkplan(
|
||||
record_id=workplan_id,
|
||||
uuid=fp.derived_record_uuid(workplan_id),
|
||||
title="Ad Hoc — 2026-08-30",
|
||||
status="active",
|
||||
relative_path="workplans/ADHOC-2026-08-30.md",
|
||||
archived=False,
|
||||
owner="codex",
|
||||
description="Small opportunistic fixes.",
|
||||
tasks=[
|
||||
fp.DerivedTask(
|
||||
record_id=task_id,
|
||||
uuid=fp.derived_record_uuid(task_id),
|
||||
title="Repair registration",
|
||||
status="progress",
|
||||
priority="high",
|
||||
description="Make registration deterministic.",
|
||||
needs_human=True,
|
||||
intervention_note="Review the rollout.",
|
||||
blocking_reason="Waiting for rollout approval.",
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
async def test_primary_reconciles_exact_commit_and_replays(client, monkeypatch) -> None:
|
||||
await create_test_domain(client)
|
||||
repo = await create_test_repo(client, slug="projection-test")
|
||||
commit = "a" * 40
|
||||
calls = 0
|
||||
|
||||
def derive(_slug: str):
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
return _derived(commit)
|
||||
|
||||
from api.config import settings
|
||||
from api.routers import work_record_projection as route
|
||||
|
||||
monkeypatch.setattr(settings, "state_hub_instance_role", "primary")
|
||||
monkeypatch.setattr(settings, "state_hub_instance_label", "railliance01")
|
||||
monkeypatch.setattr(route, "derive_from_forge", derive)
|
||||
headers = {"Idempotency-Key": f"projection-test:{commit}"}
|
||||
response = await client.post(
|
||||
"/repos/projection-test/work-record-projection/reconcile",
|
||||
json={"expected_commit": commit},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json()["outcome"]["status"] == "applied"
|
||||
|
||||
replay = await client.post(
|
||||
"/repos/projection-test/work-record-projection/reconcile",
|
||||
json={"expected_commit": commit},
|
||||
headers=headers,
|
||||
)
|
||||
assert replay.status_code == 200
|
||||
assert replay.headers["X-StateHub-Idempotency-Replay"] == "true"
|
||||
assert calls == 1
|
||||
|
||||
workplan_id = fp.derived_record_uuid("TEST-WP-ADHOC-2026-08-30")
|
||||
workplan = await client.get(f"/workplans/{workplan_id}")
|
||||
assert workplan.status_code == 200
|
||||
assert workplan.json()["repo_id"] == repo["id"]
|
||||
assert workplan.json()["owner"] == "codex"
|
||||
assert workplan.json()["description"] == "Small opportunistic fixes."
|
||||
|
||||
tasks = await client.get("/tasks/", params={"workplan_id": workplan_id})
|
||||
assert tasks.status_code == 200
|
||||
task = tasks.json()[0]
|
||||
assert task["record_id"] == "TEST-WP-ADHOC-2026-08-30-T01"
|
||||
assert task["status"] == "progress"
|
||||
assert task["priority"] == "high"
|
||||
assert task["needs_human"] is True
|
||||
assert task["intervention_note"] == "Review the rollout."
|
||||
|
||||
snapshot = await client.get(
|
||||
"/repos/projection-test/work-record-projection/snapshot"
|
||||
)
|
||||
assert snapshot.status_code == 200, snapshot.text
|
||||
payload = snapshot.json()
|
||||
assert payload["schema"] == "state-hub.repository-projection-snapshot.v1"
|
||||
assert payload["repo_id"] == repo["id"]
|
||||
assert [row["id"] for row in payload["workplans"]] == [str(workplan_id)]
|
||||
assert [row["record_id"] for row in payload["tasks"]] == [
|
||||
"TEST-WP-ADHOC-2026-08-30-T01"
|
||||
]
|
||||
assert payload["dependencies"] == []
|
||||
|
||||
|
||||
async def test_reconcile_rejects_non_primary_before_forge_access(
|
||||
client, monkeypatch
|
||||
) -> None:
|
||||
from api.config import settings
|
||||
from api.routers import work_record_projection as route
|
||||
|
||||
monkeypatch.setattr(settings, "state_hub_instance_role", "cache")
|
||||
monkeypatch.setattr(settings, "state_hub_instance_label", "workstation")
|
||||
|
||||
def forbidden(_slug: str):
|
||||
raise AssertionError("forge must not be read by a non-primary endpoint")
|
||||
|
||||
monkeypatch.setattr(route, "derive_from_forge", forbidden)
|
||||
response = await client.post(
|
||||
"/repos/projection-test/work-record-projection/reconcile",
|
||||
json={"expected_commit": "a" * 40},
|
||||
)
|
||||
assert response.status_code == 409
|
||||
assert response.json()["detail"]["instance_role"] == "cache"
|
||||
|
||||
|
||||
async def test_reconcile_rejects_commit_mismatch_without_db_changes(
|
||||
client, monkeypatch
|
||||
) -> None:
|
||||
await create_test_domain(client)
|
||||
await create_test_repo(client, slug="projection-test")
|
||||
from api.config import settings
|
||||
from api.routers import work_record_projection as route
|
||||
|
||||
monkeypatch.setattr(settings, "state_hub_instance_role", "primary")
|
||||
monkeypatch.setattr(settings, "state_hub_instance_label", "railliance01")
|
||||
monkeypatch.setattr(route, "derive_from_forge", lambda _slug: _derived("b" * 40))
|
||||
response = await client.post(
|
||||
"/repos/projection-test/work-record-projection/reconcile",
|
||||
json={"expected_commit": "a" * 40},
|
||||
)
|
||||
assert response.status_code == 409
|
||||
rows = await client.get(
|
||||
"/workplans/",
|
||||
params={"repo_id": (await client.get("/repos/projection-test")).json()["id"]},
|
||||
)
|
||||
assert rows.status_code == 200
|
||||
assert rows.json() == []
|
||||
Loading…
Add table
Add a link
Reference in a new issue