feat(consistency): rebuild deterministic projection IDs
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
cb1b028fd1
commit
697dd49390
6 changed files with 245 additions and 32 deletions
|
|
@ -79,6 +79,7 @@ import socket
|
|||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, field
|
||||
from collections import Counter
|
||||
|
|
@ -118,6 +119,12 @@ from api.task_status import ( # noqa: E402
|
|||
normalize_task_status,
|
||||
)
|
||||
|
||||
_WORK_RECORD_NAMESPACE_UUID = uuid.UUID("a4058507-5c4a-5a00-ab06-fffa4fb46009")
|
||||
|
||||
|
||||
def _derived_work_record_uuid(record_id: str) -> str:
|
||||
return str(uuid.uuid5(_WORK_RECORD_NAMESPACE_UUID, f"helixforge\n{record_id}"))
|
||||
|
||||
try:
|
||||
import yaml as _yaml
|
||||
_HAS_YAML = True
|
||||
|
|
@ -1320,14 +1327,40 @@ def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = N
|
|||
|
||||
ws = _api_get(api_base, f"/workplans/{ws_id}")
|
||||
if ws is None:
|
||||
# C-03: stale workstream reference
|
||||
report.add(
|
||||
severity="FAIL", check_id="C-03",
|
||||
message=f"state_hub_workstream_id {ws_id[:8]}… not found in DB (stale reference)",
|
||||
file_path=fname,
|
||||
db_id=ws_id,
|
||||
fixable=False,
|
||||
)
|
||||
wp_id = str(meta.get("id", "")).strip()
|
||||
if wp_id and ws_id == _derived_work_record_uuid(wp_id):
|
||||
# A deterministic file identifier missing from a replaceable
|
||||
# projection is registration work, not a stale reference.
|
||||
report.add(
|
||||
severity="WARN",
|
||||
check_id="C-06",
|
||||
message=(
|
||||
f"Derived workplan {ws_id[:8]}… is absent from this projection"
|
||||
),
|
||||
file_path=fname,
|
||||
db_id=ws_id,
|
||||
fixable=True,
|
||||
_fix_context={
|
||||
"wp_file": str(wp_file),
|
||||
"meta": meta,
|
||||
"body": body,
|
||||
"repo_id": repo_id,
|
||||
"domain": file_domain,
|
||||
"repo_market_domain": repo_market_domain,
|
||||
"repo_slug": repo_slug,
|
||||
"desired_ws_id": ws_id,
|
||||
},
|
||||
)
|
||||
else:
|
||||
# A non-derived missing identifier may be genuine stale state;
|
||||
# preserve the conservative manual-review behavior.
|
||||
report.add(
|
||||
severity="FAIL", check_id="C-03",
|
||||
message=f"state_hub_workstream_id {ws_id[:8]}… not found in DB (stale reference)",
|
||||
file_path=fname,
|
||||
db_id=ws_id,
|
||||
fixable=False,
|
||||
)
|
||||
continue
|
||||
|
||||
# C-09: repo mismatch — file is here but DB says different repo
|
||||
|
|
@ -1609,13 +1642,32 @@ def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = N
|
|||
file_task_sh_ids.add(t_sh_id)
|
||||
db_task = _api_get(api_base, f"/tasks/{t_sh_id}")
|
||||
if db_task is None:
|
||||
report.add(
|
||||
severity="FAIL", check_id="C-03",
|
||||
message=f"state_hub_task_id {t_sh_id[:8]}… not found in DB",
|
||||
file_path=f"{fname}#{t_id}",
|
||||
db_id=t_sh_id,
|
||||
fixable=False,
|
||||
)
|
||||
if t_id and t_sh_id == _derived_work_record_uuid(t_id):
|
||||
report.add(
|
||||
severity="WARN",
|
||||
check_id="C-11",
|
||||
message=f"Derived task '{t_id}' is absent from this projection",
|
||||
file_path=f"{fname}#{t_id}",
|
||||
db_id=t_sh_id,
|
||||
fixable=True,
|
||||
_fix_context={
|
||||
"ws_id": ws_id,
|
||||
"ws_status": ws.get("status", ""),
|
||||
"task": task,
|
||||
"wp_file": str(wp_file),
|
||||
"meta": meta,
|
||||
"body": body,
|
||||
"desired_task_id": t_sh_id,
|
||||
},
|
||||
)
|
||||
else:
|
||||
report.add(
|
||||
severity="FAIL", check_id="C-03",
|
||||
message=f"state_hub_task_id {t_sh_id[:8]}… not found in DB",
|
||||
file_path=f"{fname}#{t_id}",
|
||||
db_id=t_sh_id,
|
||||
fixable=False,
|
||||
)
|
||||
continue
|
||||
# C-10 / C-15: task status drift. ADR-001: the file originates
|
||||
# work. Same-rank wait/progress is not "DB ahead" — writing
|
||||
|
|
@ -2850,6 +2902,9 @@ def fix_repo(
|
|||
repo_id_val = ctx["repo_id"]
|
||||
body = ctx.get("body", "")
|
||||
wp_id = str(meta.get("id", "")).strip()
|
||||
desired_ws_id = str(
|
||||
ctx.get("desired_ws_id") or _derived_work_record_uuid(wp_id)
|
||||
)
|
||||
title = str(meta.get("title", "")).strip()
|
||||
status = str(meta.get("status", "active")).strip()
|
||||
status = normalise_workstream_status(status)
|
||||
|
|
@ -2900,16 +2955,23 @@ def fix_repo(
|
|||
(w for w in existing if w.get("repo_id") == repo_id_val),
|
||||
None,
|
||||
)
|
||||
if existing_same_repo and existing_same_repo.get("title") == (title or wp_id):
|
||||
if (
|
||||
existing_same_repo
|
||||
and existing_same_repo.get("title") == (title or wp_id)
|
||||
and existing_same_repo.get("id") == desired_ws_id
|
||||
):
|
||||
ws_data = existing_same_repo
|
||||
report.fixes_applied.append(
|
||||
f"C-06 reusing existing workstream {ws_data['id'][:8]}... for {wp_id}"
|
||||
)
|
||||
break
|
||||
last_error = f"slug {slug!r} already belongs to another workstream"
|
||||
last_error = (
|
||||
f"slug {slug!r} already belongs to a different workplan identity"
|
||||
)
|
||||
continue
|
||||
|
||||
ws_data = _api_post(api_base, "/workplans", {
|
||||
"id": desired_ws_id,
|
||||
"topic_id": topic_id,
|
||||
"repo_id": repo_id_val,
|
||||
"slug": slug,
|
||||
|
|
@ -2932,7 +2994,13 @@ def fix_repo(
|
|||
continue
|
||||
|
||||
new_ws_id = ws_data["id"]
|
||||
_add_frontmatter_field(wp_file, "state_hub_workstream_id", new_ws_id)
|
||||
if new_ws_id != desired_ws_id:
|
||||
report.fixes_applied.append(
|
||||
f"C-06 FAIL {wp_id}: projection returned unexpected UUID {new_ws_id}"
|
||||
)
|
||||
continue
|
||||
if not meta.get("state_hub_workstream_id"):
|
||||
_add_frontmatter_field(wp_file, "state_hub_workstream_id", new_ws_id)
|
||||
report.fixes_applied.append(
|
||||
f"C-06 fixed: created workstream {new_ws_id[:8]}… "
|
||||
f"for {wp_id}, wrote ID to {wp_file.name}"
|
||||
|
|
@ -2950,7 +3018,14 @@ def fix_repo(
|
|||
t_priority = str(task.get("priority", "medium")).strip()
|
||||
if t_priority not in VALID_TASK_PRIORITIES:
|
||||
t_priority = "medium"
|
||||
raw_task_id = task.get("state_hub_task_id")
|
||||
desired_task_id = (
|
||||
str(raw_task_id).strip().strip('"')
|
||||
if raw_task_id not in (None, "", "~", "null", "None", "none")
|
||||
else _derived_work_record_uuid(t_id)
|
||||
)
|
||||
t_data = _api_post(api_base, "/tasks", {
|
||||
"id": desired_task_id,
|
||||
"workplan_id": new_ws_id,
|
||||
"title": str(task.get("title", t_id)).strip() or t_id,
|
||||
"description": task.get("description") or None,
|
||||
|
|
@ -2960,11 +3035,17 @@ def fix_repo(
|
|||
})
|
||||
if t_data and "_error" not in t_data:
|
||||
t_db_id = t_data["id"]
|
||||
injected = _inject_task_id_into_block(
|
||||
wp_file, "state_hub_task_id", t_db_id, t_id
|
||||
)
|
||||
if not injected:
|
||||
_inject_task_id_frontmatter_list(wp_file, t_db_id, t_id)
|
||||
if t_db_id != desired_task_id:
|
||||
report.fixes_applied.append(
|
||||
f" ! task {t_id} returned unexpected UUID {t_db_id}"
|
||||
)
|
||||
continue
|
||||
if not raw_task_id:
|
||||
injected = _inject_task_id_into_block(
|
||||
wp_file, "state_hub_task_id", t_db_id, t_id
|
||||
)
|
||||
if not injected:
|
||||
_inject_task_id_frontmatter_list(wp_file, t_db_id, t_id)
|
||||
report.fixes_applied.append(f" + task {t_id} → {t_db_id[:8]}…")
|
||||
elif t_data:
|
||||
report.fixes_applied.append(
|
||||
|
|
@ -3115,7 +3196,11 @@ def fix_repo(
|
|||
t_priority = str(task.get("priority", "medium")).strip()
|
||||
if t_priority not in VALID_TASK_PRIORITIES:
|
||||
t_priority = "medium"
|
||||
desired_task_id = str(
|
||||
ctx.get("desired_task_id") or _derived_work_record_uuid(t_id)
|
||||
)
|
||||
t_data = _api_post(api_base, "/tasks", {
|
||||
"id": desired_task_id,
|
||||
"workplan_id": ws_id,
|
||||
"title": str(task.get("title", t_id)).strip() or t_id,
|
||||
"description": task.get("description") or None,
|
||||
|
|
@ -3125,11 +3210,17 @@ def fix_repo(
|
|||
})
|
||||
if t_data:
|
||||
t_db_id = t_data["id"]
|
||||
injected = _inject_task_id_into_block(
|
||||
wp_file, "state_hub_task_id", t_db_id, t_id
|
||||
)
|
||||
if not injected:
|
||||
_inject_task_id_frontmatter_list(wp_file, t_db_id, t_id)
|
||||
if t_db_id != desired_task_id:
|
||||
report.fixes_applied.append(
|
||||
f"C-11 FAIL: task '{t_id}' returned unexpected UUID {t_db_id}"
|
||||
)
|
||||
continue
|
||||
if not task.get("state_hub_task_id"):
|
||||
injected = _inject_task_id_into_block(
|
||||
wp_file, "state_hub_task_id", t_db_id, t_id
|
||||
)
|
||||
if not injected:
|
||||
_inject_task_id_frontmatter_list(wp_file, t_db_id, t_id)
|
||||
report.fixes_applied.append(
|
||||
f"C-11 fixed: task '{t_id}' → {t_db_id[:8]}…"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue