Advance dependency roots with guarded source implementations

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10391-6355-7b41-b0fa-3e14e874f091
This commit is contained in:
tegwick 2026-10-04 11:01:47 +02:00
parent caef8df3aa
commit 57f2751f7b
10 changed files with 996 additions and 14 deletions

View file

@ -154,6 +154,7 @@ class DerivedWorkplan:
flavor: str | None = None
flavor_promotion_reason: str | None = None
flavor_promoted_from: str | None = None
historical_preservation: bool = False
@dataclass
@ -205,6 +206,7 @@ class DerivedProjection:
"flavor_promoted_from": w.flavor_promoted_from,
"relative_path": w.relative_path,
"archived": w.archived,
"historical_preservation": w.historical_preservation,
"tasks": [
{
"record_id": t.record_id,
@ -444,7 +446,40 @@ def derive_from_checkout(repo_root: Path, repo_slug: str, commit: str) -> Derive
if not wp_dir.is_dir():
proj.records_source_present = False
return proj
# Exact immutable histories are preserved by existing UUID, never allocated.
from api.services.historical_projection import bound_sources
try:
historical = bound_sources(repo_root, repo_slug)
except (ValueError, OSError, UnicodeError) as exc:
raise ForgeDeriveError(str(exc)) from exc
consumed = {entry["path"] for entry, _text in historical}
groups = {}
for entry, text in historical:
meta, body = _split_frontmatter(text)
parent_id = entry["parent_record_id"]
# Split views carry the parent's existing UUID and repeat its task list.
# Only the reviewed archived parent supplies the projected row.
if entry["record_id"] != parent_id:
continue
if repo_slug == "activity-core":
tasks = [DerivedTask(record_id=item["id"], uuid=item["state_hub_task_id"],
title=item.get("title"), status=item.get("status"),
priority=item.get("priority")) for item in meta["tasks"]]
else:
tasks = _parse_tasks(body, parent_id)
bindings = [yaml.safe_load(m.group(1)) for m in _TASK_BLOCK_RE.finditer(body)]
for task, binding in zip(tasks, bindings, strict=True):
task.uuid = binding["state_hub_task_id"]
groups[parent_id] = DerivedWorkplan(
record_id=parent_id, uuid=entry["uuid"], title=meta.get("title"),
status=meta.get("status"), relative_path=entry["path"],
archived="/archived/" in entry["path"], tasks=tasks,
historical_preservation=True,
)
proj.workplans.extend(groups.values())
for path in sorted(wp_dir.rglob("*.md")):
if path.relative_to(repo_root).as_posix() in consumed:
continue
if path.name.startswith("."):
continue
try:
@ -646,7 +681,11 @@ def diff_against_hub(
want_wp = {w.record_id.strip().lower(): w for w in derived.workplans}
have_wp: dict[str, dict[str, Any]] = {}
want_paths = {_path_key(w.relative_path): k for k, w in want_wp.items()}
preserved_by_uuid = {w.uuid: key for key, w in want_wp.items() if w.historical_preservation}
for w in hub_workplans:
if str(w["id"]) in preserved_by_uuid:
have_wp[preserved_by_uuid[str(w["id"])]] = w
continue
slug = str(w.get("slug") or "")
key = slug.strip().lower()
if key not in want_wp:
@ -686,6 +725,11 @@ def diff_against_hub(
uid = str(cur["id"]) if cur else w.uuid
if not cur:
continue
if w.historical_preservation:
if cur.get("status") not in {"done", "finished", "archived", "completed"}:
d.differing.append({"kind": "workplan", "record_id": w.record_id, "uuid": uid,
"changed": {"historical_terminal_status": cur.get("status")}})
continue
changed = {}
if (cur.get("status") or None) != (w.status or None):
changed["status"] = {"hub": cur.get("status"), "forge": w.status}
@ -707,6 +751,18 @@ def diff_against_hub(
cur = have_wp.get(w.record_id.strip().lower())
if cur:
hub_rows = hub_tasks_by_workplan.get(str(cur["id"]), [])
if w.historical_preservation:
by_uuid = {str(task["id"]): task for task in hub_rows}
for task in w.tasks:
current = by_uuid.get(task.uuid)
if current is None:
d.missing.append({"kind": "task", "record_id": task.record_id,
"uuid": task.uuid, "workplan": w.record_id})
elif current.get("status") not in {"done", "cancel"}:
d.differing.append({"kind": "task", "record_id": task.record_id,
"uuid": task.uuid, "changed": {
"historical_terminal_status": current.get("status")}})
continue
# Match on the canonical record id where the hub has one. Rows created
# before STATE-WP-0083-T06 fall back to title, which is why those are
# reported rather than acted on: a renamed heading is indistinguishable
@ -1312,6 +1368,27 @@ async def reset_repository_projection(
for task_row in loaded:
tasks_by_wp.setdefault(task_row.workplan_id, []).append(task_row)
# Bound histories are receiving support for already existing records only.
# Verify every parent and task before any mutation; preserve labels, status,
# retirement stamps and all UUIDs. Missing rows cannot authorize recreation.
for key, workplan in want.items():
if not workplan.historical_preservation:
continue
row = matched.get(key)
local = {str(task.id): task for task in tasks_by_wp.get(row.id, [])} if row else {}
missing = [task.uuid for task in workplan.tasks if task.uuid not in local]
nonterminal = [task.uuid for task in workplan.tasks
if task.uuid in local and local[task.uuid].status not in {"done", "cancel"}]
if (row is None or str(row.id) != workplan.uuid or missing or nonterminal
or row.status not in {"done", "finished", "archived", "completed"}):
outcome.status = "refused"
outcome.refused.append({
"reason": "historical binding requires existing parent and task UUIDs",
"workplan": workplan.record_id, "uuid": workplan.uuid,
"missing_task_uuids": missing, "nonterminal_task_uuids": nonterminal,
})
return outcome
# Canonical task identity is scoped to the matched current workplan while
# reconciling. Duplicate rows attached to a retired/displaced workplan are
# historical evidence and are deliberately not loaded above. Two rows with
@ -1374,22 +1451,11 @@ async def reset_repository_projection(
creating_tasks[task.uuid] = (workplan, task)
if creating_tasks:
current_holder_filters = [Workplan.projection_retired_at.is_(None)]
if stale:
# The caller has already acknowledged these retirements (the
# unacknowledged path returned above). Their tasks are preserved as
# history, but must not prevent the replacement projection from
# claiming its deterministic identities in the same transaction.
current_holder_filters.append(
Workplan.id.not_in([workplan.id for workplan in stale])
)
task_holders = list(
(
await session.execute(
select(Task)
.join(Workplan, Workplan.id == Task.workplan_id)
.where(
*current_holder_filters,
Task.id.in_(
[uuid.UUID(task_id) for task_id in creating_tasks]
),
@ -1403,7 +1469,7 @@ async def reset_repository_projection(
workplan, task = creating_tasks[str(holder.id)]
outcome.refused.append(
{
"reason": "derived task identifier already belongs to another current workplan",
"reason": "derived task identifier already belongs to another workplan",
"kind": "task",
"record_id": task.record_id,
"uuid": task.uuid,
@ -1463,6 +1529,10 @@ async def reset_repository_projection(
outcome.created.append(w.record_id)
continue
if w.historical_preservation:
outcome.notes.append("Preserved bound historical row and task UUIDs: " + w.record_id)
continue
changed = False
# The title is derived like every other field, and not syncing it left
# `cust-wp-0010` reading "Domain and Repository Goals" while its file

View file

@ -0,0 +1,90 @@
"""Exact closed source bindings predating canonical Forge identity.
These are frozen historical source views, not aliases or UUID allocation rules.
An altered/missing source requires explicit owner review; generic source UUIDs
remain untrusted. Shared Activity views represent the one archived parent.
"""
from pathlib import Path
from hashlib import sha256
import json
BINDINGS = json.loads(r'''[
{
"repo": "activity-core",
"path": "workplans/archived/260707-custodian-WP-0003-event-bridge.md",
"sha256": "83ea8bcb151e003419b73c59dbd028f4a1ce3d1120a82e4a128a772503ff3b76",
"record_id": "custodian-WP-0003",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0001-temporal-backbone.md",
"sha256": "020e2eac973f2daa41c1687f8e867da9d521f945547e6aa82c87993b3ff6fc64",
"record_id": "custodian-WP-0001",
"uuid": "e22516cc-4fdf-4dad-a928-a9e04a9fa7c7",
"parent_record_id": "custodian-WP-0001"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0002-triggers-ops.md",
"sha256": "d8cbc61631c01b6f11a03a63f2fb6275fc853ff7f7459ce65973ed1a233daa01",
"record_id": "custodian-WP-0002",
"uuid": "3a4f47d9-8bc1-434e-acb4-bed5d4dacda0",
"parent_record_id": "custodian-WP-0002"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003a-event-bridge-model-rules-registry.md",
"sha256": "ffa6682e9ba3aaf559bd44203d561c90dea69bcf1b1e778209a7b209e6e5cdd3",
"record_id": "custodian-WP-0003a",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003b-event-bridge-parser-workflow-triggers.md",
"sha256": "4fc34e0f821e2df51e5c08c7ab34da35b65690381d01f0cfdf55d00c970b414c",
"record_id": "custodian-WP-0003b",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003c-event-bridge-context-integration.md",
"sha256": "b6cd62b26515e509670ca39736b6bcec9633beedd46c9bd8f7598c06bac7276b",
"record_id": "custodian-WP-0003c",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0004-railiance-ops.md",
"sha256": "20df02eea740e109c937130e7f8801a636fc0990ed8c6d2ced55659f8957adb1",
"record_id": "custodian-WP-0004",
"uuid": "759b1255-aa78-42b5-8fab-c5fcf168f5f4",
"parent_record_id": "custodian-WP-0004"
},
{
"repo": "the-custodian",
"path": "workplans/ADHOC-2026-08-25-retired.md",
"sha256": "344a1293dbb3a67a944eb34ca4a0337487d8f047226027a2107922346fbab2dd",
"record_id": "CUST-WP-ADHOC-2026-08-25-RETIRED",
"uuid": "1c53d3db-6633-5e2d-987f-10706d73be9e",
"parent_record_id": "CUST-WP-ADHOC-2026-08-25-RETIRED"
}
]''')
def bound_sources(repo_root: Path, repo_slug: str):
entries = [entry for entry in BINDINGS if entry["repo"] == repo_slug]
result = []
for entry in entries:
path = repo_root / entry["path"]
if path.is_symlink() or not path.is_file():
raise ValueError("Historical source binding missing or symlinked: " + entry["path"])
data = path.read_bytes()
if sha256(data).hexdigest() != entry["sha256"]:
raise ValueError("Historical source binding changed: " + entry["path"])
result.append((entry, data.decode("utf-8")))
return result