fix(consistency): fix C-04 status vocabulary mismatch + surface PATCH errors

Root cause: workplan files use "done" (task vocabulary) but the DB workstream
API only accepts "completed". The PATCH was silently failing with 422.

Fixes:
- Add FILE_TO_DB_WORKSTREAM_STATUS map and normalise_workstream_status()
- Normalise file status before C-04 comparison: done↔completed is no longer
  spurious drift
- Normalise file status before PATCHing: always send DB-valid "completed"
- _api_patch now returns {"_error": ...} instead of None on failure, so the
  fix loop reports FAILED entries rather than silently dropping them
- 9 new tests in TestNormaliseWorkstreamStatus (42 total)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 21:57:11 +01:00
parent c8f08b803d
commit 7b7b725f8b
2 changed files with 76 additions and 6 deletions

View File

@@ -23,7 +23,9 @@ sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
from consistency_check import (
ConsistencyReport,
Issue,
FILE_TO_DB_WORKSTREAM_STATUS,
get_tasks_from_workplan,
normalise_workstream_status,
parse_frontmatter,
parse_task_blocks,
render_text,
@@ -326,3 +328,46 @@ class TestReportToDict:
d = report_to_dict(r)
assert d["repo_slug"] == "the-custodian"
assert d["repo_path"] == "/home/worsch/the-custodian"
# ---------------------------------------------------------------------------
# Status vocabulary normalisation
# ---------------------------------------------------------------------------
class TestNormaliseWorkstreamStatus:
"""FILE_TO_DB_WORKSTREAM_STATUS maps workplan file vocabulary to DB vocabulary.
Workplan files use task-style "done"; the DB workstream API uses "completed".
The C-04 check and fix code must normalise before comparing or PATCHing.
"""
def test_done_maps_to_completed(self):
assert normalise_workstream_status("done") == "completed"
def test_completed_is_identity(self):
assert normalise_workstream_status("completed") == "completed"
def test_active_is_identity(self):
assert normalise_workstream_status("active") == "active"
def test_blocked_is_identity(self):
assert normalise_workstream_status("blocked") == "blocked"
def test_archived_is_identity(self):
assert normalise_workstream_status("archived") == "archived"
def test_unknown_value_returned_as_is(self):
# Don't crash on unexpected values — return them unchanged
assert normalise_workstream_status("foobar") == "foobar"
def test_map_constant_covers_done(self):
assert "done" in FILE_TO_DB_WORKSTREAM_STATUS
assert FILE_TO_DB_WORKSTREAM_STATUS["done"] == "completed"
def test_c04_no_spurious_drift_when_done_vs_completed(self):
"""done (file) vs completed (DB) must NOT be reported as C-04 drift."""
assert normalise_workstream_status("done") == normalise_workstream_status("completed")
def test_c04_real_drift_still_detected(self):
"""done (file) vs active (DB) IS real drift and must be detected."""
assert normalise_workstream_status("done") != normalise_workstream_status("active")