CUST-WP-0075: add repeatable dependency monitor and handoff evidence
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Python Tests / pytest (push) Successful in 24s

This commit is contained in:
codex 2026-10-03 23:10:54 +02:00
parent c4558d4d10
commit 84f2e1899c
6 changed files with 548 additions and 8 deletions

View file

@ -25,3 +25,39 @@ Run with `TRIAGE_SCRATCH=<dir>` holding `repos.json`, `wps.json`, `waits.json`,
- Top roots by transitive fan-in: `SAND-WP-0015-T04` (7 workplans),
`SECRETS-WP-0009-T03` (5), `GLAS-WP-0012` (4), `NK-WP-0040` (4),
`FEP-WP-0002-T03` (4, a `todo` — not blocked at all), `RAPPS-WP-0014-T03` (3).
## Repeatable monitor (CUST-WP-0075-T05)
From the Custodian checkout, with Python and PyYAML installed:
```bash
python3 scripts/monitor_dependency_triage.py --output /tmp/dependency-triage
```
The monitor makes GET requests only. It captures the repository registry,
scans available owner files, compares every open non-residual wait's status
and qualifiers with its hub projection, and records the three T06 handoffs.
`monitor.json` contains counts and actionable findings; `file-view.txt` contains
the graph analysis. Raw registry and file indexes stay in the scratch directory.
No schedule, worker injection, owner-file change, or decision resolution is enabled.
`unavailable_checkouts` and `parse_warnings` make coverage limits explicit.
The scanner now reads YAML lists and quoted text correctly; historic task
fences containing prose use a reported fallback. The detailed file view includes
residuals for observation; the monitor's actionable counts exclude them.
Terminal predecessors are candidates for review: independent human and owner
gates remain binding. Replies newer than a proposal are candidates, not evidence
of acceptance; reviewed owner workplan IDs or declines still belong in T06.
The refreshed snapshot is [monitor-2026-10-03.json](monitor-2026-10-03.json).
It supersedes the earlier headline counts for the visible checkouts. Four new
unrouted human gates, sixteen unqualified waits, and twelve qualifier differences
need follow-up through T05; the earlier T02/T03 completion records are historical.
Six registered checkouts were unavailable. The sixteen degraded parses are in
historic Vergabe workplans. These results do not establish fleet-wide completeness.
Focused regression checks:
```bash
python3 -m unittest discover -s tests -p test_dependency_triage.py
```

View file

@ -0,0 +1,225 @@
{
"open_non_residual_workplans": 100,
"workplan_statuses": {
"active": 1,
"backlog": 5,
"blocked": 94
},
"wait_tasks": 232,
"human_gates": 79,
"unrouted_human_gates": [
"FEP-WP-0009-T01",
"INFD-WP-0005-T05",
"INFD-WP-0005-T10",
"RPF-WP-0035-T08"
],
"unqualified_waits": [
"EMERGENT-WP-0002-T02",
"EMERGENT-WP-0002-T05",
"EMERGENT-WP-0002-T07",
"EMERGENT-WP-0002-T08",
"EMERGENT-WP-0002-T09",
"EMERGENT-WP-0002-T10",
"NK-WP-0011-T02",
"NK-WP-0011-T03",
"NK-WP-0011-T04",
"NK-WP-0011-T05",
"NK-WP-0011-T06",
"NK-WP-0011-T07",
"NK-WP-0011-T08",
"NK-WP-0011-T09",
"QONTO-WP-0006-T02",
"QONTO-WP-0006-T03"
],
"owner_return_waits": 56,
"terminal_dependency_waits": [
{
"task": "ACTIVITY-WP-0042-T05",
"depends_on": [
"ACTIVITY-WP-0042-T01"
],
"needs_human": false,
"blocked_on": null,
"wait_until": null
},
{
"task": "ACTIVITY-WP-0042-T08",
"depends_on": [
"RPF-WP-0049-T05"
],
"needs_human": false,
"blocked_on": null,
"wait_until": null
},
{
"task": "INFD-WP-0005-T05",
"depends_on": [
"INFD-WP-0005-T04"
],
"needs_human": true,
"blocked_on": "message-from:railiance-platform",
"wait_until": null
},
{
"task": "USER-WP-0037-T03",
"depends_on": [
"USER-WP-0037-T02"
],
"needs_human": false,
"blocked_on": "message-from:state-hub",
"wait_until": null
}
],
"qualifier_drift": [
{
"task": "ACTIVITY-WP-0042-T08",
"field": "blocking_reason",
"file": "Provider prerequisite verified at lane KV4; scoped consumer bundle/hostPath and env refresh remain unperformed. Exact primary projection also blocked by existing historical identifier compatibility in repo-manager.",
"hub": "Provider repair verified: lane KV4 coordinates-only CAS, encrypted upload/readback and independent decryption. Remaining reviewed consumer source/env refresh and acceptance unperformed; exact primary projection blocked by existing repo-manager historical identifier compatibility."
},
{
"task": "ADAPTIVE-WP-0010-T01",
"field": "blocked_on",
"file": "message-from:tenant-engine",
"hub": null
},
{
"task": "FI-WP-0005-T04",
"field": "blocked_on",
"file": "message-from:activity-core",
"hub": null
},
{
"task": "FI-WP-0005-T08",
"field": "blocked_on",
"file": "message-from:rein-aharness",
"hub": null
},
{
"task": "INFO-WP-0029-T05",
"field": "blocked_on",
"file": "message-from:audit-core",
"hub": null
},
{
"task": "KG-WP-0005-T03",
"field": "blocked_on",
"file": "message-from:qonto-assistant",
"hub": null
},
{
"task": "NK-WP-0027-T06",
"field": "blocked_on",
"file": "message-from:info-tech-canon",
"hub": "message-from:ops-warden"
},
{
"task": "NK-WP-0035-T04",
"field": "blocked_on",
"file": "message-from:approval-engine",
"hub": "message-from:info-tech-canon"
},
{
"task": "RAPP-QONTO-WP-0002-T01",
"field": "wait_until",
"file": "2026-10-31",
"hub": null
},
{
"task": "RAPP-TELEMETRY-WP-0001-T04",
"field": "blocked_on",
"file": "message-from:railiance-platform",
"hub": null
},
{
"task": "REINAH-WP-0003-T04",
"field": "blocked_on",
"file": "message-from:activity-core",
"hub": null
},
{
"task": "SAND-WP-0015-T04",
"field": "blocked_on",
"file": "message-from:railiance-platform",
"hub": null
}
],
"parse_warnings": [
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0001-projektgeruest.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0002-fachmodelle.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0003-basis-ui.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0005-lose-anforderungen.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0006-aufgaben-bieterfragen.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0007-dokumente.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0008-preise.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0009-abgabe-nachbetrachtung.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0010-partner-bibliothek.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0011-marktbegleiter.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0012-querschnitt.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0013-feedback-bugs.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0014-aufgaben-lose-scores.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0015-aufgaben-verknuepfungen-frist-issuefacade.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0016-issue-facade-integration.md",
"/home/worsch/vergabe-teilnahme/workplans/VT-WP-0017-whynot-design-tokens.md"
],
"captured_at": "2026-10-03T21:08:51.190098+00:00",
"registered_repos": 146,
"unavailable_checkouts": [
"access-engine",
"inter-hub",
"markitect-project",
"railiance-bootstrap",
"railiance-hosts",
"repo-manager"
],
"handoffs": [
{
"case_id": "case:cross-owner:CUST-WP-0075-T06:state-hub",
"scenario_type": "cross_owner_wait",
"beneficiary_workplan_id": "CUST-WP-0075",
"obligor_repo": "state-hub",
"obligor_workplan_id": null,
"state": "waiting",
"reason": "Owner acceptance or decline not yet recorded",
"proposal_message_id": "b4825539-1425-4028-a2e9-9243785a344b",
"proposal_read_at": null,
"reply_candidates": []
},
{
"case_id": "case:cross-owner:CUST-WP-0075-T06:railiance-platform",
"scenario_type": "cross_owner_wait",
"beneficiary_workplan_id": "CUST-WP-0075",
"obligor_repo": "railiance-platform",
"obligor_workplan_id": null,
"state": "waiting",
"reason": "Owner acceptance or decline not yet recorded",
"proposal_message_id": "f84637fa-2355-4971-a2dc-7f1fd01710a8",
"proposal_read_at": null,
"reply_candidates": [
{
"id": "84cb5557-cc89-43fa-b8b8-13508c5ea52b",
"subject": "Re: D1 readiness: two lanes need a reviewed command before they can be approved",
"thread_id": "208c18ec-79fe-4b21-8cc8-928ecccfd4c2"
}
]
},
{
"case_id": "case:cross-owner:CUST-WP-0075-T06:ops-warden",
"scenario_type": "cross_owner_wait",
"beneficiary_workplan_id": "CUST-WP-0075",
"obligor_repo": "ops-warden",
"obligor_workplan_id": null,
"state": "waiting",
"reason": "Owner acceptance or decline not yet recorded",
"proposal_message_id": "79549660-4fc8-44d5-989e-074f6c7b6f07",
"proposal_read_at": null,
"reply_candidates": [
{
"id": "94d07ae0-a2a4-47f8-8bf7-9957aaf2a84f",
"subject": "Re: BUG: warden secret-fetch policy check is sent to the SSH signing pin and always 403s for non-ops-warden owners",
"thread_id": "faf11061-b490-435a-b472-cb315a459760"
}
]
}
]
}

View file

@ -1,15 +1,31 @@
import json,re,os,glob,collections,datetime,sys
import yaml
import os; S=os.environ.get('TRIAGE_SCRATCH','.')
repos=json.load(open(f'{S}/repos.json')); repos=repos if isinstance(repos,list) else repos['items']
FM=re.compile(r'^---\n(.*?)\n---',re.S); TB=re.compile(r'```task\n(.*?)```',re.S)
parse_warnings=[]
def kv(block):
d={}
for line in block.splitlines():
m=re.match(r'^([A-Za-z_]+):\s*(.*?)\s*(#.*)?$',line)
if m and not line.startswith(' ') : d[m.group(1)]=m.group(2).strip().strip('"')
try:
d=yaml.safe_load(block)
except yaml.YAMLError:
# Historic records mix prose into task fences. Preserve their fields,
# but expose the degraded parse rather than silently certify the scan.
parse_warnings.append(f)
d={}
for match in re.finditer(r'^([A-Za-z_]+):\s*([^\n]*)$',block,re.M):
key,value=match.groups()
if not value.strip():
tail=block[match.end():]
items=re.match(r'\n((?:[ \t]+-[ \t]+[^\n]*\n?)+)',tail)
value='\n'+items.group(1) if items else ''
try: d[key]=yaml.safe_load(value)
except yaml.YAMLError: d[key]=value.strip().strip('"')
if not isinstance(d,dict): raise ValueError('work record must be a YAML mapping')
return d
def lst(v):
if not v: return []
if isinstance(v,list): return [str(x) for x in v]
v=str(v)
v=v.strip()
if v.startswith('['): return [x.strip().strip('"\'') for x in v[1:-1].split(',') if x.strip()]
return [v]
@ -30,8 +46,8 @@ for r in repos:
for tb in TB.findall(txt):
d=kv(tb); tid=d.get('id')
if not tid: continue
T[tid]={'wp':wid,'repo':r['slug'],'status':d.get('status'),'depends_on':lst(d.get('depends_on')),'needs_human':d.get('needs_human','').lower()=='true','blocked_on':d.get('blocked_on') or None,'wait_until':d.get('wait_until') or None,'decision_id':d.get('decision_id') or None,'reason':d.get('blocking_reason') or ''}
json.dump({'WP':WP,'T':T},open(f'{S}/files.json','w'))
T[tid]={'wp':wid,'repo':r['slug'],'status':d.get('status'),'depends_on':lst(d.get('depends_on')),'needs_human':str(d.get('needs_human','')).lower()=='true','blocked_on':d.get('blocked_on') or None,'wait_until':str(d['wait_until']) if d.get('wait_until') else None,'decision_id':d.get('decision_id') or None,'reason':d.get('blocking_reason') or '', 'hub_id':d.get('state_hub_task_id')}
json.dump({'WP':WP,'T':T,'parse_warnings':sorted(set(parse_warnings))},open(f'{S}/files.json','w'))
TW={'finished','archived'}; TT={'done','cancel'}
def term(x):
if x in T: return T[x]['status'] in TT
@ -100,7 +116,7 @@ for n in nodes:
if any(not term(b) for b in dep.get(n,[])): continue
f=fanin(n)
if f: rows.append((len(f),n,sorted(f)))
rows.sort(key=lambda r:-r[0])
rows.sort(key=lambda r:(-r[0],r[1]))
for n,k,f in rows[:45]:
t=T.get(k);
desc=(f"wait h={t['needs_human']} on={t['blocked_on']} dec={t['decision_id']} | {t['reason'][:70]}" if t and t['status']=='wait' else (f"{t['status']}" if t else f"WP {WP[k]['status']} {WP[k]['repo']}" if k in WP else 'UNKNOWN'))

