fix(registrar): register records that have an identifier but are absent from the hub
Two blockers kept 268 work records unrecoverable. 1. The registrar only looked for *missing* identifiers, so a record whose derived identifier was already in the file but absent from central was invisible to it and the run short-circuited to noop. C-06 handles exactly that case and marks it fixable; only the early return stood in the way. Records the hub holds under a *different* identifier are deliberately not touched — that is a duplicate-registration identity decision. 2. repo-onboard now corrects a stale remote_url from the working copy's origin. The forge migration moved every repository from Gitea to Forgejo but never updated the hub, leaving 50 records pointing at a retired forge. State Hub matches a checkout to its record by remote_url, so it could not find those repositories and refused to register any of their work records — the error surfaced only in a child process's stderr. Verified on kaizen-agentic: 8 records on central before, 15 after. Refs CUST-WP-0068-T06 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
parent
bdb981be2b
commit
ebf114abd6
2 changed files with 60 additions and 1 deletions
|
|
@ -102,6 +102,43 @@ def _missing_identifiers(repo: Path) -> dict[str, list[str]]:
|
|||
}
|
||||
|
||||
|
||||
def _absent_from_projection(repo: Path, api_base: str) -> list[str]:
|
||||
"""Workplans whose identifier is in the file but not on the authoritative hub.
|
||||
|
||||
These are registration work, but `_missing_identifiers` cannot see them: the
|
||||
file *has* an identifier, so nothing is missing by that measure and the run
|
||||
short-circuits to `noop`. C-06 handles exactly this case — an authoritative
|
||||
identifier absent from a projection — and marks it fixable, so the only
|
||||
thing standing between these records and registration was the early return
|
||||
(CUST-WP-0068-T06).
|
||||
|
||||
Only reports records the hub genuinely does not hold. A record the hub holds
|
||||
under a *different* identifier is a duplicate registration, which is an
|
||||
identity decision and deliberately not handled here.
|
||||
"""
|
||||
workplans_dir = repo / "workplans"
|
||||
if not workplans_dir.is_dir():
|
||||
return []
|
||||
absent: list[str] = []
|
||||
for path in sorted(workplans_dir.glob("*.md")):
|
||||
parsed = parse_workplan_file(path, repo_root=repo)
|
||||
if parsed.frontmatter.get("type") != "workplan" or not parsed.id:
|
||||
continue
|
||||
if not parsed.state_hub_workstream_id:
|
||||
continue
|
||||
try:
|
||||
response = httpx.get(
|
||||
f"{api_base.rstrip('/')}/workplans/{parsed.state_hub_workstream_id}",
|
||||
timeout=10.0,
|
||||
)
|
||||
except httpx.HTTPError:
|
||||
# Never invent registration work from a transport failure.
|
||||
return []
|
||||
if response.status_code == 404:
|
||||
absent.append(parsed.id)
|
||||
return absent
|
||||
|
||||
|
||||
def _requested_identity_findings(
|
||||
identity: dict[str, Any],
|
||||
requested: dict[str, list[str]],
|
||||
|
|
@ -577,7 +614,16 @@ def registrar_reconcile(
|
|||
},
|
||||
cid,
|
||||
)
|
||||
if not any(before.values()) and not repair_projection_id and not bootstrap_empty_projection:
|
||||
absent = _absent_from_projection(repo, api_base)
|
||||
if absent:
|
||||
evidence["absent_from_projection"] = absent
|
||||
|
||||
if (
|
||||
not any(before.values())
|
||||
and not absent
|
||||
and not repair_projection_id
|
||||
and not bootstrap_empty_projection
|
||||
):
|
||||
evidence["missing_after"] = before
|
||||
# Bind on this path too. Nothing needs minting, but a previous run may
|
||||
# have minted and failed to bind — without this, such records stay
|
||||
|
|
|
|||
|
|
@ -181,6 +181,19 @@ def onboard_repo(
|
|||
else:
|
||||
result.action = "updated"
|
||||
|
||||
# Correct a stale remote before anything else depends on it. The forge
|
||||
# migration moved every repository from Gitea to Forgejo but never updated
|
||||
# the hub, leaving 50 records pointing at a retired forge — and
|
||||
# `fix-consistency` matches a checkout to its record *by remote_url*, so it
|
||||
# could not find them and refused to register any of their work records.
|
||||
# The working copy's origin is the derivable truth (CUST-WP-0068-T06).
|
||||
if remote and existing and existing.get("remote_url") != remote:
|
||||
code, _ = _hub(api_base, "PATCH", f"/repos/{slug}", {"remote_url": remote})
|
||||
result.step(
|
||||
"remote_url_corrected", code in (200, 201),
|
||||
was=existing.get("remote_url"), now=remote, status=code,
|
||||
)
|
||||
|
||||
code, patched = _hub(api_base, "PATCH", f"/repos/{slug}", _classification_body(data))
|
||||
if code in (200, 201):
|
||||
result.step("hub_classification", True, status=code)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue