Keep historical split views from writing their parent's row.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 43s

C-04 ran once per file, so the three custodian-WP-0003 views and their archived parent flipped the shared row's status on every run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 57383@bnt-lap001
Assistant-Session: ef71febe-2a37-4c34-a979-2508ca973c91
This commit is contained in:
tegwick 2026-10-06 11:06:29 +02:00
parent b81c084dc3
commit 2b5d8f90f8
3 changed files with 66 additions and 1 deletions

View file

@ -119,6 +119,7 @@ from api.classification import ( # noqa: E402
resolve_topic_domain_slug,
)
from api.services.lifecycle import should_activate_parent_for_active_tasks # noqa: E402
from api.services.historical_projection import BINDINGS as _HISTORICAL_BINDINGS # noqa: E402
from api.task_status import ( # noqa: E402
CANONICAL_TASK_STATUSES,
OPEN_TASK_STATUSES,
@ -246,6 +247,20 @@ FILE_TO_DB_WORKSTREAM_STATUS: dict[str, str] = dict(LEGACY_WORKSTREAM_STATUS_ALI
STATUS_ORDER: dict[str, int] = dict(TASK_STATUS_ORDER)
def historical_view_paths(repo_slug: str) -> set[str]:
"""Repo-relative paths of bound historical views that share a parent row.
A split view carries its parent's UUID but does not supply the parent's
fields; only the bound parent file does (see historical_projection). Letting
each view write the shared row made its status flip on every run.
"""
return {
entry["path"]
for entry in _HISTORICAL_BINDINGS
if entry["repo"] == repo_slug and entry["record_id"] != entry["parent_record_id"]
}
def normalise_workstream_status(status: str, *, has_started: bool | None = None) -> str:
"""Translate a workplan file status value to its DB-canonical equivalent."""
return _normalize_workstream_status(status, has_started=has_started)
@ -1770,6 +1785,10 @@ def check_repo(
fixable=False,
)
# A bound historical split view shares its parent's row but supplies
# none of its fields or edges; only the bound parent file does.
historical_view = workplan_display_path(repo_dir, wp_file) in historical_view_paths(repo_slug)
# C-04: status drift — normalise file value before comparing so that
# legacy file/API aliases are not treated as drift. Lifecycle repairs
# take precedence so a stale planning file cannot regress active work.
@ -1780,6 +1799,7 @@ def check_repo(
and file_status
and db_status
and normalised_file_status != normalised_db_status
and not historical_view
):
report.add(
severity="WARN", check_id="C-04",
@ -1995,7 +2015,7 @@ def check_repo(
if dep.get("to_task_id"):
existing_dep_keys.add((from_task_id, "task", dep["to_task_id"], rel))
for target_wp_id in _frontmatter_depends_on_workplans(meta):
for target_wp_id in ([] if historical_view else _frontmatter_depends_on_workplans(meta)):
target_ws_id = workplan_id_to_ws_id.get(target_wp_id)
if not target_ws_id:
report.add(