View file

@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""Read-only CUST-WP-0075 snapshot. Requires PyYAML for the file scanner.
Writes evidence only to --output; never changes owner records or contacts owners.
"""
import argparse
from collections import Counter
from datetime import datetime, timezone
import json
import os
from pathlib import Path
import subprocess
import sys
from urllib.parse import urlencode
from urllib.request import urlopen
ROOT = Path(__file__).resolve().parents[1]
SCANNER = ROOT / "docs/evidence/2026-10-03-dependency-graph-triage/scan_workplan_files.py"
OPEN = {"proposed", "ready", "active", "blocked", "backlog"}
HANDOFFS = {
"state-hub": "b4825539-1425-4028-a2e9-9243785a344b",
"railiance-platform": "f84637fa-2355-4971-a2dc-7f1fd01710a8",
"ops-warden": "79549660-4fc8-44d5-989e-074f6c7b6f07",
}
def get_rows(base, route, **filters):
# Only /tasks has offset pagination. /messages uses a caller-selected
# limit; /repos returns the complete registry without paging parameters.
if route != "tasks":
query = urlencode(dict(filters, limit=5000)) if route == "messages" else urlencode(filters)
with urlopen(f"{base.rstrip('/')}/{route}/?{query}", timeout=30) as response:
rows = json.load(response)
if isinstance(rows, dict):
rows = rows["items"]
if route == "messages" and len(rows) >= 5000:
raise ValueError("message limit reached; narrow the owner filter before scanning")
return rows
rows = []
while True:
query = urlencode(dict(filters, limit=200, offset=len(rows)))
with urlopen(f"{base.rstrip('/')}/{route}/?{query}", timeout=30) as response:
page = json.load(response)
if isinstance(page, dict):
page = page["items"]
if not isinstance(page, list):
raise ValueError(f"{route}: expected a list")
rows.extend(page)
if len(page) < 200:
return rows
def audit(records, hub_tasks):
plans, tasks = records["WP"], records["T"]
def terminal(token):
if token in tasks:
return tasks[token]["status"] in {"done", "cancel"}
return records["WP"].get(token, {}).get("status") in {"finished", "archived"}
plans = {k: v for k, v in plans.items() if v["status"] in OPEN and v["flavor"] != "residual"}
waits = {k: v for k, v in tasks.items() if v["wp"] in plans and v["status"] == "wait"}
by_id = {t["id"]: t for t in hub_tasks}
drift = []
for tid, task in sorted(waits.items()):
hub = by_id.get(task["hub_id"])
if hub is None:
drift.append({"task": tid, "field": "projection", "file": task["hub_id"], "hub": None})
continue
fields = {"status": "status", "needs_human": "needs_human", "reason": "blocking_reason",
"decision_id": "decision_id", "blocked_on": "blocked_on", "wait_until": "wait_until"}
for file_key, hub_key in fields.items():
expected = task[file_key]
actual = hub.get(hub_key)
if file_key == "needs_human":
actual = bool(actual)
elif file_key != "status":
expected, actual = expected or None, actual or None
if file_key == "wait_until":
expected = str(expected)[:10] if expected else None
actual = str(actual)[:10] if actual else None
if expected != actual:
drift.append({"task": tid, "field": hub_key, "file": expected, "hub": actual})
return {
"open_non_residual_workplans": len(plans),
"workplan_statuses": dict(sorted(Counter(p["status"] for p in plans.values()).items())),
"wait_tasks": len(waits),
"human_gates": sum(t["needs_human"] for t in waits.values()),
"unrouted_human_gates": sorted(k for k, t in waits.items() if t["needs_human"] and not t["decision_id"]),
"unqualified_waits": sorted(k for k, t in waits.items() if not any(
t[field] for field in ("depends_on", "needs_human", "blocked_on", "wait_until"))),
"owner_return_waits": sum(str(t["blocked_on"] or "").startswith("message-from:") for t in waits.values()),
"terminal_dependency_waits": [
{"task": k, "depends_on": t["depends_on"], "needs_human": t["needs_human"],
"blocked_on": t["blocked_on"], "wait_until": t["wait_until"]}
for k, t in sorted(waits.items()) if t["depends_on"] and all(terminal(x) for x in t["depends_on"])],
"qualifier_drift": drift,
}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--api-base", default="http://127.0.0.1:8000")
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
repos = get_rows(args.api_base, "repos")
(args.output / "repos.json").write_text(json.dumps(repos))
result = subprocess.run([sys.executable, str(SCANNER)], capture_output=True, text=True,
env=dict(os.environ, TRIAGE_SCRATCH=str(args.output.resolve())), check=True)
(args.output / "file-view.txt").write_text(result.stdout)
records = json.loads((args.output / "files.json").read_text())
report = audit(records, get_rows(args.api_base, "tasks"))
report["parse_warnings"] = records.get("parse_warnings", [])
report["captured_at"] = datetime.now(timezone.utc).isoformat()
report["registered_repos"] = len(repos)
report["unavailable_checkouts"] = sorted(r["slug"] for r in repos
if not r.get("local_path") or not Path(r["local_path"]).is_dir())
report["handoffs"] = []
for owner, message_id in HANDOFFS.items():
incoming = get_rows(args.api_base, "messages", to_agent="the-custodian", from_agent=owner)
outgoing = get_rows(args.api_base, "messages", to_agent=owner, from_agent="the-custodian")
original = next(m for m in outgoing if m["id"] == message_id)
candidates = [m for m in incoming if m["from_agent"] == owner
and m["created_at"] > original["created_at"]]
report["handoffs"].append({
"case_id": f"case:cross-owner:CUST-WP-0075-T06:{owner}",
"scenario_type": "cross_owner_wait",
"beneficiary_workplan_id": "CUST-WP-0075",
"obligor_repo": owner,
"obligor_workplan_id": None,
"state": "waiting", "reason": "Owner acceptance or decline not yet recorded",
"proposal_message_id": message_id, "proposal_read_at": original.get("read_at"),
"reply_candidates": [{"id": m["id"], "subject": m["subject"], "thread_id": m.get("thread_id")}
for m in candidates],
})
(args.output / "monitor.json").write_text(json.dumps(report, indent=2) + "\n")
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()

View file

@ -0,0 +1,98 @@
import importlib.util
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
from unittest.mock import patch, MagicMock
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location("monitor", ROOT / "scripts/monitor_dependency_triage.py")
monitor = importlib.util.module_from_spec(spec)
spec.loader.exec_module(monitor)
class DependencyTriageTests(unittest.TestCase):
def test_yaml_task_lists_and_quoted_hash_are_preserved(self):
with tempfile.TemporaryDirectory() as directory:
scratch = Path(directory)
repo = scratch / "repo"
(repo / "workplans").mkdir(parents=True)
(scratch / "repos.json").write_text(json.dumps([{"slug": "example", "local_path": str(repo)}]))
(repo / "workplans/plan.md").write_text('''---
id: EX-WP-0001
type: workplan
status: blocked
flavor: implementation
---
```task
id: EX-WP-0001-T01
status: wait
depends_on:
- EX-WP-0002-T01
needs_human: true
blocking_reason: "Review packet #1"
wait_until: 2026-10-04
```
''')
subprocess.run([sys.executable, str(monitor.SCANNER)], check=True, capture_output=True,
env=dict(os.environ, TRIAGE_SCRATCH=directory))
task = json.loads((scratch / "files.json").read_text())["T"]["EX-WP-0001-T01"]
self.assertEqual(task["depends_on"], ["EX-WP-0002-T01"])
self.assertEqual(task["reason"], "Review packet #1")
self.assertTrue(task["needs_human"])
self.assertEqual(task["wait_until"], "2026-10-04")
def records(self):
task = {"wp": "plan", "status": "wait", "hub_id": "uuid", "needs_human": True,
"reason": "Review", "decision_id": "decision", "blocked_on": None,
"wait_until": None, "depends_on": []}
return {"WP": {"plan": {"status": "blocked", "flavor": "implementation"}}, "T": {"task": task}}
def test_missing_projection_is_visible(self):
result = monitor.audit(self.records(), [])
self.assertEqual(result["qualifier_drift"][0]["field"], "projection")
def test_decision_sync_gap_is_detected(self):
result = monitor.audit(self.records(), [{"id": "uuid", "status": "wait",
"needs_human": True, "blocking_reason": "Review"}])
self.assertEqual(result["qualifier_drift"], [{"task": "task", "field": "decision_id",
"file": "decision", "hub": None}])
self.assertEqual(result["unrouted_human_gates"], [])
def test_residuals_are_excluded_from_actionable_findings(self):
records = self.records()
records["WP"]["plan"]["flavor"] = "residual"
result = monitor.audit(records, [])
self.assertEqual(result["wait_tasks"], 0)
self.assertEqual(result["qualifier_drift"], [])
def test_terminal_predecessor_keeps_separate_human_gate(self):
records = self.records()
records["T"]["task"]["depends_on"] = ["predecessor"]
records["T"]["predecessor"] = {"wp": "plan", "status": "done"}
result = monitor.audit(records, [])
self.assertTrue(result["terminal_dependency_waits"][0]["needs_human"])
self.assertEqual(result["human_gates"], 1)
def test_tasks_follow_offset_pagination(self):
response = MagicMock()
with patch.object(monitor, "urlopen", return_value=response) as fetch, patch.object(
monitor.json, "load", side_effect=[[{"id": str(i)} for i in range(200)], [{"id": "last"}]]):
rows = monitor.get_rows("http://hub", "tasks")
self.assertEqual(len(rows), 201)
self.assertIn("offset=200", fetch.call_args_list[1].args[0])
def test_messages_do_not_use_unsupported_offset(self):
with patch.object(monitor, "urlopen", return_value=MagicMock()) as fetch, patch.object(
monitor.json, "load", return_value=[{"id": str(i)} for i in range(200)]):
rows = monitor.get_rows("http://hub", "messages", from_agent="owner")
self.assertEqual(len(rows), 200)
self.assertEqual(fetch.call_count, 1)
self.assertNotIn("offset", fetch.call_args.args[0])
if __name__ == "__main__":
unittest.main()

View file

@ -126,6 +126,16 @@ After every slice: commit, `statehub fix-consistency` for each touched
repo, one progress event on this workplan, and the headline numbers
appended below so progress is visible from the hub.
2026-10-03 Codex continuation: repeatable read-only monitor implemented in
`scripts/monitor_dependency_triage.py`, with seven regression checks covering
YAML lists/quoting, pagination, missing projections, decision drift, residual
exclusion and independent human gates. Run it with `--output <scratch-dir>`;
review `monitor.json` before acting. Current live findings are retained in
`docs/evidence/2026-10-03-dependency-graph-triage/monitor-2026-10-03.json`:
four unrouted human gates, sixteen unqualified waits and twelve qualifier
differences. This task remains in progress until those findings are reconciled;
the earlier T02/T03 results are point-in-time, not a current clean scan.
## Hand the infrastructure proposals to their owners
```task
@ -147,6 +157,14 @@ qualifier sync gap, already reported to state-hub as message `70d08f35`).
the proposal and the evidence; record their workplan ids here. Do not
write their workplans for them.
2026-10-03 Codex continuation: all three original proposal messages remain
unread; no acceptance, owner workplan ID or decline found. Three local derived
`cross_owner_wait` observations are attached under `handoffs` in the monitor
snapshot, with stable case IDs and original message IDs. Obligor workplan IDs
remain unknown pending owner identification. Newer platform/warden replies
concern D1 readiness and the fetch-policy bug, respectively; neither answers
these proposals. No worker was woken. T06 remains `wait`.
## Progress log
- 2026-10-03 start: 108 open / 100 blocked / 232 wait / 69 human gates /
@ -193,4 +211,11 @@ write their workplans for them.
and two unread owner replies: GH-WP-0004-T06 (informed-decision) and
NK-WP-0042-T02 (user-engine). Follow-up on the CCR-2026-0031 lane: ops-warden
catalog entry plus fetch-pin regeneration (checklist row 6).
- 2026-10-03 Codex continuation: monitored 146 registered repos; six checkouts
unavailable. Visible file view: 107 open plans including residuals; actionable
non-residual view: 100 open / 94 blocked / 232 wait / 79 human gates, four
unrouted; 56 owner returns. Twelve qualifier differences and sixteen
unqualified waits are recorded as T05 follow-up. Four waits have terminal
predecessors, but separate human/owner gates and task prose still need review.
T06's three proposals remain unanswered. The monitor and seven regression
checks are implemented; no fleet cleanup or proposal acceptance is claimed.