From 0652a68b6127e542d854d835e1cbeae3b7300e14 Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Thu, 20 Aug 2026 07:10:25 -0400 Subject: [PATCH 1/7] chore(agents): remove .grok from the repository .grok was tracked and pushed to the remote -- 41 files -- which the no-dot-agent-directories rule forbids. Unlike oyatie's, console's .grok held no source that other code reads. The one code reference, tools/ci/ingest-soft-reds.mjs, WROTE to .grok/harness/lane-board.live.json: a generated artifact, already gitignored. Its output is redirected to ci/harness/ so no tool points into an agent directory. The tree is preserved at refs/preserved/grok; nothing is lost. Recover with: git checkout refs/preserved/grok -- . .gitignore now blocks .grok/, .claude/, .codex/, .cursor/, .agents/, .beads/. NOT DONE HERE: .beads/ and .cursor/ remain tracked. gitignore does not untrack what is already committed, and .cursor/ carries prose that references the moved paths. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 9 + .grok/README.md | 114 -- .grok/bin/console-goal | 262 ---- .grok/bin/console-hook-session-start | 64 - .grok/bin/console-hook-stop | 140 -- .grok/bin/console-learn | 169 --- .grok/harness/autonomy-merge.v1.json | 36 - .grok/harness/failure-classes.v1.json | 128 -- .grok/harness/hermes-learning.v1.json | 42 - .grok/harness/lane-board.v1.json | 94 -- .grok/harness/learning-loop.v1.json | 80 -- .grok/harness/ultragoal.v1.json | 34 - .grok/harness/work-graph.v1.json | 1153 ----------------- .grok/hooks/ultragoal-loop.json | 26 - .grok/memory/MEMORY.md | 13 - .grok/memory/reflections/2026-08-06.md | 15 - .grok/memory/tips/ops-soft-red-silence.md | 13 - .../rustfmt-and-executed-tests-baseline.md | 11 - .grok/memory/trajectories/smoke.md | 9 - .grok/programs/BUN-PARALLEL-DISCIPLINE.md | 126 -- .grok/programs/ROADMAP-DURABLE-GOAL.md | 80 -- .../assessment-2026-08-06-efficiency.md | 36 - .../console-backend-roadmap-durable.brief.md | 123 -- .grok/ultragoal/brief.md | 18 - .grok/ultragoal/goals.json | 13 - .grok/ultragoal/ledger.jsonl | 3 - .grok/ultragoal/prd.live.json | 7 - .grok/ultragoal/progress.txt | 4 - .grok/workflows/ci-fleet-tick.rhai | 121 -- .grok/workflows/console-drive.rhai | 283 ---- .grok/workflows/domain-increment.rhai | 227 ---- .grok/workflows/implement-lane.rhai | 162 --- .grok/workflows/learn.rhai | 82 -- .grok/workflows/pr-babysit.rhai | 217 ---- .grok/workflows/process-upgrade.rhai | 138 -- .grok/workflows/product-process-tick.rhai | 134 -- .grok/workflows/program-control.rhai | 202 --- .grok/workflows/program-tick.rhai | 106 -- .grok/workflows/ralph.rhai | 118 -- .grok/workflows/ralplan.rhai | 172 --- .grok/workflows/ultragoal.rhai | 124 -- .grok/workflows/work-manager.rhai | 144 -- 42 files changed, 9 insertions(+), 5043 deletions(-) delete mode 100644 .grok/README.md delete mode 100755 .grok/bin/console-goal delete mode 100755 .grok/bin/console-hook-session-start delete mode 100755 .grok/bin/console-hook-stop delete mode 100755 .grok/bin/console-learn delete mode 100644 .grok/harness/autonomy-merge.v1.json delete mode 100644 .grok/harness/failure-classes.v1.json delete mode 100644 .grok/harness/hermes-learning.v1.json delete mode 100644 .grok/harness/lane-board.v1.json delete mode 100644 .grok/harness/learning-loop.v1.json delete mode 100644 .grok/harness/ultragoal.v1.json delete mode 100644 .grok/harness/work-graph.v1.json delete mode 100644 .grok/hooks/ultragoal-loop.json delete mode 100644 .grok/memory/MEMORY.md delete mode 100644 .grok/memory/reflections/2026-08-06.md delete mode 100644 .grok/memory/tips/ops-soft-red-silence.md delete mode 100644 .grok/memory/tips/rustfmt-and-executed-tests-baseline.md delete mode 100644 .grok/memory/trajectories/smoke.md delete mode 100644 .grok/programs/BUN-PARALLEL-DISCIPLINE.md delete mode 100644 .grok/programs/ROADMAP-DURABLE-GOAL.md delete mode 100644 .grok/programs/assessment-2026-08-06-efficiency.md delete mode 100644 .grok/programs/briefs/console-backend-roadmap-durable.brief.md delete mode 100644 .grok/ultragoal/brief.md delete mode 100644 .grok/ultragoal/goals.json delete mode 100644 .grok/ultragoal/ledger.jsonl delete mode 100644 .grok/ultragoal/prd.live.json delete mode 100644 .grok/ultragoal/progress.txt delete mode 100644 .grok/workflows/ci-fleet-tick.rhai delete mode 100644 .grok/workflows/console-drive.rhai delete mode 100644 .grok/workflows/domain-increment.rhai delete mode 100644 .grok/workflows/implement-lane.rhai delete mode 100644 .grok/workflows/learn.rhai delete mode 100644 .grok/workflows/pr-babysit.rhai delete mode 100644 .grok/workflows/process-upgrade.rhai delete mode 100644 .grok/workflows/product-process-tick.rhai delete mode 100644 .grok/workflows/program-control.rhai delete mode 100644 .grok/workflows/program-tick.rhai delete mode 100644 .grok/workflows/ralph.rhai delete mode 100644 .grok/workflows/ralplan.rhai delete mode 100644 .grok/workflows/ultragoal.rhai delete mode 100644 .grok/workflows/work-manager.rhai diff --git a/.gitignore b/.gitignore index 91e0ea30c..a8e6755e9 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,12 @@ deploy/talos/**/talosconfig # Grok runtime board (materialized by tools/ci/ingest-soft-reds.mjs; not custodied) .grok/harness/lane-board.live.json + +# Agent working directories must never be committed. Load-bearing content is +# drained to a real home first (see PLAN.md 39); these hold scratch only. +.grok/ +.claude/ +.codex/ +.cursor/ +.agents/ +.beads/ diff --git a/.grok/README.md b/.grok/README.md deleted file mode 100644 index 3a2e730da..000000000 --- a/.grok/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Console delivery harness (Grok-native) - -Process control plane for parallel backend roadmap delivery. -**Not product authority** — that stays `docs/current/*`. - -**Does not use** `omc`, `omx`, `gjc`, or `hermes` CLIs. Ideas are incorporated; implementations live under `.grok/`. - -## Ideas absorbed - -| Source | What we took | -|--------|----------------| -| [Bun rewrite](https://bun.com/blog/bun-in-rust) | Prep contract, dual adversarial review, **edit the process** | -| OMC **ultragoal** | Durable `goals.json` + `ledger.jsonl` + native `/goal` handoff | -| OMC **ralplan** | Planner → Architect → Critic until APPROVE before heavy execute | -| OMC **ralph** | PRD story loop + “boulder never stops” Stop-hook loop | -| [Hermes Agent](https://github.com/NousResearch/hermes-agent) | Closed learning: curated memory, skill drafts, trajectories, persist nudges | -| Oyatie `.grok` | mm-learn / dual-track / soft-red queue patterns | - -## Autonomy (default) - -Under human **supervision** (intervene only if awry): - -1. **`console-drive`** runs the Bun control plane: soft-red tools → fleet fix/merge → **product implement** → process-edit → learn. -2. Soft reds are **inputs to fix loops**, not the product of the drive. -3. When a *class* of drive failure recurs, **edit** `console-drive.rhai` / harness / `tools/ci/*` (not chat memory). -4. Agent review **APPROVE** + Required CI/Security → merge (`autonomy-merge.v1.json`). -5. **Stop hook** loop-back while ultragoal active → re-dispatch `/workflow console-drive`. - -## Primary entry (implementation, not board-only) - -```text -/workflow console-drive -``` - -Optional: `{ "skip_product": true }` · `{ "allow_merge": false }` - -## Ultragoal + native `/goal` + hooks - -```text -# 1) Inject durable plan + arm loop -/workflow ultragoal {"action":"activate","objective":"Drain tip-serial queue and land Wave 0 pure tests","workflow":"console-drive"} - -# 2) Session-native goal (Grok) -/goal - -# 3) Drive implementation -/workflow console-drive -# vague/large first: -/workflow ralplan {"task":"…"} -/workflow ralph - -# 4) Learn → process-upgrade if class repeats -/workflow learn -``` - -Hooks (project trust required — `/hooks-trust`): - -| Event | Script | Effect | -|-------|--------|--------| -| `SessionStart` | `bin/console-hook-session-start` | Injects active ultragoal context | -| `Stop` | `bin/console-hook-stop` | **Blocks stop** while `active-goal.live.json.active`; instructs `/workflow …` dispatch | - -```json -// .grok/hooks/ultragoal-loop.json -``` - -CLI helpers: - -```bash -.grok/bin/console-goal status|activate|deactivate|checkpoint|handoff -.grok/bin/console-learn from-event --id … --summary '…' --classes 'ops.soft-red-silence' -``` - -## Workflow catalog - -| Workflow | Role | -|----------|------| -| **`console-drive`** | **Implementation control plane**: soft-red tools → fleet fix/merge → product build → process-edit → learn | -| **`program-control`** | Lighter meta heartbeat (prefer console-drive) | -| **`ultragoal`** | Durable goals + activate hook loop + `/goal` handoff | -| **`ralplan`** | Consensus planning (Planner/Architect/Critic) | -| **`ralph`** | PRD story execution loop until APPROVE | -| **`learn`** | Hermes-style reflection/tip/skill promotion | -| **`work-manager`** | Board + soft reds/blocks (no silence) | -| **`implement-lane`** | Claim one item → PR | -| **`pr-babysit`** | Repair → review → merge | -| `program-tick` | Legacy dual-track | -| `domain-increment` | Full backend SDLC + Admit | -| `process-upgrade` | Failure class → permanent control | - -## Layout - -```text -.grok/ - bin/ console-goal console-learn console-hook-* - hooks/ ultragoal-loop.json (SessionStart + Stop) - ultragoal/ goals.json ledger.jsonl active-goal prd progress brief - harness/ autonomy-merge lane-board learning-loop hermes-learning ultragoal - workflows/ *.rhai - memory/ MEMORY.md reflections/ tips/ trajectories/ - skills/ learned skill drafts - programs/ BUN-PARALLEL-DISCIPLINE ROADMAP-DURABLE-GOAL -``` - -## Soft reds & blocks - -Every soft red/block → `harness/lane-board.live.json` (`ops.soft-red-silence` if dropped). - -## Fix the process - -1. Map red → `failure-classes.v1.json` -2. `/workflow learn` then `/workflow process-upgrade` if class ≥2 -3. Edit workflows/harness/tools — not chat memory -4. Never one-shot hooks named for a PR diff --git a/.grok/bin/console-goal b/.grok/bin/console-goal deleted file mode 100755 index c88248b6b..000000000 --- a/.grok/bin/console-goal +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/env python3 -"""Grok-native ultragoal helpers (ideas from OMC/GJC ultragoal; no omc CLI). - -Subcommands: - status - activate --objective "..." [--story-id ID] [--workflow program-control|ralph] - deactivate - checkpoint --story-id ID --status complete|blocked --evidence "..." - ledger-append --event TYPE --payload-json '{...}' - handoff # print native /goal handoff text -""" -from __future__ import annotations - -import argparse -import json -import sys -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - - -def now() -> str: - return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - -def repo_root(start: Path) -> Path: - cur = start.resolve() - for p in [cur, *cur.parents]: - if (p / ".grok" / "harness" / "ultragoal.v1.json").is_file(): - return p - return cur - - -def ug_dir(repo: Path) -> Path: - return repo / ".grok" / "ultragoal" - - -def load_json(path: Path, default: Any) -> Any: - if not path.is_file(): - return default - return json.loads(path.read_text()) - - -def write_json(path: Path, data: Any) -> None: - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(data, indent=2) + "\n") - - -def append_ledger(repo: Path, event: str, payload: dict[str, Any]) -> None: - line = {"ts": now(), "event": event, **payload} - path = ug_dir(repo) / "ledger.jsonl" - path.parent.mkdir(parents=True, exist_ok=True) - with path.open("a") as f: - f.write(json.dumps(line, separators=(",", ":")) + "\n") - - -def cmd_status(args: argparse.Namespace) -> int: - repo = repo_root(Path(args.cwd)) - active = load_json(ug_dir(repo) / "active-goal.live.json", {}) - goals = load_json(ug_dir(repo) / "goals.json", {}) - prd = load_json(ug_dir(repo) / "prd.live.json", {}) - stories = prd.get("stories") or [] - done = sum(1 for s in stories if s.get("passes") is True) - print( - json.dumps( - { - "active": active, - "goals_count": len(goals.get("goals") or []), - "activeGoalId": goals.get("activeGoalId"), - "prd_stories": len(stories), - "prd_passes": done, - "ledger": str(ug_dir(repo) / "ledger.jsonl"), - }, - indent=2, - ) - ) - return 0 - - -def cmd_activate(args: argparse.Namespace) -> int: - repo = repo_root(Path(args.cwd)) - active = { - "active": True, - "mode": args.mode, - "objective": args.objective, - "plan_id": args.plan_id, - "story_id": args.story_id, - "status": "active", - "iteration": 0, - "max_iterations": args.max_iterations, - "dispatch_workflow": args.workflow, - "ralph_active": args.workflow == "ralph", - "ralplan_required": args.ralplan_required, - "updated_at": now(), - "handoff": ( - f"Native Grok goal: run `/goal {args.objective}` in this session. " - f"Then `/workflow {args.workflow}`. Soft reds must stay on lane board. " - f"Hooks will block Stop while active-goal.live.json.active is true." - ), - } - write_json(ug_dir(repo) / "active-goal.live.json", active) - append_ledger( - repo, - "activate", - { - "objective": args.objective, - "story_id": args.story_id, - "workflow": args.workflow, - }, - ) - print(json.dumps(active, indent=2)) - print("\n--- HANDOFF (paste / run) ---\n", file=sys.stderr) - print(active["handoff"], file=sys.stderr) - return 0 - - -def cmd_deactivate(args: argparse.Namespace) -> int: - repo = repo_root(Path(args.cwd)) - active = { - "active": False, - "mode": None, - "objective": None, - "plan_id": None, - "story_id": None, - "status": "idle", - "iteration": 0, - "max_iterations": 50, - "dispatch_workflow": "program-control", - "ralph_active": False, - "ralplan_required": False, - "updated_at": now(), - "handoff": "No active ultragoal.", - } - write_json(ug_dir(repo) / "active-goal.live.json", active) - append_ledger(repo, "deactivate", {"reason": args.reason}) - print(json.dumps(active, indent=2)) - return 0 - - -def cmd_checkpoint(args: argparse.Namespace) -> int: - repo = repo_root(Path(args.cwd)) - goals_path = ug_dir(repo) / "goals.json" - goals = load_json(goals_path, {"goals": []}) - found = False - for g in goals.get("goals") or []: - if g.get("id") == args.story_id: - found = True - g["status"] = args.status - g["updatedAt"] = now() - g["evidence"] = args.evidence - if args.status == "complete": - g["completedAt"] = now() - break - if not found and args.story_id: - goals.setdefault("goals", []).append( - { - "id": args.story_id, - "title": args.story_id, - "objective": args.evidence, - "status": args.status, - "attempt": 1, - "updatedAt": now(), - "evidence": args.evidence, - } - ) - goals["updatedAt"] = now() - write_json(goals_path, goals) - append_ledger( - repo, - "checkpoint", - { - "story_id": args.story_id, - "status": args.status, - "evidence": args.evidence, - }, - ) - print(json.dumps({"ok": True, "story_id": args.story_id, "status": args.status}, indent=2)) - return 0 - - -def cmd_handoff(args: argparse.Namespace) -> int: - repo = repo_root(Path(args.cwd)) - active = load_json(ug_dir(repo) / "active-goal.live.json", {}) - if not active.get("active"): - print("No active ultragoal. Activate first: console-goal activate --objective '…'") - return 1 - obj = active.get("objective") or "" - wf = active.get("dispatch_workflow") or "program-control" - print( - f"""# Ultragoal handoff (Grok-native) - -1. Ensure session goal is set (Grok native): - /goal {obj} - -2. Drive the control plane (hook will re-dispatch on Stop while active): - /workflow {wf} - -3. Soft reds/blocks: work-manager must enqueue (ops.soft-red-silence forbidden). - -4. Autonomy: agent review APPROVE + Required CI/Security → merge. - -5. When quality gate met for final story: - .grok/bin/console-goal checkpoint --story-id --status complete --evidence '…' - .grok/bin/console-goal deactivate --reason complete - /goal clear - -Active state: {json.dumps(active, indent=2)} -""" - ) - return 0 - - -def cmd_ledger_append(args: argparse.Namespace) -> int: - repo = repo_root(Path(args.cwd)) - payload = json.loads(args.payload_json) if args.payload_json else {} - append_ledger(repo, args.event, payload) - print(json.dumps({"ok": True, "event": args.event}, indent=2)) - return 0 - - -def main() -> int: - ap = argparse.ArgumentParser(description="console-goal ultragoal helpers") - ap.add_argument("--cwd", default=".") - sub = ap.add_subparsers(dest="cmd", required=True) - - p = sub.add_parser("status") - p.set_defaults(func=cmd_status) - - p = sub.add_parser("activate") - p.add_argument("--objective", required=True) - p.add_argument("--story-id", default=None) - p.add_argument("--plan-id", default=None) - p.add_argument("--mode", default="aggregate", choices=["aggregate", "per_story"]) - p.add_argument("--workflow", default="program-control") - p.add_argument("--max-iterations", type=int, default=50) - p.add_argument("--ralplan-required", action="store_true") - p.set_defaults(func=cmd_activate) - - p = sub.add_parser("deactivate") - p.add_argument("--reason", default="manual") - p.set_defaults(func=cmd_deactivate) - - p = sub.add_parser("checkpoint") - p.add_argument("--story-id", required=True) - p.add_argument("--status", required=True, choices=["complete", "blocked", "in_progress"]) - p.add_argument("--evidence", required=True) - p.set_defaults(func=cmd_checkpoint) - - p = sub.add_parser("handoff") - p.set_defaults(func=cmd_handoff) - - p = sub.add_parser("ledger-append") - p.add_argument("--event", required=True) - p.add_argument("--payload-json", default="{}") - p.set_defaults(func=cmd_ledger_append) - - args = ap.parse_args() - return int(args.func(args)) - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.grok/bin/console-hook-session-start b/.grok/bin/console-hook-session-start deleted file mode 100755 index c12c02b2f..000000000 --- a/.grok/bin/console-hook-session-start +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -"""Grok SessionStart hook: inject ultragoal context (non-blocking). - -Prints a short status line to stderr for session log; exits 0 always. -""" -from __future__ import annotations - -import json -import sys -from pathlib import Path - - -def find_repo(cwd: Path) -> Path: - cur = cwd.resolve() - for p in [cur, *cur.parents]: - if (p / ".grok" / "ultragoal" / "active-goal.live.json").is_file(): - return p - return cur - - -def main() -> int: - raw = sys.stdin.read() - try: - payload = json.loads(raw) if raw.strip() else {} - except json.JSONDecodeError: - payload = {} - cwd = Path(payload.get("cwd") or payload.get("workspaceRoot") or ".") - repo = find_repo(cwd) - path = repo / ".grok" / "ultragoal" / "active-goal.live.json" - if not path.is_file(): - return 0 - try: - active = json.loads(path.read_text()) - except (OSError, json.JSONDecodeError): - return 0 - if not active.get("active"): - print("[console-ultragoal] idle", file=sys.stderr) - return 0 - wf = active.get("dispatch_workflow") or "program-control" - obj = (active.get("objective") or "")[:160] - print( - f"[console-ultragoal] ACTIVE → set `/goal {obj}` if not set; " - f"drive `/workflow {wf}`; Stop hook will loop-back until deactivate.", - file=sys.stderr, - ) - # Optional additionalContext for hosts that honor it on SessionStart - sys.stdout.write( - json.dumps( - { - "hookSpecificOutput": { - "hookEventName": "SessionStart", - "additionalContext": ( - f"Ultragoal active: {obj}. Run /workflow {wf}. " - f"Soft reds must stay on lane board. Hermes learn after each tick." - ), - } - } - ) - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.grok/bin/console-hook-stop b/.grok/bin/console-hook-stop deleted file mode 100755 index d60b20646..000000000 --- a/.grok/bin/console-hook-stop +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 -"""Grok Stop hook: ultragoal/ralph loop-back + workflow dispatch instruction. - -Reads stdin JSON (Grok hook payload). Writes decision JSON to stdout. - -If active-goal.live.json.active: - - increment iteration - - block stop with reason that tells the model to run the dispatch workflow - - force-stop if max_iterations exceeded - -If idle: allow stop (exit 0, empty/allow). -""" -from __future__ import annotations - -import json -import sys -from datetime import datetime, timezone -from pathlib import Path - - -def now() -> str: - return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - -def find_repo(cwd: Path) -> Path: - cur = cwd.resolve() - for p in [cur, *cur.parents]: - if (p / ".grok" / "ultragoal" / "active-goal.live.json").is_file(): - return p - return cur - - -def main() -> int: - raw = sys.stdin.read() - try: - payload = json.loads(raw) if raw.strip() else {} - except json.JSONDecodeError: - payload = {} - - # Session-end Stop fires must not gate - reason = payload.get("reason") or payload.get("stopReason") or "" - if reason in ("channel_closed", "shutdown"): - return 0 - - cwd = Path(payload.get("cwd") or payload.get("workspaceRoot") or ".") - repo = find_repo(cwd) - active_path = repo / ".grok" / "ultragoal" / "active-goal.live.json" - if not active_path.is_file(): - return 0 - - try: - active = json.loads(active_path.read_text()) - except (OSError, json.JSONDecodeError): - return 0 - - if not active.get("active"): - return 0 - - # Avoid infinite same-turn thrash: still block but note stopHookActive - stop_hook_active = bool(payload.get("stopHookActive")) - iteration = int(active.get("iteration") or 0) + 1 - max_i = int(active.get("max_iterations") or 50) - active["iteration"] = iteration - active["updated_at"] = now() - - if iteration > max_i: - active["status"] = "exhausted" - active_path.write_text(json.dumps(active, indent=2) + "\n") - out = { - "continue": False, - "stopReason": f"ultragoal max_iterations={max_i} exhausted — human supervise", - } - sys.stdout.write(json.dumps(out)) - return 0 - - active_path.write_text(json.dumps(active, indent=2) + "\n") - - wf = active.get("dispatch_workflow") or "console-drive" - # Prefer console-drive (implements product+fleet) over board-only heartbeats - dispatch_wf = "console-drive" if wf in ("program-control", "console-drive", "") else wf - obj = active.get("objective") or "(see active-goal.live.json)" - boulder = "The boulder never stops." if active.get("ralph_active") else "Ultragoal loop continues." - - # Mechanical dispatch artifact (workflows/agents read this; not aspirational only) - dispatch_path = repo / ".grok" / "ultragoal" / "dispatch.live.json" - dispatch = { - "ts": now(), - "iteration": iteration, - "max_iterations": max_i, - "workflow": dispatch_wf, - "objective": obj, - "commands": [ - "node tools/ci/ingest-soft-reds.mjs", - f"/workflow {dispatch_wf}", - "/workflow learn", - ], - "ralph_active": bool(active.get("ralph_active")), - "ralplan_required": bool(active.get("ralplan_required")), - "stop_hook_active": stop_hook_active, - } - try: - dispatch_path.write_text(json.dumps(dispatch, indent=2) + "\n") - except OSError: - pass - - # Best-effort soft-red ingest so silence cannot accumulate across Stop fires - try: - import subprocess - - subprocess.run( - ["node", str(repo / "tools" / "ci" / "ingest-soft-reds.mjs")], - cwd=str(repo), - capture_output=True, - timeout=120, - check=False, - ) - except Exception: - pass - - msg = ( - f"{boulder} Ultragoal ACTIVE (iteration {iteration}/{max_i}). " - f"Objective: {obj}. " - f"Dispatch artifact: .grok/ultragoal/dispatch.live.json → /workflow {dispatch_wf}. " - f"Do NOT end the turn idle — IMPLEMENT via durable workflows, not ticket status. " - f"1) Soft reds: `node tools/ci/ingest-soft-reds.mjs` (hook already attempted). " - f"2) `/workflow {dispatch_wf}` (console-drive runs fleet repair + product build). " - f"3) If ralplan_required and no approved plan, `/workflow ralplan` first. " - f"4) `/workflow learn` after progress; edit console-drive.rhai when drive fails as a class. " - f"5) Only clear goal after quality gate + `.grok/bin/console-goal deactivate`. " - ) - if stop_hook_active: - msg += "stopHookActive=true — make concrete tool progress this round (no pure narration). " - - # Grok Stop decision: block keeps agent working - sys.stdout.write(json.dumps({"decision": "block", "reason": msg})) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.grok/bin/console-learn b/.grok/bin/console-learn deleted file mode 100755 index eb673715c..000000000 --- a/.grok/bin/console-learn +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python3 -"""Hermes-inspired closed learning loop for Console (no hermes CLI). - -Writes: - - .grok/memory/reflections/{date}.md - - .grok/memory/tips/{slug}.md when classes present - - .grok/skills/{slug}/SKILL.md drafts on hard/repeat debt - - .grok/memory/trajectories/{id}.md compress notes - - optional failure-class note for process-upgrade - -Usage: - .grok/bin/console-learn from-event --id tick-1 --classes ops.soft-red-silence,docs.tip-blob-prebind --summary '…' - .grok/bin/console-learn from-event --id pr-590 --classes '' --summary 'merged after prebind fix' --force-skill -""" -from __future__ import annotations - -import argparse -import json -import re -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - - -def now() -> str: - return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - -def utc_date() -> str: - return datetime.now(timezone.utc).strftime("%Y-%m-%d") - - -def find_repo(start: Path) -> Path: - cur = start.resolve() - for p in [cur, *cur.parents]: - if (p / ".grok" / "harness" / "learning-loop.v1.json").is_file(): - return p - return cur - - -def slugify(s: str) -> str: - s = s.lower().strip() - s = re.sub(r"[^a-z0-9]+", "-", s).strip("-") - return s[:60] or "tip" - - -def cmd_from_event(args: argparse.Namespace) -> int: - repo = find_repo(Path(args.cwd)) - classes = [c.strip() for c in (args.classes or "").split(",") if c.strip()] - hard = [c.strip() for c in (args.hard_fails or "").split(",") if c.strip()] - mem = repo / ".grok" / "memory" - ref_dir = mem / "reflections" - ref_dir.mkdir(parents=True, exist_ok=True) - date = utc_date() - ref_path = ref_dir / f"{date}.md" - block = [ - f"\n## Event `{args.id}` ({now()})\n", - f"- summary: {args.summary}\n", - f"- failure_classes: {', '.join(classes) or '(none)'}\n", - f"- hard_fails: {', '.join(hard) or '(none)'}\n", - "\n### Hermes loop actions\n", - "- Persist curated tip if class present.\n", - "- Prefer process edit (workflow/harness/tool) when class repeats.\n", - "- Soft reds must be on lane-board (ops.soft-red-silence).\n", - ] - with ref_path.open("a") as f: - f.writelines(block) - - tip_path = None - if classes or hard: - tips = mem / "tips" - tips.mkdir(parents=True, exist_ok=True) - title = classes[0] if classes else hard[0] - tip_path = tips / f"{slugify(title)}.md" - if not tip_path.is_file(): - tip_path.write_text( - f"# Setup Tip — {title}\n\n" - f"- **event:** `{args.id}`\n" - f"- **summary:** {args.summary}\n\n" - f"## Rule\n\n" - f"When `{title}` appears, apply the control in " - f"`.grok/harness/failure-classes.v1.json` before expanding product scope.\n\n" - f"## Do not\n\n" - f"- Silent-drop soft reds.\n" - f"- Call omc/omx/gjc/hermes CLIs as control plane.\n" - ) - - skill_path = None - if args.force_skill or hard or (classes and args.promote_skill): - name = slugify(classes[0] if classes else (hard[0] if hard else "process-hardening")) - skill_dir = repo / ".grok" / "skills" / name - skill_dir.mkdir(parents=True, exist_ok=True) - skill_path = skill_dir / "SKILL.md" - if not skill_path.is_file() or args.force_skill: - skill_path.write_text( - f"---\n" - f"name: {name}\n" - f"description: Hermes-style skill draft from Console learning loop (event {args.id})\n" - f"---\n\n" - f"# {name}\n\n" - f"## When to use\n\n" - f"Failure class: `{classes[0] if classes else 'unknown'}`.\n\n" - f"## Steps\n\n" - f"1. Map to failure-classes.v1.json control.\n" - f"2. Patch workflow/harness/tool (not chat memory).\n" - f"3. Re-run admit / Required checks.\n" - f"4. Enqueue any residual soft reds on lane-board.\n\n" - f"## Non-goals\n\n" - f"- No omc/omx/gjc/hermes CLI dependency.\n" - ) - - tdir = mem / "trajectories" - tdir.mkdir(parents=True, exist_ok=True) - tmd = tdir / f"{slugify(args.id)}.md" - tmd.write_text( - f"# Trajectory `{args.id}`\n\n" - f"- ts: {now()}\n" - f"- summary: {args.summary}\n" - f"- classes: {classes}\n" - f"- hard: {hard}\n" - f"- reflection: {ref_path}\n" - f"- tip: {tip_path}\n" - f"- skill: {skill_path}\n" - ) - - # nudge MEMORY.md (bounded) - memory_md = mem / "MEMORY.md" - if not memory_md.is_file(): - memory_md.write_text( - "# Console agent MEMORY (bounded, Hermes-style)\n\n" - "Curated process facts only. Promote durable rules to harness/workflows.\n\n" - ) - if classes: - with memory_md.open("a") as f: - f.write(f"- [{utc_date()}] class `{classes[0]}`: {args.summary[:120]}\n") - - print( - json.dumps( - { - "reflection": str(ref_path), - "tip": str(tip_path) if tip_path else None, - "skill": str(skill_path) if skill_path else None, - "trajectory": str(tmd), - }, - indent=2, - ) - ) - return 0 - - -def main() -> int: - ap = argparse.ArgumentParser(description="console-learn Hermes-inspired loop") - ap.add_argument("--cwd", default=".") - sub = ap.add_subparsers(dest="cmd", required=True) - sp = sub.add_parser("from-event") - sp.add_argument("--cwd", default=".") - sp.add_argument("--id", required=True) - sp.add_argument("--summary", required=True) - sp.add_argument("--classes", default="") - sp.add_argument("--hard-fails", default="") - sp.add_argument("--force-skill", action="store_true") - sp.add_argument("--promote-skill", action="store_true") - sp.set_defaults(func=cmd_from_event) - args = ap.parse_args() - return int(args.func(args)) - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.grok/harness/autonomy-merge.v1.json b/.grok/harness/autonomy-merge.v1.json deleted file mode 100644 index 8941fce23..000000000 --- a/.grok/harness/autonomy-merge.v1.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "version": "1.0.0", - "description": "Autonomous merge policy: agent review approve + Required CI/Security green → merge without human gate. Human supervises; intervenes only if awry.", - "merge_when": { - "agent_review_verdict": "approve", - "required_ci": "success", - "required_security": "success", - "mergeable": true, - "not_behind": true - }, - "do_not_wait_for_human_if": [ - "approve_and_green" - ], - "if_not_approve": { - "action": "enqueue_review_fix", - "board_kind": "review_fix", - "loop": "fix until approve or max_fix_rounds", - "max_fix_rounds": 5, - "on_exhaust": "board status blocked + notify operator; do not silent-drop" - }, - "soft_reds_and_blocks": { - "must_enqueue": true, - "owner": "work-manager + pr-babysit", - "forbidden": "silent leave unaddressed" - }, - "human_supervision": { - "default": "observe_only", - "intervene_if": [ - "operator_flags_awry", - "product_hold_would_be_cleared", - "production_secrets_exposure", - "max_fix_rounds_exhausted" - ] - }, - "supersedes": "leader-only merge for routine agent-approved PRs (Console operator override 2026-08-06)" -} diff --git a/.grok/harness/failure-classes.v1.json b/.grok/harness/failure-classes.v1.json deleted file mode 100644 index 73c215cad..000000000 --- a/.grok/harness/failure-classes.v1.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "version": "1.0.0", - "description": "Reusable failure classes for Console process intelligence. Process edits add controls here + in workflows/tools \u2014 never one-shot PR-named hooks.", - "promote_process_edit_after_repeats": 2, - "classes": [ - { - "id": "auth.unsigned-tip", - "symptom": "C or T not signed by pinned SSH authority", - "detector": "git verify-commit + scripts/console/verify-console-pr-authority-bootstrap.mjs", - "control": "Resign C/T; handoff checklist; never force-push unsigned tip", - "admit_commands": [] - }, - { - "id": "docs.tip-blob-prebind", - "symptom": "documentation-manifest blob_sha does not match tip ledger blob", - "detector": "npm run check:doc-manifest (on tip tree)", - "control": "Prebind tip blob on C; T only adds Authority tip section", - "admit_commands": [ - "npm run check:doc-manifest", - "npm run check:doc-links" - ] - }, - { - "id": "ci.verify-job-ids", - "symptom": "verify.mjs / preflight digests missing new CI jobs", - "detector": "npm run check:ci-preflight", - "control": "Update digests/verify when editing ci.yml job graph", - "admit_commands": [ - "npm run check:ci-preflight" - ] - }, - { - "id": "ci.residual-buck-growth", - "symptom": "New //tools/buck product wrappers in ci.yml without map row", - "detector": "npm run check:product-buck-residual", - "control": "Map or document unmapped; ceiling only shrinks", - "admit_commands": [ - "npm run check:product-buck-residual" - ] - }, - { - "id": "ops.passive-wait", - "symptom": "Autonomous turn only restates unchanged CI WAIT", - "detector": "session review / program-tick", - "control": "Dual track: fleet action + product/process action every wake", - "admit_commands": [] - }, - { - "id": "ops.mid-run-push", - "symptom": "Push while Required CI in_progress cancels running cone", - "detector": "gh run list status in_progress on same branch", - "control": "Batch local fixes \u2192 one push \u2192 wait complete", - "admit_commands": [] - }, - { - "id": "ops.missed-tip-sync", - "symptom": "Open PR mergeStateStatus BEHIND after main advances", - "detector": "gh pr view --json mergeStateStatus", - "control": "open-pr-fleet tip-sync report; restack in PR worktree", - "admit_commands": [] - }, - { - "id": "ops.skip-admit", - "symptom": "Push/PR without local gates that would have caught hosted red", - "detector": "compare hosted fail step to admit_commands for class", - "control": "domain-increment Admit phase; optional PreToolUse on push", - "admit_commands": [ - "npm run check:ci-preflight" - ] - }, - { - "id": "ops.soft-red-silence", - "symptom": "Soft red or non-hard block observed but not on lane board / left unowned", - "detector": "work-manager silence_check; program-control productivity audit; lane-board.live.json vs open PR scan", - "control": "work-manager must upsert every soft_red_sources/hard_block_sources hit; pr-babysit requeues residuals", - "admit_commands": [] - }, - { - "id": "ops.tip-serial-contention", - "symptom": "Multiple open tip-writing PRs (manifest/baseline/tip/ci.yml) thrashing serial queue", - "detector": "tools/ci/assess-tip-contention.mjs or work-manager tip writer inventory", - "control": "Serialize tip queue; batch pure-domain tests; no new tip PR while tip_writers>=2", - "admit_commands": [] - }, - { - "id": "ops.review-not-approve-stall", - "symptom": "PR review not approve and no fix loop / board review_fix item", - "detector": "pr-babysit FixLoop; board kind review_fix", - "control": "Fix until approve or max rounds then board blocked with evidence; merge only on approve", - "admit_commands": [] - }, - { - "id": "ops.multi-pr-wall-tax", - "symptom": "Several small PRs each pay full Required CI wall (~25-45m)", - "detector": "count open PRs with full CI vs total LOC changed", - "control": "Stack or batch when work is pure unit tests and would tip-serialize anyway", - "admit_commands": [] - }, - { - "id": "ops.rustfmt-drift", - "symptom": "Backend rustfmt check fails", - "detector": "cd backend && cargo fmt --all -- --check", - "control": "Admit requires rustfmt", - "admit_commands": [ - "cd backend && cargo fmt --all -- --check" - ] - }, - { - "id": "ops.executed-tests-baseline", - "symptom": "executed-tests gain without baseline update", - "detector": "node scripts/check-executed-tests.mjs", - "control": "Admit requires baseline --update commit", - "admit_commands": [ - "node scripts/check-executed-tests.mjs" - ] - }, - { - "id": "lens.manifest-drift", - "symptom": "CLAUDE.md or README.md lens projection drifted from the AGENTS.md canonical list", - "detector": "node scripts/check-reasoning-lens-manifest.mjs", - "control": "Regenerate the numbered projections from AGENTS.md; never hand-edit one projection alone", - "admit_commands": [ - "node --test scripts/check-reasoning-lens-manifest.test.mjs", - "node scripts/check-reasoning-lens-manifest.mjs" - ] - } - ] -} diff --git a/.grok/harness/hermes-learning.v1.json b/.grok/harness/hermes-learning.v1.json deleted file mode 100644 index 0f690284b..000000000 --- a/.grok/harness/hermes-learning.v1.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "version": "1.0.0", - "description": "Hermes Agent (NousResearch) learning-loop ideas adapted to Console Grok harness — no hermes CLI.", - "source": "https://github.com/NousResearch/hermes-agent (ideas only)", - "forbidden_clis": ["hermes", "omc", "omx", "gjc"], - "loop_parts": { - "curated_memory": { - "paths": [ - ".grok/memory/MEMORY.md", - ".grok/memory/tips/", - ".grok/memory/reflections/" - ], - "rule": "Bounded, curated facts and process tips — not unbounded chat dumps. Prefer promote-to-harness over long MEMORY." - }, - "skill_creation": { - "path": ".grok/skills/{slug}/SKILL.md", - "trigger": "failure_class repeats >= promote_process_edit_after_repeats OR hard fail after complex multi-step task", - "rule": "Draft skills from evidence; promote durable control into workflows/harness/tools (Bun doctrine)." - }, - "skill_self_improve": { - "trigger": "same class recurs after skill draft exists", - "action": "revise skill + patch failure-classes / workflow admit step" - }, - "persist_nudge": { - "when": ["Stop hook with active ultragoal", "program-control productivity phase", "learn workflow"], - "message": "Persist learnings: reflection tip skill trajectory before declaring done." - }, - "session_search": { - "tools": ["hindsight recall", "t session_search", "bd list"], - "rule": "Cross-session recall before inventing process folklore." - }, - "trajectory": { - "path": ".grok/memory/trajectories/{id}.md", - "rule": "Compress what failed, what process changed, evidence pointers — not full transcripts." - } - }, - "map_to_console": { - "process_edit": "failure-classes.v1.json + workflows/*.rhai + tools/ci/*", - "soft_red_silence": "ops.soft-red-silence — never leave soft reds unowned", - "quality_gate": "agent review APPROVE + Required CI/Security (autonomy-merge.v1.json)" - } -} diff --git a/.grok/harness/lane-board.v1.json b/.grok/harness/lane-board.v1.json deleted file mode 100644 index c1d86822e..000000000 --- a/.grok/harness/lane-board.v1.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "version": "1.0.0", - "description": "Shared lane board for Console autonomous drive. Projection of Beads + open-PR + soft-red inventory. Not product authority.", - "path": ".grok/harness/lane-board.live.json", - "statuses": [ - "ready", - "claimed", - "implementing", - "pr_open", - "review_pending", - "fixing", - "approved", - "merged", - "blocked", - "deferred" - ], - "lane_kinds": [ - "product", - "process", - "substrate", - "soft_red", - "hard_block", - "fleet_fix", - "review_fix" - ], - "priority_order": [ - "hard_block", - "soft_red", - "review_fix", - "fleet_fix", - "substrate", - "process", - "product" - ], - "soft_red_sources": [ - "ci_non_required_fail", - "ci_flaky_or_cancelled", - "merge_behind", - "merge_dirty_or_conflicting", - "auth_bootstrap_fail", - "tip_unsigned_or_prebind", - "repo_gates_or_preflight_fail", - "waiting_ci_stale_gt_threshold", - "review_changes_requested", - "review_comment_unaddressed", - "tip_serial_contention", - "baseline_or_manifest_drift", - "beads_blocked_without_owner" - ], - "hard_block_sources": [ - "required_ci_fail", - "required_security_fail", - "product_hold", - "authority_train_reject", - "merge_protection_reject" - ], - "silence_forbidden": true, - "rules": { - "every_soft_red_must_become_board_item": true, - "every_block_must_become_board_item": true, - "no_silent_drop": "If a soft red or block is observed and not already on the board with same source_key, work-manager MUST enqueue it.", - "dedupe_key": "source_key", - "autonomous_merge": { - "require_agent_review_approve": true, - "require_required_ci_green": true, - "require_required_security_green": true, - "human_required_only_if": [ - "product_hold_clearance", - "production_or_secrets", - "operator_explicit_pause", - "review_cannot_approve_after_max_fix_rounds" - ], - "max_fix_rounds_default": 5 - } - }, - "item_schema": { - "id": "string board id or beads id", - "source_key": "stable dedupe e.g. soft_red:pr:591:auth_bootstrap_fail", - "kind": "lane_kinds enum", - "title": "string", - "status": "statuses enum", - "priority": "P0|P1|P2|P3", - "allowlist": ["path globs"], - "forbidden": ["path globs"], - "holds_checked": "string", - "pr_number": "number|null", - "beads_id": "string|null", - "goal_id": "G00x|null", - "evidence": "urls or paths", - "fix_rounds": "number", - "last_review_verdict": "approve|changes_requested|comment|null", - "updated_at": "ISO-8601 when written by a tick" - } -} diff --git a/.grok/harness/learning-loop.v1.json b/.grok/harness/learning-loop.v1.json deleted file mode 100644 index 444b39179..000000000 --- a/.grok/harness/learning-loop.v1.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "version": "1.1.0", - "description": "Closed process learning loop for Console — edit workflows/tools, not product scope. Inspired by Bun rewrite + Hermes Agent learning (NousResearch) without hermes/omc/omx/gjc CLIs.", - "inspired_by": [ - "https://bun.com/blog/bun-in-rust", - "https://github.com/NousResearch/hermes-agent", - "oyatie .grok/bin/mm-learn", - "OMC ultragoal/ralplan/ralph ideas only" - ], - "forbidden_clis": ["hermes", "omc", "omx", "gjc"], - "lanes": { - "daily_reflection": { - "path_template": ".grok/memory/reflections/{date}.md", - "purpose": "What failed, which class, what control changed" - }, - "setup_tip": { - "path_template": ".grok/memory/tips/{slug}.md", - "purpose": "Reusable operational prescription for a failure class" - }, - "curated_memory": { - "path_template": ".grok/memory/MEMORY.md", - "purpose": "Bounded Hermes-style curated facts; promote durable rules to harness" - }, - "skill_draft": { - "path_template": ".grok/skills/{slug}/SKILL.md", - "purpose": "Skill creation from experience; revise on recurrence" - }, - "trajectory": { - "path_template": ".grok/memory/trajectories/{id}.md", - "purpose": "Compressed execution learnings" - }, - "process_edit": { - "path_template": ".grok/programs/evidence/process-edits-{date}.md", - "purpose": "Record workflow/harness files changed for a class" - } - }, - "tools": { - "console_learn": ".grok/bin/console-learn", - "workflow": "learn" - }, - "triggers": { - "after_hard_fail": true, - "after_repeat_failure_class": 2, - "after_hosted_preflight_red": true, - "after_passive_wait_incident": true, - "after_ralph_iteration": true, - "after_program_control_tick": true, - "after_soft_red_silence": true - }, - "actions": { - "map_to_failure_class": true, - "run_process_upgrade_workflow": true, - "run_console_learn": true, - "forbid_one_shot_hooks": true, - "product_scope_unchanged": true, - "skill_self_improve_on_repeat": true - }, - "process_edit_allowlist": [ - ".grok/workflows/**", - ".grok/harness/**", - ".grok/programs/**", - ".grok/hooks/**", - ".grok/bin/**", - ".grok/ultragoal/**", - "tools/ci/**", - "scripts/console/**", - "scripts/check-*.mjs", - "package.json" - ], - "process_edit_forbidden": [ - "docs/current/**", - "backend/crates/**", - "deploy/**" - ], - "verified_empty_is_work": true, - "see_also": [ - ".grok/harness/hermes-learning.v1.json", - ".grok/harness/ultragoal.v1.json" - ] -} diff --git a/.grok/harness/ultragoal.v1.json b/.grok/harness/ultragoal.v1.json deleted file mode 100644 index 42375b26f..000000000 --- a/.grok/harness/ultragoal.v1.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": "1.0.0", - "description": "Grok-native ultragoal: durable multi-story plan + ledger + native /goal handoff + hook loop. Ideas from OMC/GJC ultragoal; no omc CLI.", - "paths": { - "goals": ".grok/ultragoal/goals.json", - "ledger": ".grok/ultragoal/ledger.jsonl", - "active": ".grok/ultragoal/active-goal.live.json", - "prd": ".grok/ultragoal/prd.live.json", - "progress": ".grok/ultragoal/progress.txt", - "brief": ".grok/ultragoal/brief.md" - }, - "modes": { - "aggregate": "One session /goal covers the whole plan objective", - "per_story": "Each story has its own /goal objective" - }, - "final_quality_gate": [ - "independent agent code-review APPROVE", - "Required / CI success on merge head", - "Required / Security success", - "soft reds and blocks cleared or explicitly deferred on lane board" - ], - "integrations": { - "ralplan": "Vague large goals must pass ralplan consensus before ralph/implement fan-out", - "ralph": "Execution loop is prd.live.json story-by-story until all passes + reviewer APPROVE", - "hermes_learn": "After story/plan checkpoint run learn workflow / console-learn", - "native_goal": "Print handoff: user or agent sets Grok /goal ; hooks keep Stop blocked while active", - "program_control": "Hook-driven dispatch: Stop reason instructs /workflow program-control or /workflow ralph" - }, - "forbidden": [ - "Clear PRODUCT HOLDs by implication", - "Silent soft reds", - "Declare ultragoal complete without quality gate evidence in ledger" - ] -} diff --git a/.grok/harness/work-graph.v1.json b/.grok/harness/work-graph.v1.json deleted file mode 100644 index f472d1c8a..000000000 --- a/.grok/harness/work-graph.v1.json +++ /dev/null @@ -1,1153 +0,0 @@ -{ - "version": "1.0.0", - "description": "Console completion work-order graph for multi-agent dispatch. Authority remains docs/current/* only. Success per lane (completion) = the reviewed candidate merges through the protected path with post-merge readback (DELIVERY.md); a PR or stacked commit is implementer handoff, not completion. Tip-serial is a global mutex, not a product phase. Lane packets are populated from lane_packet_fields at dispatch time (operational fields pre_mortem/blast_radius/detection/rollback/stop_conditions/review_identities/head_sha are filled by the dispatcher, not pre-baked in each node).", - "consumer": "console-complete + multi-agent-dispatch", - "live_status_source": "board+beads", - "select_helper": "node tools/ci/ingest-soft-reds.mjs (re-materialize .grok/harness/lane-board.live.json from the shared gh pr list before selection — the per-worktree live board is a derived view, not durable state; board-only/manual status lives in beads, not the live board; on absence or tool failure STOP selection fail-closed) + bd ready + .grok/harness/lane-board.live.json (the console-graph-ready.mjs selector ships in Wave C with its fail-closed allowlist/blocked-lane/beads-status/dependency/verification/parallel-ceiling fixes; until then do not treat it as the selector)", - "transport": "subagents (harness) + bd CLI; .grok/bin/mm-role is workstation-local and NOT custodied in the tree (recorded L∞-PROC gap: custody or rebind before specialist dispatch claims)", - "lane_packet_fields": [ - "lane_id", - "objective", - "phase", - "depends_on", - "allowlist", - "forbidden", - "must_read", - "hold_touch", - "tip_serial", - "verification", - "admit", - "success", - "not_doing", - "hindsight_recall", - "hindsight_retain", - "pre_mortem", - "blast_radius", - "detection", - "rollback", - "stop_conditions", - "review_identities", - "head_sha", - "remaining_holds", - "owner", - "base_sha", - "target", - "mechanical_guide" - ], - "tip_serial_paths": [ - "docs/documentation-manifest.seed.json", - "docs/documentation-index.json", - "docs/program/ledger/**", - "docs/program/executed-tests-baseline.json", - "docs/program/js-test-reachability-baseline.json", - "docs/program/gate-input-exceptions.json", - "docs/program/console-program-ledger.md", - "docs/program/console-capability-registry.json", - "docs/program/console-enterprise-roadmap.md", - "docs/program/console-jurisdiction-register.json", - "docs/CI-GATES.md", - "docs/GO-LIVE-CHECKLIST.md", - "docs/benchmarks/enterprise-parity-matrix.md", - "docs/specs/", - "docs/program/console-fanout-epoch-contract.md", - "docs/program/console-buck2-scale-playbook.md", - "docs/current/", - ".github/workflows/", - ".github/actions/", - ".github/trust/console.allowed_signers", - "scripts/", - ".grok/", - ".claude/workflows/", - "backend/Cargo.toml", - "backend/Cargo.lock", - "backend/.sqlx/", - "package.json", - "package-lock.json", - "tools/buck/", - "**/*.bzl", - "third-party/rust/reindeer/", - "third-party/rust/reindeer.toml", - "**/BUCK", - ".buckconfig", - "tools/ci/", - "backend/ci/gates/", - "registry/", - "backend/**/migrations/", - "backend/**/openapi/", - "**/BUCK", - "**/BUCK", - "**/BUCK", - "**/BUCK", - "security/", - "backend/rust-toolchain.toml", - "backend/deny.toml", - "renovate.json5", - "release-please-config.json", - "ops/", - "deploy/", - ".config/nextest.toml", - ".release-please-manifest.json" - ], - "phases": [ - { - "id": "P0", - "title": "Unblock queue", - "lanes": [ - "L0-611", - "L0-SSF" - ] - }, - { - "id": "P1", - "title": "Substrate R1 residual + R3", - "lanes": [ - "L1-DOC-INDEX", - "L1-PG-PART", - "L1-JS-REACH", - "L1-CARGO-MEM", - "L1-MIG-PARSE", - "L1-BUCK-RES", - "L1-PROC" - ] - }, - { - "id": "P2", - "title": "Architecture foundations R4", - "lanes": [ - "L2-POL", - "L2-APR", - "L2-CAP", - "L2-API" - ] - }, - { - "id": "P3", - "title": "Ontology engine P1/G005", - "lanes": [ - "L3-ONT-CELL", - "L3-SSF-CLOSE" - ] - }, - { - "id": "P4", - "title": "Owning ports H2 prep", - "lanes": [ - "L4-PORT-DES", - "L4-PORT-CO", - "L4-PORT-OU", - "L4-PORT-JP", - "L4-PORT-PE", - "L4-PORT-EM", - "L4-PORT-PR", - "L4-STALE-RECONC", - "L4-CLOSE" - ] - }, - { - "id": "P5", - "title": "Org → HR → Payroll", - "lanes": [ - "L5-CLOSE", - "L5-ORG", - "L5-JOB", - "L5-HR", - "L5-PAY", - "L5-CONT-PAY", - "L5-CONT-EMP", - "L5-CONT-ID", - "L5-CONT-HR", - "L5-KP0" - ] - }, - { - "id": "P6", - "title": "Leptos H1-gated", - "lanes": [ - "L6-ADR", - "L6-SSR", - "L6-UI-ORG", - "L6-UI-HR", - "L6-UI-PAY", - "L6-SEC7-TRACKER" - ] - }, - { - "id": "P∞", - "title": "Always-on non-product", - "lanes": [ - "L∞-PROC", - "L∞-HOLD-PREP", - "L∞-CI-RESID" - ] - } - ], - "nodes": [ - { - "id": "L0-611", - "phase": "P0", - "objective": "Green + merge docs false-authority fence PR #611", - "depends_on": [], - "tip_serial": false, - "hold_touch": "none", - "priority": 0, - "status": "done" - }, - { - "id": "L0-SSF", - "phase": "P0", - "objective": "Close beads epic console-ssf (children 100% complete)", - "depends_on": [], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "ready", - "must_use": [ - "hindsight: prior ONT wave lessons if any" - ] - }, - { - "id": "L1-DOC-INDEX", - "phase": "P1", - "objective": "Manifest seed hygiene only if required after #611 (tip-serial lease)", - "depends_on": [ - "L0-611" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 2, - "status": "blocked" - }, - { - "id": "L1-PG-PART", - "phase": "P1", - "objective": "Partition 183-test PG reachability across isolated DBs + aggregate proof", - "depends_on": [ - "L0-611" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L1-JS-REACH", - "phase": "P1", - "objective": "JS dark-suite reachability ratchet (no orphan .test.mjs)", - "depends_on": [ - "L0-611" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L1-CARGO-MEM", - "phase": "P1", - "objective": "Cargo test membership + feature-bearing reachability inventory", - "depends_on": [ - "L0-611" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L1-MIG-PARSE", - "phase": "P1", - "objective": "Fix migration-parser gaps before admitting new migrations", - "depends_on": [ - "L0-611" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L1-BUCK-RES", - "phase": "P1", - "objective": "Residual Buck map/proof only — no Buck deletion without zero required Buck-only coverage", - "depends_on": [ - "L0-611" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 2, - "status": "ready" - }, - { - "id": "L1-PROC", - "phase": "P1", - "objective": "Process harness: board honesty, mm-role-only specialists, no Grok Sol/Claude models; Hindsight recall before inventing process state; retain after class≥2", - "depends_on": [], - "tip_serial": true, - "hold_touch": "none", - "priority": 1, - "status": "ready", - "allowlist_hint": [ - ".grok/**", - "tools/ci/**" - ], - "must_use": [ - "hindsight recall (if MCP up)", - "bd ready", - "work-graph.v1.json" - ] - }, - { - "id": "L2-POL", - "phase": "P2", - "objective": "Policy/Cedar fail-closed increments (console-a80)", - "depends_on": [ - "L1-DOC-INDEX", - "L1-PG-PART", - "L1-JS-REACH", - "L1-CARGO-MEM", - "L1-MIG-PARSE", - "L1-BUCK-RES", - "L1-PROC" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "blocked", - "beads_epic": "console-a80" - }, - { - "id": "L2-APR", - "phase": "P2", - "objective": "True preflight non-mutation + distinct-human approval (console-66n)", - "depends_on": [ - "L1-DOC-INDEX", - "L1-PG-PART", - "L1-JS-REACH", - "L1-CARGO-MEM", - "L1-MIG-PARSE", - "L1-BUCK-RES", - "L1-PROC" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "blocked", - "beads_epic": "console-66n" - }, - { - "id": "L2-CAP", - "phase": "P2", - "objective": "Branchless capability + temporal-grant contracts", - "depends_on": [ - "L2-POL" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 2, - "status": "blocked" - }, - { - "id": "L2-API", - "phase": "P2", - "objective": "Contracts-crate / OpenAPI composition (trial one face; serial OpenAPI lease)", - "depends_on": [ - "L1-DOC-INDEX", - "L1-PG-PART", - "L1-JS-REACH", - "L1-CARGO-MEM", - "L1-MIG-PARSE", - "L1-BUCK-RES", - "L1-PROC" - ], - "tip_serial": "openapi_face", - "hold_touch": "none", - "priority": 2, - "status": "blocked" - }, - { - "id": "L3-ONT-CELL", - "phase": "P3", - "objective": "Ontology engine path-disjoint cell increments (backend only)", - "depends_on": [ - "L2-POL", - "L2-APR" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 2, - "status": "blocked", - "beads_epic": "console-ssf" - }, - { - "id": "L3-SSF-CLOSE", - "phase": "P3", - "objective": "Epic hygiene after ONT cells; keep console-ssf closed or reopened only for new admission", - "depends_on": [ - "L0-SSF", - "L3-ONT-CELL" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 3, - "status": "ready" - }, - { - "id": "L4-PORT-DES", - "phase": "P4", - "objective": "Serial design: owning ports + forbidden dual writers for projected types", - "depends_on": [ - "L3-ONT-CELL", - "L2-POL", - "L2-APR", - "L2-CAP", - "L2-API" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 1, - "status": "blocked" - }, - { - "id": "L4-PORT-CO", - "phase": "P4", - "objective": "Prove single-writer port: Company", - "depends_on": [ - "L4-PORT-DES" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 2, - "status": "blocked" - }, - { - "id": "L4-PORT-OU", - "phase": "P4", - "objective": "Prove single-writer port: OrgUnit", - "depends_on": [ - "L4-PORT-DES" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 2, - "status": "blocked" - }, - { - "id": "L4-PORT-JP", - "phase": "P4", - "objective": "Prove single-writer port: JobPosition", - "depends_on": [ - "L4-PORT-DES" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 2, - "status": "blocked" - }, - { - "id": "L4-PORT-PE", - "phase": "P4", - "objective": "Prove single-writer port: Person", - "depends_on": [ - "L4-PORT-DES" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 2, - "status": "blocked" - }, - { - "id": "L4-PORT-EM", - "phase": "P4", - "objective": "Prove single-writer port: Employment", - "depends_on": [ - "L4-PORT-DES" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 2, - "status": "blocked" - }, - { - "id": "L4-PORT-PR", - "phase": "P4", - "objective": "Prove single-writer port: PayRun", - "depends_on": [ - "L4-PORT-DES" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H2", - "priority": 2, - "status": "blocked" - }, - { - "id": "L5-ORG", - "phase": "P5", - "objective": "Company/OrgUnit reference after ports proven", - "depends_on": [ - "L4-PORT-CO", - "L4-PORT-OU" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 2, - "status": "blocked" - }, - { - "id": "L5-JOB", - "phase": "P5", - "objective": "JobPosition as first-class object", - "depends_on": [ - "L4-PORT-JP" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 2, - "status": "blocked" - }, - { - "id": "L5-HR", - "phase": "P5", - "objective": "Canonical HR assignment writer (appointment/promotion/transfer)", - "depends_on": [ - "L5-ORG", - "L5-JOB", - "L2-APR", - "L4-PORT-EM", - "L4-PORT-PE" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "blocked" - }, - { - "id": "L5-PAY", - "phase": "P5", - "objective": "PayRun projection from existing payroll truth (no second writer)", - "depends_on": [ - "L5-HR", - "L4-PORT-PR" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "blocked" - }, - { - "id": "L6-ADR", - "phase": "P6", - "objective": "ADR-0030 substrate gate evidence (backend unlocks only until H1)", - "depends_on": [ - "L5-PAY", - "L2-API" - ], - "tip_serial": false, - "hold_touch": "prepare-only:H1", - "priority": 2, - "status": "blocked" - }, - { - "id": "L6-SSR", - "phase": "P6", - "objective": "Contracts-only Leptos SSR shell after H1 clear (gated on hold_touch H1 + L6-SEC7-TRACKER; do not dispatch until the console-8nq HOLD clears)", - "depends_on": [ - "L6-ADR", - "L6-SEC7-TRACKER" - ], - "tip_serial": false, - "hold_touch": "H1", - "priority": 2, - "status": "blocked" - }, - { - "id": "L6-UI-ORG", - "phase": "P6", - "objective": "Org surfaces deny-by-omission", - "depends_on": [ - "L6-SSR", - "L5-ORG" - ], - "tip_serial": false, - "hold_touch": "H1", - "priority": 3, - "status": "blocked" - }, - { - "id": "L6-UI-HR", - "phase": "P6", - "objective": "HR surfaces deny-by-omission", - "depends_on": [ - "L6-SSR", - "L5-HR" - ], - "tip_serial": false, - "hold_touch": "H1", - "priority": 3, - "status": "blocked" - }, - { - "id": "L6-UI-PAY", - "phase": "P6", - "objective": "Payroll surfaces deny-by-omission", - "depends_on": [ - "L6-SSR", - "L5-PAY" - ], - "tip_serial": false, - "hold_touch": "H1", - "priority": 3, - "status": "blocked" - }, - { - "id": "L∞-PROC", - "phase": "P∞", - "objective": "Process upgrades from failure-classes; always-on; Hindsight recall before inventing process state; retain after class≥2", - "depends_on": [], - "tip_serial": true, - "hold_touch": "none", - "priority": 2, - "status": "ready", - "must_use": [ - "hindsight recall (if MCP up)", - "bd ready", - "work-graph.v1.json" - ] - }, - { - "id": "L∞-HOLD-PREP", - "phase": "P∞", - "objective": "Custody inventory for disk wipe HOLD; never OCI A1 mutate; no live prod claims", - "depends_on": [], - "tip_serial": false, - "hold_touch": "prepare-only:H3,H5,H6", - "priority": 3, - "status": "ready" - }, - { - "id": "L4-STALE-RECONC", - "phase": "P4", - "objective": "Reconcile stale p4/canonical-ports-writer-ownership stream (2 unique commits; true uncommitted residue = 6 tracked files + untracked custody candidates) against origin/main by two-dot delta; preserve the checkout as evidence and land only genuine residue (EMPL-RETARGET, CI-DERIVE, holiday-table design) as leaves", - "depends_on": [], - "tip_serial": true, - "hold_touch": "none", - "priority": 0, - "status": "ready" - }, - { - "id": "L5-CONT-PAY", - "phase": "P5", - "objective": "Canonical containment: PayRun conflict arm, payroll drain staging, backdated head (console-3yu, console-ai2, console-r25 — console-3yu/console-ai2 landed via #777, console-r25 via #780)", - "depends_on": [ - "L5-CLOSE" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L5-CONT-EMP", - "phase": "P5", - "objective": "Employment canonical containment: ONBOARD current_status, org-change freeze windows, tenant removal, pure preflight, replay approval (console-31e, console-rte, console-fi8, console-2kd, console-0hf — console-rte landed via #780)", - "depends_on": [ - "L5-CLOSE" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L5-CONT-ID", - "phase": "P5", - "objective": "Identity/leave containment: live leave writer, role-sequence comparison, no-op replay trail, deactivated branch re-parent (console-2v1, console-9sb, console-cg6, console-lx6 — console-9sb/console-cg6/console-lx6 landed via #776)", - "depends_on": [ - "L5-CLOSE" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L5-CONT-HR", - "phase": "P5", - "objective": "HR REST free-text org_unit dual-namespace (console-tfg2); openapi enum peripheral drift pin via conductor (console-e8vn)", - "depends_on": [ - "L5-CLOSE" - ], - "tip_serial": "openapi_face", - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L5-KP0", - "phase": "P5", - "objective": "Public-holiday reference table for payroll (console-kp0, payroll-owned scope only); the collaboration/workbench iljeong projection is out of scope (communications are outside the PRODUCT boundary)", - "depends_on": [ - "L4-CLOSE", - "L1-MIG-PARSE", - "L5-HR" - ], - "tip_serial": true, - "hold_touch": "none", - "priority": 2, - "status": "ready" - }, - { - "id": "L∞-CI-RESID", - "phase": "P∞", - "objective": "CI/contract residuals: console-8x4 conflict decision, console-ss7, console-gwa, console-cae0, console-u4p5, console-37c, console-ry4f/c236; tip-serial, conductor-led", - "depends_on": [], - "tip_serial": true, - "hold_touch": "none", - "priority": 1, - "status": "ready" - }, - { - "id": "L6-SEC7-TRACKER", - "phase": "P6", - "objective": "Track the console-8nq P6 delivery HOLD and custody the ADR-0030 section 7 six-green evidence; prepare-only, no shell", - "depends_on": [], - "tip_serial": false, - "hold_touch": "prepare-only:H1", - "priority": 1, - "status": "ready" - }, - { - "id": "L5-CLOSE", - "phase": "P5", - "objective": "Close epic console-dgo (P5 org→HR→payroll) with merged/readback evidence; gates the canonical-containment lanes", - "depends_on": [ - "L4-CLOSE", - "L1-DOC-INDEX", - "L1-PG-PART", - "L1-JS-REACH", - "L1-CARGO-MEM", - "L1-MIG-PARSE", - "L1-BUCK-RES", - "L1-PROC", - "L2-POL", - "L2-APR", - "L2-CAP", - "L2-API" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 0, - "status": "ready" - }, - { - "id": "L4-CLOSE", - "phase": "P4", - "objective": "Close the P4 epic console-1qw after console-1qw.4 and console-1qw.5 merge, with readback evidence; explicit P4 close gates P5 closure", - "depends_on": [ - "L4-STALE-RECONC" - ], - "tip_serial": false, - "hold_touch": "none", - "priority": 0, - "status": "ready" - } - ], - "edges": [ - { - "from": "L0-611", - "to": "L1-BUCK-RES" - }, - { - "from": "L0-611", - "to": "L1-CARGO-MEM" - }, - { - "from": "L0-611", - "to": "L1-DOC-INDEX" - }, - { - "from": "L0-611", - "to": "L1-JS-REACH" - }, - { - "from": "L0-611", - "to": "L1-MIG-PARSE" - }, - { - "from": "L0-611", - "to": "L1-PG-PART" - }, - { - "from": "L0-SSF", - "to": "L3-SSF-CLOSE" - }, - { - "from": "L1-BUCK-RES", - "to": "L2-API" - }, - { - "from": "L1-BUCK-RES", - "to": "L2-APR" - }, - { - "from": "L1-BUCK-RES", - "to": "L2-POL" - }, - { - "from": "L1-BUCK-RES", - "to": "L5-CLOSE" - }, - { - "from": "L1-CARGO-MEM", - "to": "L2-API" - }, - { - "from": "L1-CARGO-MEM", - "to": "L2-APR" - }, - { - "from": "L1-CARGO-MEM", - "to": "L2-POL" - }, - { - "from": "L1-CARGO-MEM", - "to": "L5-CLOSE" - }, - { - "from": "L1-DOC-INDEX", - "to": "L2-API" - }, - { - "from": "L1-DOC-INDEX", - "to": "L2-APR" - }, - { - "from": "L1-DOC-INDEX", - "to": "L2-POL" - }, - { - "from": "L1-DOC-INDEX", - "to": "L5-CLOSE" - }, - { - "from": "L1-JS-REACH", - "to": "L2-API" - }, - { - "from": "L1-JS-REACH", - "to": "L2-APR" - }, - { - "from": "L1-JS-REACH", - "to": "L2-POL" - }, - { - "from": "L1-JS-REACH", - "to": "L5-CLOSE" - }, - { - "from": "L1-MIG-PARSE", - "to": "L2-API" - }, - { - "from": "L1-MIG-PARSE", - "to": "L2-APR" - }, - { - "from": "L1-MIG-PARSE", - "to": "L2-POL" - }, - { - "from": "L1-MIG-PARSE", - "to": "L5-CLOSE" - }, - { - "from": "L1-MIG-PARSE", - "to": "L5-KP0" - }, - { - "from": "L1-PG-PART", - "to": "L2-API" - }, - { - "from": "L1-PG-PART", - "to": "L2-APR" - }, - { - "from": "L1-PG-PART", - "to": "L2-POL" - }, - { - "from": "L1-PG-PART", - "to": "L5-CLOSE" - }, - { - "from": "L1-PROC", - "to": "L2-API" - }, - { - "from": "L1-PROC", - "to": "L2-APR" - }, - { - "from": "L1-PROC", - "to": "L2-POL" - }, - { - "from": "L1-PROC", - "to": "L5-CLOSE" - }, - { - "from": "L2-API", - "to": "L4-PORT-DES" - }, - { - "from": "L2-API", - "to": "L5-CLOSE" - }, - { - "from": "L2-API", - "to": "L6-ADR" - }, - { - "from": "L2-APR", - "to": "L3-ONT-CELL" - }, - { - "from": "L2-APR", - "to": "L4-PORT-DES" - }, - { - "from": "L2-APR", - "to": "L5-CLOSE" - }, - { - "from": "L2-APR", - "to": "L5-HR" - }, - { - "from": "L2-CAP", - "to": "L4-PORT-DES" - }, - { - "from": "L2-CAP", - "to": "L5-CLOSE" - }, - { - "from": "L2-POL", - "to": "L2-CAP" - }, - { - "from": "L2-POL", - "to": "L3-ONT-CELL" - }, - { - "from": "L2-POL", - "to": "L4-PORT-DES" - }, - { - "from": "L2-POL", - "to": "L5-CLOSE" - }, - { - "from": "L3-ONT-CELL", - "to": "L3-SSF-CLOSE" - }, - { - "from": "L3-ONT-CELL", - "to": "L4-PORT-DES" - }, - { - "from": "L4-CLOSE", - "to": "L5-CLOSE" - }, - { - "from": "L4-CLOSE", - "to": "L5-KP0" - }, - { - "from": "L4-PORT-CO", - "to": "L5-ORG" - }, - { - "from": "L4-PORT-DES", - "to": "L4-PORT-CO" - }, - { - "from": "L4-PORT-DES", - "to": "L4-PORT-EM" - }, - { - "from": "L4-PORT-DES", - "to": "L4-PORT-JP" - }, - { - "from": "L4-PORT-DES", - "to": "L4-PORT-OU" - }, - { - "from": "L4-PORT-DES", - "to": "L4-PORT-PE" - }, - { - "from": "L4-PORT-DES", - "to": "L4-PORT-PR" - }, - { - "from": "L4-PORT-EM", - "to": "L5-HR" - }, - { - "from": "L4-PORT-JP", - "to": "L5-JOB" - }, - { - "from": "L4-PORT-OU", - "to": "L5-ORG" - }, - { - "from": "L4-PORT-PE", - "to": "L5-HR" - }, - { - "from": "L4-PORT-PR", - "to": "L5-PAY" - }, - { - "from": "L4-STALE-RECONC", - "to": "L4-CLOSE" - }, - { - "from": "L5-CLOSE", - "to": "L5-CONT-EMP" - }, - { - "from": "L5-CLOSE", - "to": "L5-CONT-HR" - }, - { - "from": "L5-CLOSE", - "to": "L5-CONT-ID" - }, - { - "from": "L5-CLOSE", - "to": "L5-CONT-PAY" - }, - { - "from": "L5-HR", - "to": "L5-KP0" - }, - { - "from": "L5-HR", - "to": "L5-PAY" - }, - { - "from": "L5-HR", - "to": "L6-UI-HR" - }, - { - "from": "L5-JOB", - "to": "L5-HR" - }, - { - "from": "L5-ORG", - "to": "L5-HR" - }, - { - "from": "L5-ORG", - "to": "L6-UI-ORG" - }, - { - "from": "L5-PAY", - "to": "L6-ADR" - }, - { - "from": "L5-PAY", - "to": "L6-UI-PAY" - }, - { - "from": "L6-ADR", - "to": "L6-SSR" - }, - { - "from": "L6-SEC7-TRACKER", - "to": "L6-SSR" - }, - { - "from": "L6-SSR", - "to": "L6-UI-HR" - }, - { - "from": "L6-SSR", - "to": "L6-UI-ORG" - }, - { - "from": "L6-SSR", - "to": "L6-UI-PAY" - } - ], - "not_doing": [ - "ERP/comms/compliance/ingest/AI product expansion", - "Bulk docs graveyard (ROADMAP HOLD)", - "Multi-tip product PRs", - "Grok agent(model: sol|claude)", - "UI before H1", - "Dual writers for projected types" - ], - "projections": { - "idea_md": "docs/ideas/console-parallel-lanes-work-graph.md (historical quarry, superseded by this executable graph — its 'PR or stacked commit' success line and workstation-local mm-role specialist line are NOT authoritative)", - "beads_graph": "bd CLI (live); the .grok/harness/work-graph.beads.json export is workstation-local and not custodied in the tree (L∞-PROC gap: custody or rebind)", - "lane_board": ".grok/harness/lane-board.live.json" - }, - "memory": { - "hindsight_mcp": { - "config": "~/.grok/config.toml [mcp_servers.hindsight] url=http://localhost:8888/mcp/", - "rule": "Before inventing soft-red/process/fleet state, attempt Hindsight recall (MCP when connected). On fail-open if down, fall back to bd list + lane-board + project memory.", - "retain_when": [ - "phase lane lands or PR merges", - "failure class repeats >=2", - "tip-serial contention or multi-tip thrash", - "admit gate would have caught hosted red" - ], - "retain_fields": [ - "class_id", - "lane_id", - "head_sha", - "control_path", - "evidence_urls", - "not_doing" - ] - }, - "session_search": [ - "hindsight recall", - "bd list", - "lane-board.live.json", - "project memory" - ], - "hermes": ".grok/harness/hermes-learning.v1.json", - "framework_v1": { - "spec": "docs/specs/parallel-development-framework.md (quarry)", - "base_sha": "7fc04e3e4167817f6ebc497cb5b329185c2eed5c", - "cargo_rules": [ - "one conductor-run aggregate error census per wave before fan-out; lanes run targeted single-crate checks only; never cargo inside parallel fix loops", - "one lane = one worktree = one CARGO_TARGET_DIR, jobserver-capped", - "tip-serial via conductor: the canonical top-level tip_serial_paths inventory (single source — docs custody + manifest seed/index, program ledgers/registers incl. console-capability-registry.json + console-enterprise-roadmap.md, docs/current/, .github/workflows/ + actions + trust, scripts/check-ci-preflight.mjs + verify.mjs, backend/Cargo.toml + Cargo.lock + .sqlx/, package.json + package-lock.json, tools/buck/, registry/, migrations, openapi, generated BUCK faces, Reindeer lockfiles, .grok/)", - "hosted CI is evidence, never authority: review, protected merge, and post-merge readback remain the gate" - ], - "git_rules": [ - "lanes only branch/add-commit-specific-files/push; no stash/reset/checkout-hopping; force-with-lease reserved for the integration owner restack (console-drive.rhai)", - "path-disjoint allowlists (<=3 roots), single-parent leaves, base-SHA pinning", - "two-dot delta measurement before any rebase decision", - "one integration owner; no multi-tip product PRs" - ], - "waves": { - "A": "P4 closure: L4-STALE-RECONC (two-dot delta vs origin/main)", - "B": "P5 containment: L5-CONT-PAY/EMP/ID/HR", - "C": "CI residuals: L∞-CI-RESID (serial)", - "D": "holiday reference table (payroll-owned only): L5-KP0", - "E": "P6 prep: L6-SEC7-TRACKER (prepare-only)", - "P∞": "program-tick, babysit PRs, failure-class promotion, worktree reclaim" - }, - "parallel_ceiling": 4 - } - } -} diff --git a/.grok/hooks/ultragoal-loop.json b/.grok/hooks/ultragoal-loop.json deleted file mode 100644 index 40ee38a11..000000000 --- a/.grok/hooks/ultragoal-loop.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 .grok/bin/console-hook-session-start", - "timeout": 10 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 .grok/bin/console-hook-stop", - "timeout": 120 - } - ] - } - ] - } -} diff --git a/.grok/memory/MEMORY.md b/.grok/memory/MEMORY.md deleted file mode 100644 index b5baea8e0..000000000 --- a/.grok/memory/MEMORY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Console agent MEMORY (bounded, Hermes-style) - -Curated process facts only. Prefer promoting durable rules into -`.grok/harness/*` and `.grok/workflows/*` (Bun doctrine). - -## Standing rules - -- Soft reds/blocks must land on `lane-board.live.json` (`ops.soft-red-silence`). -- Tip prebind: C prebinds T-final ledger blob; never naive `generate --write` after tip. -- Autonomy: agent review **APPROVE** + Required CI/Security → merge; fix until APPROVE. -- Ultragoal active → Stop hook loops into `/workflow program-control` or `ralph`. -- Forbidden control-plane CLIs: `omc`, `omx`, `gjc`, `hermes` (ideas only). -- [2026-08-06] class `ops.soft-red-silence`: hermes loop smoke diff --git a/.grok/memory/reflections/2026-08-06.md b/.grok/memory/reflections/2026-08-06.md deleted file mode 100644 index ef73002a7..000000000 --- a/.grok/memory/reflections/2026-08-06.md +++ /dev/null @@ -1,15 +0,0 @@ - -## Event `smoke` (2026-08-06T11:16:15Z) -- summary: hermes loop smoke -- failure_classes: ops.soft-red-silence -- hard_fails: (none) - -### Hermes loop actions -- Persist curated tip if class present. -- Prefer process edit (workflow/harness/tool) when class repeats. -- Soft reds must be on lane-board (ops.soft-red-silence). - -## Reflection — tip-serial efficiency -- Fan-out of four tip-writing micro-PRs burned multi-wall CI for small pure-test gains. -- Fix: tip-serial queue discipline + batch pure domain tests; Admit rustfmt/baseline (#591). -- Dual-track (fleet + product/process) works; tip contention is the real serial choke after dual-track. diff --git a/.grok/memory/tips/ops-soft-red-silence.md b/.grok/memory/tips/ops-soft-red-silence.md deleted file mode 100644 index 87bfc4138..000000000 --- a/.grok/memory/tips/ops-soft-red-silence.md +++ /dev/null @@ -1,13 +0,0 @@ -# Setup Tip — ops.soft-red-silence - -- **event:** `smoke` -- **summary:** hermes loop smoke - -## Rule - -When `ops.soft-red-silence` appears, apply the control in `.grok/harness/failure-classes.v1.json` before expanding product scope. - -## Do not - -- Silent-drop soft reds. -- Call omc/omx/gjc/hermes CLIs as control plane. diff --git a/.grok/memory/tips/rustfmt-and-executed-tests-baseline.md b/.grok/memory/tips/rustfmt-and-executed-tests-baseline.md deleted file mode 100644 index 2e165a560..000000000 --- a/.grok/memory/tips/rustfmt-and-executed-tests-baseline.md +++ /dev/null @@ -1,11 +0,0 @@ -# Setup tip — rustfmt + executed-tests baseline (ops.rustfmt-drift / ops.executed-tests-baseline) - -When changing `backend/**` tests or code before push: - -1. `(cd backend && cargo fmt --all -- --check)` — fix with `cargo fmt --all`. -2. `node scripts/check-executed-tests.mjs` — if it says attributes gained, run - `node scripts/check-executed-tests.mjs --update` and commit - `docs/program/executed-tests-baseline.json` in the **same** PR. -3. Then rebuild authority tip (C/T) if ledger/manifest already staged. - -Wired into `.grok/workflows/domain-increment.rhai` Admit phase. diff --git a/.grok/memory/trajectories/smoke.md b/.grok/memory/trajectories/smoke.md deleted file mode 100644 index 0433cea81..000000000 --- a/.grok/memory/trajectories/smoke.md +++ /dev/null @@ -1,9 +0,0 @@ -# Trajectory `smoke` - -- ts: 2026-08-06T11:16:15Z -- summary: hermes loop smoke -- classes: ['ops.soft-red-silence'] -- hard: [] -- reflection: /Users/jasonlee/Developer/console/.grok/memory/reflections/2026-08-06.md -- tip: /Users/jasonlee/Developer/console/.grok/memory/tips/ops-soft-red-silence.md -- skill: None diff --git a/.grok/programs/BUN-PARALLEL-DISCIPLINE.md b/.grok/programs/BUN-PARALLEL-DISCIPLINE.md deleted file mode 100644 index a0b624ebf..000000000 --- a/.grok/programs/BUN-PARALLEL-DISCIPLINE.md +++ /dev/null @@ -1,126 +0,0 @@ -# Bun rewrite lessons — Console delivery process - -Canonical process rules for multi-PR / multi-lane Console delivery. -**Doctrine source:** [Rewriting Bun in Rust](https://bun.com/blog/bun-in-rust) — prep first, trial before fan-out, dual adversarial review, **edit the process** so agents cannot repeat the mistake. - -Workflows (edit these when fixing process): - -| Workflow | Role | -|----------|------| -| **`console-drive`** | **Primary Bun-style driver** — process tools + fleet repair/merge + **product implement** + process-edit + learn. Edit *this* when the drive fails as a class. | -| `program-control` | Lighter heartbeat (fleet + implement + audit); prefer `console-drive` for full implementation | -| `ultragoal` | Durable multi-story plan + activate Stop-hook loop + native `/goal` handoff | -| `ralplan` | Planner → Architect → Critic consensus before heavy execute | -| `ralph` | PRD story loop until passes + APPROVE (“boulder never stops”) | -| `learn` | Hermes-style reflection / tip / skill draft / process-upgrade promote | -| `work-manager` | Discover lanes; **enqueue every soft red/block** (no silence) | -| `implement-lane` | Claim board/beads item → **implement code** → open PR | -| `pr-babysit` | Repair → agent review → **approve then merge**; fix until approve | -| `domain-increment` | Full backend SDLC + Admit (build is capability `all`) | -| `process-upgrade` | Map a failure class → permanent control | -| `program-tick` | Legacy dual-track (fleet + product/process) | - -**Primary entry:** `/workflow console-drive` — not “track reds only”. - -### Hook-driven loop (Grok-native) - -`.grok/hooks/ultragoal-loop.json` → `SessionStart` + `Stop` run `bin/console-hook-*`. -While `ultragoal/active-goal.live.json.active`, Stop **blocks** and dispatches `/workflow program-control` or `ralph`. -Pair with native `/goal `. Trust project hooks: `/hooks-trust`. - -Authority remains `docs/current/{PRODUCT,ROADMAP,DELIVERY}.md`. This file is **process**, not product. - -## What "fix the process" means - -**Means:** edit a **reusable workflow / harness / tool** so the next agent cannot make the same *class* of mistake without a hard fail, forced step, or explicit waiver. - -| Fix class | Do this | Not this | -|-----------|---------|----------| -| **Workflow** | New phase in `domain-increment` / `open-pr-fleet`; admit gate; tip-sync step | Chat note "remember to run lens check" | -| **Tool / script** | Extend `npm run check:*` / `admit` / residual gate | One-line fix on one PR and stop | -| **Harness catalog** | Add row to `failure-classes.v1.json` + control path | One-shot hook named for a PR number | -| **Memory tip** | `.grok/memory/tips/{class}.md` after repeat ≥2 | Session-only folklore | - -**Does not mean:** expanding product scope, clearing PRODUCT HOLDs, auto-merge to main (Console: **leader-only** merge), or inventing features to avoid a process gate. - -When a mistake class repeats **twice**, promote a process edit (see `learning-loop.v1.json`). - -## Six Bun rules (Console) - -1. **Prep contract first** — admission + OWNERSHIP/allowlist before multi-agent write. -2. **One representative trial before fan-out** — do not open N domain lanes while the trial path is red. -3. **Dual split-context review** — implementer ≠ reviewer context (`domain-increment` Review phase). -4. **Fail closed on missing evidence** — no handoff without admit green; no "complete" without Required CI on exact head when claiming merge-ready. -5. **Edit process when systematic** — if agents keep doing X, change workflow/tool so X fails closed. -6. **Do not expand while trial red** — no second CI graph writer while #ci tip is red; no product fan-out without prep pack. - -## Systematic mistakes → required process edits - -| Class ID | Observed | Control (must live in tool/workflow) | -|----------|----------|--------------------------------------| -| `auth.unsigned-tip` | C/T not pinned SSH after rebase | Signed train checklist in handoff; authority bootstrap | -| `lens.manifest-drift` | Hosted preflight lens fail | `check-reasoning-lens-manifest` in admit when AGENTS.md, CLAUDE.md or README.md touched | -| `docs.tip-blob-prebind` | Manifest ≠ tip blob | `check:doc-manifest` on tip content before push | -| `ci.verify-job-ids` | verify.mjs / preflight digests | `check:ci-preflight` + `test:verify` | -| `ci.residual-buck-growth` | New Buck wrappers in ci.yml | `check:product-buck-residual` | -| `ops.passive-wait` | Watch-only CI turns | `program-tick` / `open-pr-fleet` anti-passive; dual track | -| `ops.mid-run-push` | Push while CI in_progress cancels cone | Fleet rule: one push → wait complete | -| `ops.missed-tip-sync` | PR BEHIND after main moves | `open-pr-fleet` tip-sync report (leader restacks) | -| `ops.skip-admit` | Push without local gates | PreToolUse / admit phase in workflows | -| `ops.tip-serial-contention` | Multiple tip-writing PRs thrashing serial queue | `npm run assess:tip-contention`; tip-serial merge queue | -| `ops.multi-pr-wall-tax` | N small PRs each pay full Required CI wall | Batch pure tests; stack when tip would serialize | - -## Anti-passive rule - -`waiting_ci` is **not** idle. Every autonomous wake must: merge-report · fix · restack/preflight · fan-out ready work · or advance tracker. -Unchanged WAIT narration alone is forbidden (Hindsight `autonomous-drive-no-passive-watch`). - -## Autonomous merge (operator override 2026-08-06) - -Policy file: `.grok/harness/autonomy-merge.v1.json`. - -1. **Agent review** must produce verdict `approve` | `changes_requested` | `comment`. -2. If **not approve** → fix on branch (and/or board `review_fix`) until **approve**, up to max fix rounds; residual becomes board **blocked** with evidence — **never silent**. -3. If **approve** + Required CI green + Required Security green + mergeable/not BEHIND → **`gh pr merge --squash` without human gate**. -4. **Human supervises** and intervenes only if something looks awry, HOLD clearance is required, or production/secrets exposure. -5. Soft reds and blocks are first-class work: see **Soft reds & blocks** below. - -## Soft reds & blocks (no silent drop) - -Harness: `.grok/harness/lane-board.v1.json` + live board `.grok/harness/lane-board.live.json`. - -**Soft reds** include (non-exhaustive): non-required CI fails, cancelled/flaky runs, BEHIND/DIRTY/CONFLICTING, auth bootstrap fail, tip prebind/unsigned, stale waiting_ci, review not approve, tip_serial contention, baseline/manifest drift, beads blocked without owner. - -**Rules:** - -- Every observed soft red or block **must** become a board item (`source_key` dedupe). -- `work-manager` silence_check fails closed if any observation is missing from the board. -- `pr-babysit` / `program-control` productivity audit fails if open PR issues exist with no board coverage. -- Class id: `ops.soft-red-silence` — treating a soft red as "noise" or leaving it unowned is a process defect. - -## Parallelism - -- **Serialize:** authority tip, `ci.yml` / preflight digests, migrations, Cargo.lock, OpenAPI faces, `docs/current/**`. -- **Parallel:** path-disjoint backend crates after prep pack; process-upgrade PRs vs product PRs when paths disjoint. -- Prefer **one tip-writing PR** at a time; batch pure-domain tests rather than N tip PRs. - -## Tip-serial queue (authority + baseline) - -These paths are a **single writer** across the fleet (not just concurrent git conflicts — each PR pays full CI after restack): - -- `docs/documentation-manifest.seed.json` / `docs/documentation-index.json` -- `docs/program/ledger/**` (authority tip train) -- `docs/program/executed-tests-baseline.json` - -**Rules:** - -1. Maintain an explicit **tip-serial queue** ordered by merge readiness. -2. Do **not** open a new tip-writing PR while ≥2 tip writers are already open — stack commits or wait. -3. Pure `#[test]` hardenings across domains: **prefer one PR / stack** when tip would serialize them anyway. -4. `program-tick` must report `tip_writers` and flag `ops.tip-serial-contention`. - -## CI wall tax - -Hosted Required CI wall is ~25–45m. Opening N independent PRs multiplies wall cost. -Fan-out is still correct for **path-disjoint product crates that do not share tip/baseline** — rare under current authority train. Until tip binding is less chatty, **local parallelism** (implement next stack commit while CI runs) beats **remote PR fan-out**. - diff --git a/.grok/programs/ROADMAP-DURABLE-GOAL.md b/.grok/programs/ROADMAP-DURABLE-GOAL.md deleted file mode 100644 index 02e634000..000000000 --- a/.grok/programs/ROADMAP-DURABLE-GOAL.md +++ /dev/null @@ -1,80 +0,0 @@ -# Console backend roadmap — durable goal (operator view) - -**Brief (session paste):** [`briefs/console-backend-roadmap-durable.brief.md`](briefs/console-backend-roadmap-durable.brief.md) -**Process doctrine:** [`BUN-PARALLEL-DISCIPLINE.md`](BUN-PARALLEL-DISCIPLINE.md) -**Authority:** `docs/current/{PRODUCT,ROADMAP,DELIVERY}.md` only. - -## Interview locks (2026-08-06) - -| Knob | Choice | -|------|--------| -| Horizon | **Full backend roadmap** (ROADMAP 1→6 + backend unlocks for 7) | -| HOLDs | Prerequisites → evidence → **stop**; never clear by implication | -| Merge | **Leader-only** squash-merge | -| Closed loop | Fix **process** (workflows/harness/tools), not invent product scope | -| Anti-wait | **Two workflows**: `ci-fleet-tick` + `product-process-tick` (orchestrated by `program-tick`) | - -## Dual-track anti-wait (required) - -```text -Every autonomous wake / program-tick: - ┌─────────────────────┐ ┌──────────────────────────┐ - │ ci-fleet-tick │ │ product-process-tick │ - │ CI/PR classify │ │ product OR process work │ - │ CLEAN for leader │ │ NEVER pure "wait CI" │ - │ fail URLs / behind │ │ domain-increment or │ - │ tip-sync report │ │ process-upgrade │ - └──────────┬──────────┘ └────────────┬─────────────┘ - │ if waiting_ci │ - └──────────── must pair ──────┘ -``` - -**Endless waiting-for-CI** is a **process defect** (class `ops.passive-wait`). -Fix: edit workflows so a tick that only narrates WAIT is incomplete (`program-tick` always runs both legs by default). - -## Workflow catalog (edit these to fix process) - -| Workflow | Purpose | -|----------|---------| -| **program-tick** | Orchestrator: fleet + product/process in one run | -| **ci-fleet-tick** | Fleet half only | -| **product-process-tick** | Product/process half only (pair when fleet waits) | -| **domain-increment** | One backend lane SDLC + **Admit** phase | -| **process-upgrade** | One failure class → permanent control | - -## Goal spine (G001–G009) - -```text -G001 Substrate / docs custody + Required CI health -G002 Cargo-first product tests (DN-0005 sequence; residual Buck cutover) -G003 Process intelligence always-on (this harness; never "done") -G004 Architecture foundations (policy, preflight, distinct-human) -G005 Ontology / object engine backend increments -G006 Org/HR owning ports + single-writer (HOLD-gated fan-out) -G007 Payroll projection (no second writer) -G008 Backend unlocks for Leptos HOLD (contracts/gates — not UI claim) -G009 Human-blocked ops (prod/secrets/erase — prepare only) -``` - -## Activate (session start) - -```bash -cd ~/Developer/console -bd prime -bd ready -# Trust project hooks once: /hooks-trust -# Durable ultragoal (optional): -# /workflow ultragoal {"action":"activate","objective":"…"} -# /goal -# Every wake: -# /workflow program-control -# Vague large work first: -# /workflow ralplan {"task":"…"} -# /workflow ralph -# After ticks: -# /workflow learn -``` - -## Durable goal prompt (copy-paste) - -See brief § Shared constraints + § Session prompt. diff --git a/.grok/programs/assessment-2026-08-06-efficiency.md b/.grok/programs/assessment-2026-08-06-efficiency.md deleted file mode 100644 index 7f5c57408..000000000 --- a/.grok/programs/assessment-2026-08-06-efficiency.md +++ /dev/null @@ -1,36 +0,0 @@ -# Continuous efficiency assessment — 2026-08-06 - -## Measured inefficiencies (this train) - -| Inefficiency | Evidence | Cost | Process fix | -|--------------|----------|------|-------------| -| **Tip-serial bottleneck** | PRs #589–#592 all touch `documentation-manifest` + ledger tip | Only one can merge at a time; others BEHIND + full CI re-run | Batch pure-domain test increments **or** stack; never fan-out N tip writers | -| **Multi-PR CI wall tax** | ~4 open PRs × ~25–45m wall each | Hours of runner time for ~100 LOC of pure tests | Prefer **one PR per wave** for pure `#[test]` hardenings across disjoint crates when tip would serialize anyway | -| **Baseline file collision** | `executed-tests-baseline.json` shared by ONT/POL/APR | Restack thrash | Admit requires baseline update in same PR; when stacking, re-run `--update` once at tip | -| **Late admit gates** | rustfmt + baseline only after hosted red | 1–2 full CI cycles wasted per PR | domain-increment Admit (+ #591) | -| **Passive wait risk** | Dual-track workflows exist but multi-PR babysit still long | Agent idle if only watching | program-tick forces product/process leg; this assessment is the process track | - -## Parallel tracks that *are* free (when tip queue non-empty) - -| Track | Condition | Example | -|-------|-----------|---------| -| **Read-only** | Always | A-AUD #571, ROADMAP evidence inventory | -| **Process allowlist** | Disjoint from open tip PRs' `.grok` if none open | Admit/workflow edits — **if** no other process PR open | -| **Product crate pure tests** | Only if **not** adding ledger tip this PR (impossible today — authority requires tip) | Practically: **stack** onto one branch | -| **Local dual-clock** | Always | Implement next stack commit while prior PR CI runs | - -## Improvement loop (operating rule) - -Every `program-tick` / autonomous wake: - -1. **Measure** open PRs: tip writers vs path-disjoint; CI wall still running; fails by failure-class. -2. **Act fleet**: merge CLEAN head of tip-serial queue only; restack others. -3. **Act product/process**: if waiting_ci, do **not** open another tip-writing PR unless batching into the same branch. -4. **Retro**: if a class repeated ≥2, process-upgrade (already cataloged). -5. **Record** one line in `.grok/memory/reflections/{date}.md` when a new inefficiency class appears. - -## Decision for Wave 0 remaining pure tests - -**Default:** one stack branch `feat/w0-domain-test-hardening` with ONT+POL+APR commits **or** merge train 589→590→591→592 with no *new* tip PRs until drain. - -Opening a fifth tip-writing PR while four are open is **ops.tip-serial-contention**. diff --git a/.grok/programs/briefs/console-backend-roadmap-durable.brief.md b/.grok/programs/briefs/console-backend-roadmap-durable.brief.md deleted file mode 100644 index 992629052..000000000 --- a/.grok/programs/briefs/console-backend-roadmap-durable.brief.md +++ /dev/null @@ -1,123 +0,0 @@ -# Durable goal brief — Console backend roadmap (closed-loop) - -**Artifact class:** durable multi-session goal for Grok workflows + Beads -**Not merge authority.** Leader squash-merges when CLEAN. -**Orchestration:** `.grok/workflows/*` only for drive loops. -**Doctrine:** [Bun rewrite](https://bun.com/blog/bun-in-rust) — edit reusable workflows/tools so mistakes cannot recur; trial before fan-out; dual-track while CI runs. - ---- - -## Shared constraints (every goal inherits) - -### A. Mission - -Implement the **Console backend product roadmap** end-to-end under live `docs/current/PRODUCT.md` + `ROADMAP.md` + `DELIVERY.md`: substrate → architecture foundations → org/HR ports → payroll projection → backend unlocks for frontend HOLD — via small, path-disjoint, merge-safe increments. Not a single-session tasklist. - -### B. Authority (fail-closed) - -1. PRODUCT / ROADMAP / DELIVERY are sole active product law. -2. Historical plans, chats, and session todos are queue signals only. -3. HOLDs: implement prerequisites + evidence; **stop**; never clear by implication. -4. Out of product scope: ERP expansion, comms verticals as product claims, live prod/DNS/TLS, Korea compliance conclusions, frontend UI until HOLD clears. - -### C. Dual-track anti-wait (two workflows) - -**Problem:** agents fall into endless "still waiting for CI" loops. - -**Rule:** every autonomous wake runs **both**: - -| Workflow | Owns | -|----------|------| -| `ci-fleet-tick` | Open PRs, exact-head CI/Security, CLEAN list for leader, fail URLs, BEHIND tip-sync needs | -| `product-process-tick` | Backend lane or process-upgrade work that does **not** require waiting on that CI | - -Orchestrator: **`program-tick`** runs both legs. Completing only fleet with `waiting_ci` and no product/process action is **ops.passive-wait** (fail closed). - -### D. Merge autonomy - -- Agents: open PR, babysit, report CLEAN, restack when BEHIND. -- **Leader only:** `gh pr merge --squash`. -- No mid-run push thrash while Required CI in_progress. - -### E. Process closed loop (not product) - -On hosted/local red: - -1. Map to `.grok/harness/failure-classes.v1.json` class. -2. If control missing or class repeats ≥2 → **`process-upgrade`** (allowlist: workflows/harness/tools/ci/scripts). -3. Forbidden: one-shot hooks named for a PR; expanding product scope to "work around" process pain. - -### F. Parallelism - -- Serialize: authority tip, ci.yml/preflight, migrations, Cargo.lock, OpenAPI faces, docs/current. -- Parallel: path-disjoint backend crates after prep pack; process-upgrade vs product when paths disjoint. -- Trial before multi-lane fan-out. - -### G. Delivery mechanics (every implement slice) - -1. Worktree from current `origin/main`. -2. Admission + allowlist. -3. `domain-increment` including **Admit** phase. -4. Dual review on high risk. -5. Signed C/T when authority/docs tip requires. -6. One push → wait complete run. -7. `program-tick` while waiting. -8. Leader merge → tip-sync remaining PRs. - ---- - -## Goal graph - -@goal: G001 Substrate and Required CI health -Keep Required / CI + Security meaningful; preflight fail-closed; authority train healthy; docs custody intact. Drain open process/substrate PRs with dual-track ticks. - -@goal: G002 Cargo-first product tests (DN-0005) -Equivalence residual gate → nextest serial groups → switch residual Buck product jobs → drop Buck product CI steps (files retained). Faces carved out. No Buck-as-driver-until-RE. - -@goal: G003 Process intelligence (always-on) -Maintain failure-classes, learning-loop, dual-track workflows; land process-upgrade PRs on repeat classes. Never "done." - -@goal: G004 Architecture foundations -Branchless capability / temporal grants / true preflight / distinct-human approval as PRODUCT item 4 — backend only. - -@goal: G005 Ontology backend increments -Wave ontology/foundry backend under allowlist; no UI. - -@goal: G006 Org/HR ports and single-writer -Owning ports + proofs; projection fan-out remains HOLD until conditions met. - -@goal: G007 Payroll projection -Project PayRun from existing writer; no second write path. - -@goal: G008 Backend unlocks for frontend HOLD -Contracts/gates/SSR prerequisites only; do not claim Leptos UI complete under HOLD. - -@goal: G009 Human-blocked ops -Prod/secrets/erase: prepare evidence only; human_blocked. - ---- - -## Session prompt (paste each wake) - -```text -You are driving Console durable goal: full backend roadmap under PRODUCT/ROADMAP/DELIVERY. - -HARD RULES -- Leader-only merge. You open/babysit/report CLEAN. -- HOLDs fail-closed; never clear by implication. -- Dual-track every wake: run /workflow program-tick (ci-fleet-tick + product-process-tick). - If CI is waiting, you MUST still advance product or process work. Pure WAIT is forbidden. -- Fix process via reusable workflows/harness/tools (process-upgrade), not one-shot hooks. -- One writer per root; serialize tip/ci.yml/migrations/lockfile. -- Authority: docs/current only for product law. - -START -1) bd prime; bd ready -2) /workflow program-tick -3) If clean_merge_ready → report for leader merge -4) If waiting_ci → execute product-process dispatch (domain-increment or process-upgrade) -5) If failed → map failure-classes.v1.json → fix + process-upgrade if class control missing - -Doctrine: .grok/programs/BUN-PARALLEL-DISCIPLINE.md -Brief: .grok/programs/briefs/console-backend-roadmap-durable.brief.md -``` diff --git a/.grok/ultragoal/brief.md b/.grok/ultragoal/brief.md deleted file mode 100644 index 4149eaa9e..000000000 --- a/.grok/ultragoal/brief.md +++ /dev/null @@ -1,18 +0,0 @@ -# Console ultragoal brief (template) - -Replace with the durable initiative brief, then: - -```text -/workflow ultragoal {"action":"create-goals","brief_path":".grok/ultragoal/brief.md"} -/goal -/workflow program-control -``` - -## Constraints (always) - -- Authority: `docs/current/{PRODUCT,ROADMAP,DELIVERY}.md` only -- HOLDs fail-closed -- Soft reds/blocks always on lane board -- Autonomy: agent review APPROVE + Required CI/Security → merge -- Process defects → edit workflows/harness/tools (Bun + Hermes learn) -- Forbidden CLIs: omc, omx, gjc, hermes (ideas only) diff --git a/.grok/ultragoal/goals.json b/.grok/ultragoal/goals.json deleted file mode 100644 index 9d0cc224f..000000000 --- a/.grok/ultragoal/goals.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": 1, - "createdAt": null, - "updatedAt": null, - "briefPath": ".grok/ultragoal/brief.md", - "goalsPath": ".grok/ultragoal/goals.json", - "ledgerPath": ".grok/ultragoal/ledger.jsonl", - "claudeGoalMode": "aggregate", - "grokGoalMode": "aggregate", - "activeGoalId": null, - "goals": [], - "notes": "Empty plan. Inject via /workflow ultragoal or console-goal create-goals." -} diff --git a/.grok/ultragoal/ledger.jsonl b/.grok/ultragoal/ledger.jsonl deleted file mode 100644 index 33f221bfc..000000000 --- a/.grok/ultragoal/ledger.jsonl +++ /dev/null @@ -1,3 +0,0 @@ -{"ts":"2026-08-06T11:16:15Z","event":"activate","objective":"smoke ultragoal","story_id":null,"workflow":"program-control"} -{"ts":"2026-08-06T11:16:15Z","event":"deactivate","reason":"smoke"} -{"ts":"2026-08-06T11:21:14Z","event":"activate","objective":"Drain tip-serial #591-593; land process soft-red tools + Wave0 domain batch via console-drive","story_id":null,"workflow":"console-drive"} diff --git a/.grok/ultragoal/prd.live.json b/.grok/ultragoal/prd.live.json deleted file mode 100644 index 27a190d52..000000000 --- a/.grok/ultragoal/prd.live.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 1, - "title": null, - "source": "ralph", - "stories": [], - "notes": "Ralph-style PRD. Stories need task-specific acceptanceCriteria (not generic boilerplate)." -} diff --git a/.grok/ultragoal/progress.txt b/.grok/ultragoal/progress.txt deleted file mode 100644 index e086a4e48..000000000 --- a/.grok/ultragoal/progress.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Ralph / ultragoal progress - -Patterns and learnings (append-only during execution): - diff --git a/.grok/workflows/ci-fleet-tick.rhai b/.grok/workflows/ci-fleet-tick.rhai deleted file mode 100644 index 48da17c55..000000000 --- a/.grok/workflows/ci-fleet-tick.rhai +++ /dev/null @@ -1,121 +0,0 @@ -// CI fleet tick — one half of the anti-wait pair. -// NEVER treat waiting_ci as a terminal idle state. -// Pair with: product-process-tick (must run when this reports waiting_ci). -// Leader-only merge. Process edits go through process-upgrade, not here. -// Doctrine: .grok/programs/BUN-PARALLEL-DISCIPLINE.md - -let meta = #{ - name: "ci-fleet-tick", - description: "Fleet half of dual-track drive: PR/CI status, tip-sync needs, CLEAN report — never end as pure WAIT", - when_to_use: "Every autonomous wake while open PRs exist; always pair with product-process-tick if any PR is waiting_ci", - phases: [ - #{ title: "Inspect", detail: "open PRs and exact-head CI/Security" }, - #{ title: "Classify", detail: "CLEAN / waiting_ci / failed / behind / blocked" }, - #{ title: "Act", detail: "report merge-ready, fail URLs, tip-sync list; arm next action" }, - ], -}; - -let fleet_schema = #{ - "type": "object", - "required": [ - "open_prs", - "clean_merge_ready", - "waiting_ci", - "failed", - "behind", - "blocked", - "tip_sync_needed", - "next_fleet_action", - "must_run_product_process_tick", - "anti_passive_ok", - ], - "properties": #{ - "open_prs": #{ "type": "array", "items": #{ "type": "string" } }, - "clean_merge_ready": #{ "type": "array", "items": #{ "type": "string" } }, - "waiting_ci": #{ "type": "array", "items": #{ "type": "string" } }, - "failed": #{ "type": "array", "items": #{ "type": "string" } }, - "behind": #{ "type": "array", "items": #{ "type": "string" } }, - "blocked": #{ "type": "array", "items": #{ "type": "string" } }, - "tip_sync_needed": #{ "type": "array", "items": #{ "type": "string" } }, - "next_fleet_action": #{ "type": "string" }, - "must_run_product_process_tick": #{ "type": "boolean" }, - "anti_passive_ok": #{ "type": "boolean" }, - "notes": #{ "type": "string" }, - }, -}; - -phase("Inspect"); -let inspect = agent( - "Console CI fleet inspect (read tools + gh). " - + "Run: gh pr list --state open --limit 25 --json number,title,headRefName,mergeStateStatus,headRefOid,url. " - + "For each OPEN PR that looks substrate/CI/docs (or all open if few), get CI and Security workflow conclusions for exact headRefOid " - + "(gh run list --branch HEAD_REF --limit 8 --json name,status,conclusion,headSha). " - + "Do not merge. Do not edit files. Do not claim success without exact head SHA match. " - + "Return plain text: table of PR | sha9 | mergeState | CI | Security | url.", - #{ - label: "fleet-inspect", - capability_mode: "execute", - }, -); -if inspect == () || !inspect.success { - complete(#{ ok: false, stage: "inspect", error: "fleet inspect failed" }); -} - -phase("Classify"); -let classify_prompt = ""; -classify_prompt += "Using this fleet inspect output, classify every open PR into buckets.\n\n"; -classify_prompt += json_encode(inspect.output); -classify_prompt += "\n\nRules:\n"; -classify_prompt += "- clean_merge_ready: CI success + Security success + MERGEABLE + not BEHIND (leader merges; do not merge yourself).\n"; -classify_prompt += "- waiting_ci: CI or Security in_progress/queued/pending on exact head.\n"; -classify_prompt += "- failed: CI or Security failure on exact head (include run URL in notes).\n"; -classify_prompt += "- behind: mergeStateStatus BEHIND (tip-sync/restack needed).\n"; -classify_prompt += "- blocked: other blockers (draft, conflicts, auth bootstrap fail, etc.).\n"; -classify_prompt += "- tip_sync_needed: BEHIND or base main moved under open head.\n"; -classify_prompt += "- next_fleet_action: ONE concrete next action for fleet (e.g. 'leader merge #585', 'diagnose fail #N url', 'restack #N onto main', 're-poll in 3m after arming monitor').\n"; -classify_prompt += "- must_run_product_process_tick: TRUE if waiting_ci is non-empty OR clean_merge_ready is empty and failed is empty — i.e. CI wait must not idle product/process track.\n"; -classify_prompt += "- anti_passive_ok: TRUE only if next_fleet_action is non-empty AND (failed/behind/clean have actions OR must_run_product_process_tick is true with explicit pairing note).\n"; -classify_prompt += "Forbidden: next_fleet_action = 'wait' or 'still running' alone. That is class ops.passive-wait.\n"; -let classified = agent(classify_prompt, #{ - label: "fleet-classify", - capability_mode: "read-only", - output_schema: fleet_schema, -}); -if classified == () || !classified.success || classified.output == () { - complete(#{ ok: false, stage: "classify", error: "classify failed", inspect: inspect.output }); -} - -phase("Act"); -let act_prompt = ""; -act_prompt += "Produce a short operator-facing fleet report from this classification.\n"; -act_prompt += json_encode(classified.output); -act_prompt += "\n\nHard rules:\n"; -act_prompt += "1) If clean_merge_ready non-empty: list exact PRs for LEADER squash-merge (agents do not merge).\n"; -act_prompt += "2) If failed non-empty: list fail URLs and map to a failure class id from .grok/harness/failure-classes.v1.json if possible.\n"; -act_prompt += "3) If behind non-empty: tip-sync list (restack in PR worktree, force-with-lease only after signed tip if authority).\n"; -act_prompt += "4) If waiting_ci non-empty: you MUST state: run workflow product-process-tick now (do not end session as WAIT).\n"; -act_prompt += "5) Never recommend mid-run push thrash (ops.mid-run-push).\n"; -act_prompt += "Plain text only.\n"; -let report = agent(act_prompt, #{ - label: "fleet-report", - capability_mode: "read-only", -}); - -let must_pair = false; -if classified.output.must_run_product_process_tick == true { - must_pair = true; -} -if classified.output.waiting_ci != () { - // schema array — if present and we could not read bool, still pair when waiting listed - must_pair = true; -} - -complete(#{ - ok: classified.output.anti_passive_ok == true, - pair_workflow: "product-process-tick", - must_run_product_process_tick: must_pair, - fleet: classified.output, - report: if report != () && report.success { report.output } else { () }, - inspect: inspect.output, - doctrine: "BUN-PARALLEL-DISCIPLINE dual-track: ci-fleet-tick + product-process-tick", -}); diff --git a/.grok/workflows/console-drive.rhai b/.grok/workflows/console-drive.rhai deleted file mode 100644 index c130bd685..000000000 --- a/.grok/workflows/console-drive.rhai +++ /dev/null @@ -1,283 +0,0 @@ -// console-drive — Bun-doctrine autonomous implementation control plane. -// https://bun.com/blog/bun-in-rust -// -// This workflow DRIVES implementation. It is not a ticket board. -// Failures of process must be fixed by editing THIS file / harness / tools — -// not by chat "remember to…". Soft reds are inputs to fix loops, not the product. -// -// Durable phases: -// 1. ProcessHealth — mechanical ingest + tip contention (tools) -// 2. FleetRepair — restack/fix/review/merge open PRs (implementation of fixes) -// 3. ProductBuild — claim path-disjoint product work; code + tests + PR -// 4. ProcessEdit — if a failure class hit ≥1 this run, patch workflow/tool/harness -// 5. LearnProof — Hermes learn + ledger evidence that drive did real work -// -// Forbidden: complete() after only narration; skip ProductBuild without tip_serial exclusive reason; -// silent soft reds; PRODUCT HOLD clearance. - -let meta = #{ - name: "console-drive", - description: "Bun-style durable drive: process tools + fleet repair/merge + product implement + process-edit + learn — not board-only", - when_to_use: "Primary autonomous implementation driver every wake; edit this workflow when the drive class fails", - phases: [ - #{ title: "ProcessHealth", detail: "mechanical soft-red ingest + tip assess" }, - #{ title: "FleetRepair", detail: "restack fix review merge open PRs" }, - #{ title: "ProductBuild", detail: "implement next product/process allowlist work" }, - #{ title: "ProcessEdit", detail: "edit reusable workflow/tool if class failed" }, - #{ title: "LearnProof", detail: "learn + proof of non-idle drive" }, - ], -}; - -let health_schema = #{ - "type": "object", - "required": ["soft_red_ingest_ok", "tip_writers", "open_prs", "fleet_targets", "product_capacity"], - "properties": #{ - "soft_red_ingest_ok": #{ "type": "boolean" }, - "tip_writers": #{ "type": "number" }, - "open_prs": #{ "type": "array", "items": #{ "type": "string" } }, - "fleet_targets": #{ "type": "array", "items": #{ "type": "string" } }, - "product_capacity": #{ "type": "boolean" }, - "notes": #{ "type": "string" }, - }, -}; - -let fleet_schema = #{ - "type": "object", - "required": ["actions", "merged", "still_open", "classes_hit"], - "properties": #{ - "actions": #{ "type": "array", "items": #{ "type": "string" } }, - "merged": #{ "type": "array", "items": #{ "type": "string" } }, - "still_open": #{ "type": "array", "items": #{ "type": "string" } }, - "classes_hit": #{ "type": "array", "items": #{ "type": "string" } }, - "evidence": #{ "type": "string" }, - }, -}; - -let product_schema = #{ - "type": "object", - "required": ["did_implement", "summary", "paths_touched", "verification", "pr_or_local"], - "properties": #{ - "did_implement": #{ "type": "boolean" }, - "summary": #{ "type": "string" }, - "paths_touched": #{ "type": "array", "items": #{ "type": "string" } }, - "verification": #{ "type": "string" }, - "pr_or_local": #{ "type": "string" }, - "skip_reason": #{ "type": "string" }, - "classes_hit": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -let process_schema = #{ - "type": "object", - "required": ["edited_process", "files", "class_ids", "summary"], - "properties": #{ - "edited_process": #{ "type": "boolean" }, - "files": #{ "type": "array", "items": #{ "type": "string" } }, - "class_ids": #{ "type": "array", "items": #{ "type": "string" } }, - "summary": #{ "type": "string" }, - }, -}; - -let proof_schema = #{ - "type": "object", - "required": ["productive", "implementation_happened", "silence_check_ok", "summary", "next_drive"], - "properties": #{ - "productive": #{ "type": "boolean" }, - "implementation_happened": #{ "type": "boolean" }, - "silence_check_ok": #{ "type": "boolean" }, - "summary": #{ "type": "string" }, - "next_drive": #{ "type": "string" }, - "defects": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -let skip_product = false; -if args != () && args.skip_product == true { - skip_product = true; -} -let allow_merge = true; -if args != () && args.allow_merge == false { - allow_merge = false; -} - -// --- 1 ProcessHealth (mechanical tools preferred) --- -phase("ProcessHealth"); -let health_prompt = ""; -health_prompt += "You are Console-drive ProcessHealth. This phase is MECHANICAL + brief report.\n"; -health_prompt += "MUST run (use shell):\n"; -health_prompt += "1) `node tools/ci/ingest-soft-reds.mjs` then `node tools/ci/ingest-soft-reds.mjs --check`\n"; -health_prompt += "2) `gh pr list --state open --limit 30 --json number,title,mergeable,mergeStateStatus,headRefOid`\n"; -health_prompt += "3) If present: `node tools/ci/assess-tip-contention.mjs` or count tip-touching open PRs (manifest/baseline/ledger tip)\n"; -health_prompt += "4) `bd ready | head -20`\n"; -health_prompt += "5) Read .grok/ultragoal/active-goal.live.json\n"; -health_prompt += "product_capacity=true if tip_writers<=1 OR there is path-disjoint non-tip work (pure domain tests batch, process allowlist).\n"; -health_prompt += "fleet_targets = PR numbers needing restack/fix/merge in tip-serial order.\n"; -health_prompt += "soft_red_ingest_ok=false if --check fails.\n"; -health_prompt += "Do not implement product code here.\n"; -let health = agent(health_prompt, #{ - label: "drive-health", - capability_mode: "all", - output_schema: health_schema, -}); -if health == () || !health.success { - complete(#{ ok: false, stage: "process-health", error: "health failed — edit console-drive.rhai ProcessHealth if agents skip tools" }); -} -if health.output.soft_red_ingest_ok != true { - complete(#{ - ok: false, - stage: "process-health", - error: "ops.soft-red-silence — ingest/check failed; fix tools/ci/ingest-soft-reds.mjs or board", - health: health.output, - }); -} - -// --- 2 FleetRepair (implementation of fixes) --- -phase("FleetRepair"); -let fleet_prompt = ""; -fleet_prompt += "You are Console-drive FleetRepair. IMPLEMENT fixes — do not only report.\n"; -fleet_prompt += "Health:\n"; -fleet_prompt += json_encode(health.output); -fleet_prompt += "\nFor each fleet_target in tip-serial order (tip writers first):\n"; -fleet_prompt += "1) Restack onto origin/main with SSH-signed C/T; tip prebind (C prebinds T-final ledger blob).\n"; -fleet_prompt += "2) Fix CI/auth/preflight reds on the branch; push --force-with-lease.\n"; -fleet_prompt += "3) Independent mini-review: if approve-worthy and Required CI+Security green and mergeable:\n"; -if allow_merge { - fleet_prompt += " `gh pr merge N --squash` (autonomy-merge policy).\n"; -} else { - fleet_prompt += " report CLEAN only (allow_merge=false).\n"; -} -fleet_prompt += "4) Record failure classes hit (auth.unsigned-tip, docs.tip-blob-prebind, ops.missed-tip-sync, …).\n"; -fleet_prompt += "If no open PRs, actions=['none'] merged=[] still_open=[].\n"; -fleet_prompt += "Evidence must include commands run and outcomes — not vibes.\n"; -let fleet = agent(fleet_prompt, #{ - label: "drive-fleet", - capability_mode: "all", - output_schema: fleet_schema, -}); -if fleet == () || !fleet.success { - complete(#{ ok: false, stage: "fleet-repair", error: "fleet repair failed", health: health.output }); -} - -// --- 3 ProductBuild (real implementation) --- -let product = #{ - did_implement: false, - summary: "skipped", - paths_touched: [], - verification: "", - pr_or_local: "none", - skip_reason: "", - classes_hit: [], -}; -if !skip_product { - phase("ProductBuild"); - let prod_prompt = ""; - prod_prompt += "You are Console-drive ProductBuild. You MUST produce code or an explicit capacity skip.\n"; - prod_prompt += "Health:\n"; - prod_prompt += json_encode(health.output); - prod_prompt += "\nFleet:\n"; - prod_prompt += json_encode(fleet.output); - prod_prompt += "\nRules (Bun prep → trial → fan-out):\n"; - prod_prompt += "- If tip_writers>=2: only implement process fixes for the tip head OR pure local dual-clock work WITHOUT opening a new tip PR.\n"; - prod_prompt += "- Else implement ONE of:\n"; - prod_prompt += " A) Path-disjoint pure domain unit tests (prefer sparse crates: inspection already done; benefit/dispatch/finance-gl/logistics).\n"; - prod_prompt += " B) Process allowlist hardening already scoped (admit gates, assess tools) as a single tip PR after fleet drain.\n"; - prod_prompt += " C) Residual Buck company-conformance cargo cutover ONLY if no other ci.yml writer open.\n"; - prod_prompt += "- Read PRODUCT HOLDs; never clear HOLDs.\n"; - prod_prompt += "- Worktree; rustfmt; cargo test -p --lib; signed commits; open PR if tip capacity allows.\n"; - prod_prompt += "- Authority tip C/T when required; prebind tip blob correctly.\n"; - prod_prompt += "did_implement=true only if files changed with verification evidence.\n"; - prod_prompt += "skip_reason only if fleet still owns exclusive tip serial AND no local dual-clock possible — justify.\n"; - let prod = agent(prod_prompt, #{ - label: "drive-product", - capability_mode: "all", - isolation_worktree: true, - output_schema: product_schema, - }); - if prod == () || !prod.success { - complete(#{ ok: false, stage: "product-build", error: "product build agent failed", fleet: fleet.output }); - } - product = prod.output; - // Fail closed: idle product without capacity justification is process defect - if product.did_implement != true { - let cap = health.output.product_capacity; - if cap == true && (product.skip_reason == () || product.skip_reason == "") { - complete(#{ - ok: false, - stage: "product-build", - error: "ops.passive-wait: product_capacity true but did_implement false without skip_reason — edit console-drive ProductBuild prompts/gates", - product: product, - }); - } - } -} - -// --- 4 ProcessEdit (Bun: fix the process, not the anecdote) --- -phase("ProcessEdit"); -let pe_prompt = ""; -pe_prompt += "You are Console-drive ProcessEdit. Bun doctrine: edit reusable workflow/harness/tool.\n"; -pe_prompt += "Classes from fleet:\n"; -pe_prompt += json_encode(fleet.output.classes_hit); -pe_prompt += "\nProduct classes:\n"; -pe_prompt += json_encode(product.classes_hit); -pe_prompt += "\nIf any class_id present OR fleet still has auth/prebind/behind issues:\n"; -pe_prompt += "1) Map to failure-classes.v1.json\n"; -pe_prompt += "2) Patch the CONTROL: prefer editing .grok/workflows/console-drive.rhai, pr-babysit.rhai, tools/ci/*, harness/*\n"; -pe_prompt += "3) Do NOT leave only a chat note. If no code change needed because control already exists, edited_process=false with summary citing control path.\n"; -pe_prompt += "If no classes and fleet clean: edited_process=false, class_ids=[], summary='no process edit needed'.\n"; -pe_prompt += "Allowlist: .grok/**, tools/ci/**, package.json scripts only.\n"; -let process_edit = agent(pe_prompt, #{ - label: "drive-process-edit", - capability_mode: "all", - output_schema: process_schema, -}); -if process_edit == () || !process_edit.success { - complete(#{ ok: false, stage: "process-edit", error: "process edit failed", product: product, fleet: fleet.output }); -} - -// --- 5 LearnProof --- -phase("LearnProof"); -let learn_prompt = ""; -learn_prompt += "Prove this drive was productive and non-silent.\n"; -learn_prompt += "Run: `node tools/ci/ingest-soft-reds.mjs --check`\n"; -learn_prompt += "Run: `.grok/bin/console-learn from-event --id console-drive --summary '…' --classes '…'`\n"; -learn_prompt += "implementation_happened = fleet merged any OR product.did_implement OR process_edit.edited_process\n"; -learn_prompt += "productive = implementation_happened OR (fleet actions non-empty with concrete restack/push)\n"; -learn_prompt += "If only status narration: productive=false (ops.passive-wait).\n"; -learn_prompt += "Fleet:\n"; -learn_prompt += json_encode(fleet.output); -learn_prompt += "\nProduct:\n"; -learn_prompt += json_encode(product); -learn_prompt += "\nProcessEdit:\n"; -learn_prompt += json_encode(process_edit.output); -learn_prompt += "\nAppend ultragoal ledger via console-goal ledger-append if goal active.\n"; -learn_prompt += "next_drive must be concrete `/workflow console-drive` or specific follow-up.\n"; -let proof = agent(learn_prompt, #{ - label: "drive-proof", - capability_mode: "all", - output_schema: proof_schema, -}); -if proof == () || !proof.success { - complete(#{ ok: false, stage: "learn-proof", error: "proof failed" }); -} -if proof.output.productive != true || proof.output.silence_check_ok != true { - complete(#{ - ok: false, - stage: "learn-proof", - error: "drive unproductive or soft-red silence — fix process (console-drive.rhai / ingest tool)", - proof: proof.output, - fleet: fleet.output, - product: product, - process_edit: process_edit.output, - }); -} - -complete(#{ - ok: true, - workflow: "console-drive", - doctrine: "bun-edit-the-process", - health: health.output, - fleet: fleet.output, - product: product, - process_edit: process_edit.output, - proof: proof.output, -}); diff --git a/.grok/workflows/domain-increment.rhai b/.grok/workflows/domain-increment.rhai deleted file mode 100644 index 7c6ca45c3..000000000 --- a/.grok/workflows/domain-increment.rhai +++ /dev/null @@ -1,227 +0,0 @@ -// Domain lane increment lifecycle: specify → design → build → verify → admit → review → handoff. -// Leader merges. Executors must not clear PRODUCT HOLDs or edit docs/current/**. -// Pair with ci-fleet-tick while PR CI runs (never CI-only wait): product-process-tick / program-tick. -// Process doctrine: .grok/programs/BUN-PARALLEL-DISCIPLINE.md -let meta = #{ - name: "domain-increment", - description: "SDLC for one backend domain lane (admission → design → build → verify → admit → review → handoff)", - when_to_use: "Product track work under PRODUCT/ROADMAP; while CI waits use with program-tick dual track", - phases: [ - #{ title: "Specify", detail: "admission record and acceptance criteria" }, - #{ title: "Design", detail: "architect CLEAR/WATCH/BLOCK" }, - #{ title: "Build", detail: "executor in allowlisted paths" }, - #{ title: "Verify", detail: "run verification commands" }, - #{ title: "Admit", detail: "local process gates (lens/doc/ci) before handoff" }, - #{ title: "Review", detail: "code + optional security review" }, - #{ title: "Handoff", detail: "PR body for leader merge; no auto-merge" }, - ], -}; - -let admission_schema = #{ - "type": "object", - "required": ["objective", "non_goals", "allowed_paths", "forbidden_paths", "holds_checked", "verification_commands", "stop_conditions"], - "properties": #{ - "objective": #{ "type": "string" }, - "non_goals": #{ "type": "string" }, - "allowed_paths": #{ "type": "array", "items": #{ "type": "string" } }, - "forbidden_paths": #{ "type": "array", "items": #{ "type": "string" } }, - "holds_checked": #{ "type": "string" }, - "verification_commands": #{ "type": "array", "items": #{ "type": "string" } }, - "stop_conditions": #{ "type": "string" }, - }, -}; - -let design_schema = #{ - "type": "object", - "required": ["verdict", "evidence", "blockers"], - "properties": #{ - "verdict": #{ "type": "string" }, - "evidence": #{ "type": "string" }, - "blockers": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -let handoff_schema = #{ - "type": "object", - "required": ["summary", "pr_title", "pr_body", "candidate_notes"], - "properties": #{ - "summary": #{ "type": "string" }, - "pr_title": #{ "type": "string" }, - "pr_body": #{ "type": "string" }, - "candidate_notes": #{ "type": "string" }, - }, -}; - -if args == () { pause("verification", "Pass args: lane_id, issue, base_sha, allowlist_glob, risk_class, objective_hint"); } - -let lane_id = args.lane_id; -let issue = args.issue; -let base_sha = args.base_sha; -let allowlist_glob = args.allowlist_glob; -let risk_class = args.risk_class; -let objective_hint = args.objective_hint; -if lane_id == () || issue == () || base_sha == () || allowlist_glob == () { - pause("verification", "Required args: lane_id, issue, base_sha, allowlist_glob"); -} -if risk_class == () { risk_class = "high"; } -if objective_hint == () { objective_hint = "Advance backend-only work under PRODUCT/ROADMAP HOLDs"; } - -// --- Specify --- -phase("Specify"); -let specify_prompt = ""; -specify_prompt += "You are the specify stage for Console domain lane " + lane_id + " (issue " + issue + ").\n"; -specify_prompt += "Base SHA (immutable): " + base_sha + ".\n"; -specify_prompt += "Allowlist glob: " + allowlist_glob + ".\n"; -specify_prompt += "Objective hint: " + objective_hint + ".\n"; -specify_prompt += "Read docs/current/PRODUCT.md, ROADMAP.md, DELIVERY.md with read_file/grep.\n"; -specify_prompt += "Produce a strict admission record. HOLDs must be listed; none may be cleared by implication.\n"; -specify_prompt += "Forbidden always includes: docs/current/**, production exposure, frontend/Leptos unless HOLD cleared, migrations unless this increment owns the migration lease.\n"; -specify_prompt += "Do not implement code. Return only the schema fields.\n"; -let spec = agent(specify_prompt, #{ - label: "specify:" + lane_id, - capability_mode: "read-only", - output_schema: admission_schema, -}); -if spec == () || !spec.success || spec.output == () { - complete(#{ ok: false, stage: "specify", error: "specify agent failed" }); -} - -// --- Design --- -phase("Design"); -let design_prompt = ""; -design_prompt += "Architect review for lane " + lane_id + ".\n"; -design_prompt += "Admission objective: " + spec.output.objective + "\n"; -design_prompt += "Non-goals: " + spec.output.non_goals + "\n"; -design_prompt += "HOLDs: " + spec.output.holds_checked + "\n"; -design_prompt += "Allowed paths: " + json_encode(spec.output.allowed_paths) + "\n"; -design_prompt += "Read the allowlisted code only. verdict must be CLEAR, WATCH, or BLOCK.\n"; -design_prompt += "BLOCK if the work would clear a PRODUCT HOLD, open a second write path for payroll, or ship UI under frontend HOLD.\n"; -let design = agent(design_prompt, #{ - label: "design:" + lane_id, - capability_mode: "read-only", - agent_type: "oh-my-claudecode:architect", - output_schema: design_schema, -}); -if design == () || !design.success || design.output == () { - complete(#{ ok: false, stage: "design", error: "design agent failed", admission: spec.output }); -} -if design.output.verdict == "BLOCK" { - complete(#{ - ok: false, - stage: "design", - verdict: "BLOCK", - blockers: design.output.blockers, - admission: spec.output, - }); -} - -// --- Build --- -phase("Build"); -let build_prompt = ""; -build_prompt += "Implement the admission for lane " + lane_id + " issue " + issue + ".\n"; -build_prompt += "Objective: " + spec.output.objective + "\n"; -build_prompt += "Non-goals: " + spec.output.non_goals + "\n"; -build_prompt += "ONLY modify paths matching allowlist: " + allowlist_glob + "\n"; -build_prompt += "Allowed path list: " + json_encode(spec.output.allowed_paths) + "\n"; -build_prompt += "Forbidden: " + json_encode(spec.output.forbidden_paths) + "\n"; -build_prompt += "Base SHA " + base_sha + ". Work in the current workspace; keep commits focused.\n"; -build_prompt += "Do not edit docs/current/**, .gjc/**, or parent dirty deploy files.\n"; -build_prompt += "Run verification commands when done: " + json_encode(spec.output.verification_commands) + "\n"; -build_prompt += "Return a short summary of files changed and test results as plain text.\n"; -let build = agent(build_prompt, #{ - label: "build:" + lane_id, - // all: write + shell (execute-only is too weak for real implementation) - capability_mode: "all", - isolation_worktree: true, -}); -if build == () || !build.success { - complete(#{ ok: false, stage: "build", error: "build agent failed", admission: spec.output, design: design.output }); -} - -// --- Verify --- -phase("Verify"); -let verify_prompt = ""; -verify_prompt += "Verify the build for lane " + lane_id + " against the admission.\n"; -verify_prompt += "Run these commands and report discovered vs executed counts where applicable:\n"; -verify_prompt += json_encode(spec.output.verification_commands) + "\n"; -verify_prompt += "Fail closed if any command fails or if tests were skipped/deleted without receipt.\n"; -verify_prompt += "Return plain text evidence only.\n"; -let verify = agent(verify_prompt, #{ - label: "verify:" + lane_id, - capability_mode: "execute", - agent_type: "agent-skills:test-engineer", -}); -if verify == () || !verify.success { - complete(#{ ok: false, stage: "verify", error: "verify agent failed", admission: spec.output }); -} - -// --- Admit (process gate — prevents hosted reds that local tools catch) --- -phase("Admit"); -let admit_prompt = ""; -admit_prompt += "Local admission for lane " + lane_id + " before handoff/PR.\n"; -admit_prompt += "Run applicable gates based on changed paths (use git status/diff).\n"; -admit_prompt += "Always consider: npm run check:ci-preflight if CI/tools touched;\n"; -admit_prompt += "node scripts/check-reasoning-lens-manifest.mjs if AGENTS.md, CLAUDE.md or README.md touched;\n"; -admit_prompt += "npm run check:doc-manifest if documentation-manifest or ledger tip touched;\n"; -admit_prompt += "npm run check:product-buck-residual if ci.yml or postgres map touched.\n"; -admit_prompt += "Map any failure to .grok/harness/failure-classes.v1.json class id.\n"; -admit_prompt += "If admit fails due to missing permanent control, recommend /workflow process-upgrade — do not only patch one file and forget the class.\n"; -admit_prompt += "Return plain text: commands run, pass/fail, class ids, go/no-go for handoff.\n"; -let admit = agent(admit_prompt, #{ - label: "admit:" + lane_id, - capability_mode: "execute", -}); -if admit == () || !admit.success { - complete(#{ ok: false, stage: "admit", error: "admit agent failed", admission: spec.output, verify: verify.output }); -} - -// --- Review --- -phase("Review"); -let review_jobs = []; -review_jobs.push(#{ - prompt: "Code review the current changes for lane " + lane_id + ". Focus on correctness, allowlist violations, HOLD breaches, and missing tests. Read the diff with tools.", - label: "review-code:" + lane_id, - capability_mode: "read-only", - agent_type: "agent-skills:code-reviewer", -}); -if risk_class == "high" { - review_jobs.push(#{ - prompt: "Security review for lane " + lane_id + " focusing on authz fail-closed, tenant isolation, PII logging, and injection. Read the diff with tools.", - label: "review-sec:" + lane_id, - capability_mode: "read-only", - agent_type: "agent-skills:security-auditor", - }); -} -let reviews = parallel(review_jobs); - -// --- Handoff --- -phase("Handoff"); -let handoff_prompt = ""; -handoff_prompt += "Assemble a GitHub PR title and body for lane " + lane_id + " issue " + issue + ".\n"; -handoff_prompt += "Admission objective: " + spec.output.objective + "\n"; -handoff_prompt += "Design verdict: " + design.output.verdict + "\n"; -handoff_prompt += "Build notes: " + json_encode(build.output) + "\n"; -handoff_prompt += "Verify notes: " + json_encode(verify.output) + "\n"; -handoff_prompt += "Include Closes " + issue + " if appropriate. State HOLDs not cleared. Leader will merge.\n"; -handoff_prompt += "Admit notes: " + json_encode(admit.output) + "\n"; -handoff_prompt += "After PR open: run program-tick (ci-fleet-tick + product-process-tick) — never pure CI wait.\n"; -let handoff = agent(handoff_prompt, #{ - label: "handoff:" + lane_id, - capability_mode: "read-only", - output_schema: handoff_schema, -}); - -complete(#{ - ok: true, - lane_id: lane_id, - issue: issue, - base_sha: base_sha, - admission: spec.output, - design: design.output, - build: build.output, - verify: verify.output, - admit: admit.output, - reviews: reviews, - handoff: if handoff != () && handoff.success { handoff.output } else { () }, - note: "Leader owns git push, gh pr create/merge, and beads updates. Dual-track while CI runs.", -}); diff --git a/.grok/workflows/implement-lane.rhai b/.grok/workflows/implement-lane.rhai deleted file mode 100644 index b1413e880..000000000 --- a/.grok/workflows/implement-lane.rhai +++ /dev/null @@ -1,162 +0,0 @@ -// implement-lane — claim one board/beads item and drive implementation to a PR. -// Grabs highest-priority ready work that is path-safe. Soft reds/fleet_fix of type -// "code fix" may be claimed here; pure CI restack belongs to pr-babysit. -// Opens PR with signed C/T when required. Does not merge (pr-babysit does after approve). - -let meta = #{ - name: "implement-lane", - description: "Claim ready board/beads work and implement through PR open — no silent skip of soft-red fix items", - when_to_use: "After work-manager; when board has ready implementable items", - phases: [ - #{ title: "Claim", detail: "select and claim one item" }, - #{ title: "Admit", detail: "allowlist HOLDs verification" }, - #{ title: "Build", detail: "implement in worktree" }, - #{ title: "Verify", detail: "local tests/gates" }, - #{ title: "Handoff", detail: "signed tip if needed + open PR + board update" }, - ], -}; - -let claim_schema = #{ - "type": "object", - "required": ["claimed", "item_id", "kind", "title", "allowlist", "forbidden", "holds_checked", "rationale"], - "properties": #{ - "claimed": #{ "type": "boolean" }, - "item_id": #{ "type": "string" }, - "kind": #{ "type": "string" }, - "title": #{ "type": "string" }, - "allowlist": #{ "type": "array", "items": #{ "type": "string" } }, - "forbidden": #{ "type": "array", "items": #{ "type": "string" } }, - "holds_checked": #{ "type": "string" }, - "rationale": #{ "type": "string" }, - "pr_number_if_fix": #{ "type": "string" }, - "skip_reason": #{ "type": "string" }, - }, -}; - -let handoff_schema = #{ - "type": "object", - "required": ["ok", "summary", "pr_url_or_number", "board_updated", "next", "paths_touched", "verification"], - "properties": #{ - "ok": #{ "type": "boolean" }, - "summary": #{ "type": "string" }, - "pr_url_or_number": #{ "type": "string" }, - "board_updated": #{ "type": "boolean" }, - "next": #{ "type": "string" }, - "paths_touched": #{ "type": "array", "items": #{ "type": "string" } }, - "verification": #{ "type": "string" }, - }, -}; - -let force_id = ""; -if args != () && args.item_id != () { - force_id = args.item_id; -} - -phase("Claim"); -let claim_prompt = ""; -claim_prompt += "Claim ONE implementable item from Console lane board / beads.\n"; -claim_prompt += "Read .grok/harness/lane-board.live.json and .grok/harness/lane-board.v1.json priority_order.\n"; -claim_prompt += "Run bd ready. Prefer priority: hard_block/soft_red/review_fix that need CODE fixes, then process, then product.\n"; -claim_prompt += "Do NOT claim pure restack/CI-wait items (those are pr-babysit) unless the board marks them kind fleet_fix needing code.\n"; -claim_prompt += "Do NOT claim tip-serial thrashing product while tip_writers>=2 unless item is the serial tip head fix.\n"; -claim_prompt += "PRODUCT HOLDs fail-closed. If force item_id provided use it: "; -claim_prompt += force_id; -claim_prompt += "\nClaim via bd update ID --claim when beads_id present. Set board item status claimed/implementing.\n"; -claim_prompt += "If nothing claimable, claimed=false with skip_reason (must not ignore open soft reds — say they are babysit-owned if so).\n"; -let claim = agent(claim_prompt, #{ - label: "impl-claim", - capability_mode: "all", - output_schema: claim_schema, -}); -if claim == () || !claim.success { - complete(#{ ok: false, stage: "claim", error: "claim failed" }); -} -if claim.output.claimed != true { - complete(#{ - ok: true, - workflow: "implement-lane", - idle: true, - skip_reason: claim.output.skip_reason, - note: "No implementable claim — soft reds must still be on board for pr-babysit", - }); -} - -phase("Admit"); -let admit_prompt = ""; -admit_prompt += "Admission for claimed item. Enforce allowlist/forbidden/HOLDs.\n"; -admit_prompt += json_encode(claim.output); -admit_prompt += "\nRead PRODUCT HOLDs. Write short admission: objective, non_goals, verification_commands, stop_conditions.\n"; -admit_prompt += "If HOLD would be violated, stop and requeue as blocked on board.\n"; -let admit = agent(admit_prompt, #{ - label: "impl-admit", - capability_mode: "execute", -}); -if admit == () || !admit.success { - complete(#{ ok: false, stage: "admit", error: "admit failed", claim: claim.output }); -} - -phase("Build"); -let build_prompt = ""; -build_prompt += "IMPLEMENT code for the claimed item — this workflow drives implementation, not ticket text.\n"; -build_prompt += "Isolated worktree required. Produce real file diffs.\n"; -build_prompt += "Claim:\n"; -build_prompt += json_encode(claim.output); -build_prompt += "\nAdmission:\n"; -build_prompt += json_encode(admit.output); -build_prompt += "\nRules:\n"; -build_prompt += "- Stay inside allowlist; never docs/current/** product authority edits.\n"; -build_prompt += "- Authority tip C/T SSH-signed when PR needs tip; C prebinds T-final ledger blob_sha (docs.tip-blob-prebind).\n"; -build_prompt += "- Do not open a second tip-writing PR if tip_writers>=2 unless this item IS the serial tip drain fix.\n"; -build_prompt += "- Prefer batch pure domain tests on one branch.\n"; -build_prompt += "- rustfmt + cargo test for touched crates; admit gates if process lane.\n"; -build_prompt += "- Fail closed if you only write docs/ledger without product/process allowlist code when claim is product/process code.\n"; -build_prompt += "Commit signed (gpg.format ssh). Push branch. Open PR with gh pr create if tip capacity allows; else local branch only and say so.\n"; -build_prompt += "Return PR number/url, paths_touched[], and verification command output summary.\n"; -let built = agent(build_prompt, #{ - label: "impl-build", - capability_mode: "all", - isolation_worktree: true, -}); -if built == () || !built.success { - complete(#{ ok: false, stage: "build", error: "build failed — edit implement-lane Build phase if agents narrate without code", claim: claim.output }); -} - -phase("Verify"); -let ver_prompt = ""; -ver_prompt += "Re-verify implementation evidence from build output. Run key verification commands.\n"; -ver_prompt += json_encode(built.output); -ver_prompt += "\nIf red, fix in place (same branch) up to 2 tight loops. Report pass/fail.\n"; -let verified = agent(ver_prompt, #{ - label: "impl-verify", - capability_mode: "all", -}); -if verified == () || !verified.success { - complete(#{ ok: false, stage: "verify", error: "verify failed", build: built.output }); -} - -phase("Handoff"); -let hand_prompt = ""; -hand_prompt += "Handoff: ensure PR is open; update lane-board.live.json item status to pr_open or review_pending;\n"; -hand_prompt += "include pr_number; clear claim locks appropriately; bd update if needed.\n"; -hand_prompt += "Claim:\n"; -hand_prompt += json_encode(claim.output); -hand_prompt += "\nBuild:\n"; -hand_prompt += json_encode(built.output); -hand_prompt += "\nVerify:\n"; -hand_prompt += json_encode(verified.output); -hand_prompt += "\nDo NOT merge. next must say run pr-babysit for this PR.\n"; -let hand = agent(hand_prompt, #{ - label: "impl-handoff", - capability_mode: "all", - output_schema: handoff_schema, -}); -if hand == () || !hand.success { - complete(#{ ok: false, stage: "handoff", error: "handoff failed", build: built.output }); -} - -complete(#{ - ok: hand.output.ok, - workflow: "implement-lane", - claim: claim.output, - handoff: hand.output, -}); diff --git a/.grok/workflows/learn.rhai b/.grok/workflows/learn.rhai deleted file mode 100644 index a5fd53624..000000000 --- a/.grok/workflows/learn.rhai +++ /dev/null @@ -1,82 +0,0 @@ -// learn — Hermes-inspired closed learning tick (no hermes CLI). -// Maps failures → reflection/tip/skill draft → process-upgrade recommendation. - -let meta = #{ - name: "learn", - description: "Closed learning loop: reflections, tips, skill drafts, process-upgrade prompts from failure classes", - when_to_use: "After program-control, ralph, or any hard fail; promotes process edits not chat folklore", - phases: [ - #{ title: "Collect", detail: "open soft reds, recent fails, board" }, - #{ title: "Persist", detail: "console-learn + memory" }, - #{ title: "Promote", detail: "process-upgrade if class repeats" }, - ], -}; - -let out_schema = #{ - "type": "object", - "required": ["ok", "classes", "wrote", "promote_process_upgrade", "summary"], - "properties": #{ - "ok": #{ "type": "boolean" }, - "classes": #{ "type": "array", "items": #{ "type": "string" } }, - "wrote": #{ "type": "array", "items": #{ "type": "string" } }, - "promote_process_upgrade": #{ "type": "boolean" }, - "summary": #{ "type": "string" }, - }, -}; - -phase("Collect"); -let c_prompt = ""; -c_prompt += "Collect learning signals. Read lane-board.live.json, failure-classes.v1.json, hermes-learning.v1.json,\n"; -c_prompt += "memory/reflections (today), ultragoal ledger tail if present.\n"; -c_prompt += "List failure classes observed (soft reds count as ops.soft-red-silence if unowned).\n"; -c_prompt += "gh pr list open with fails → classes.\n"; -if args != () && args.classes != () { - c_prompt += "Forced classes: "; - c_prompt += args.classes; - c_prompt += "\n"; -} -if args != () && args.summary != () { - c_prompt += "Summary: "; - c_prompt += args.summary; - c_prompt += "\n"; -} -let collected = agent(c_prompt, #{ - label: "learn-collect", - capability_mode: "execute", -}); -if collected == () || !collected.success { - complete(#{ ok: false, stage: "collect", error: "collect failed" }); -} - -phase("Persist"); -let p_prompt = ""; -p_prompt += "Run console-learn and ensure MEMORY/tips/trajectories updated.\n"; -p_prompt += "Collected:\n"; -p_prompt += json_encode(collected.output); -p_prompt += "\nExample: `.grok/bin/console-learn from-event --id learn-tick --summary '…' --classes 'a,b'`\n"; -p_prompt += "If hard or repeated class, --promote-skill.\n"; -let persisted = agent(p_prompt, #{ - label: "learn-persist", - capability_mode: "all", - output_schema: out_schema, -}); -if persisted == () || !persisted.success { - complete(#{ ok: false, stage: "persist", error: "persist failed" }); -} - -phase("Promote"); -let pr_prompt = ""; -pr_prompt += "If promote_process_upgrade or class repeats >=2, recommend `/workflow process-upgrade` with class_id.\n"; -pr_prompt += "Do not expand product scope. Soft reds must remain owned on board.\n"; -pr_prompt += json_encode(persisted.output); -let promoted = agent(pr_prompt, #{ - label: "learn-promote", - capability_mode: "execute", -}); - -complete(#{ - ok: true, - workflow: "learn", - result: persisted.output, - promote: if promoted != () && promoted.success { promoted.output } else { () }, -}); diff --git a/.grok/workflows/pr-babysit.rhai b/.grok/workflows/pr-babysit.rhai deleted file mode 100644 index 92940a405..000000000 --- a/.grok/workflows/pr-babysit.rhai +++ /dev/null @@ -1,217 +0,0 @@ -// pr-babysit — drive open PRs to merge under autonomy-merge policy. -// Fix CI/conflicts/soft reds → agent review → if not approve fix until approve → merge. -// Soft reds and blocks MUST be addressed or re-enqueued (never silent). -// Human supervises; intervenes only if awry. Default: full autonomous. - -let meta = #{ - name: "pr-babysit", - description: "Babysit PRs: fix reds/blocks, agent-review until approve, then merge — soft reds never silent", - when_to_use: "Whenever open PRs exist; after implement-lane; every program-control tick", - phases: [ - #{ title: "Select", detail: "pick PR(s) needing action including soft reds" }, - #{ title: "Repair", detail: "restack, CI fix, tip prebind, conflicts" }, - #{ title: "Review", detail: "independent agent review verdict" }, - #{ title: "FixLoop", detail: "until approve or max rounds" }, - #{ title: "Merge", detail: "merge when approve + Required green" }, - #{ title: "Board", detail: "update board; requeue residual soft reds" }, - ], -}; - -let select_schema = #{ - "type": "object", - "required": ["targets", "soft_reds_seen", "blocks_seen", "none_idle_ok"], - "properties": #{ - "targets": #{ "type": "array", "items": #{ "type": "string" } }, - "soft_reds_seen": #{ "type": "array", "items": #{ "type": "string" } }, - "blocks_seen": #{ "type": "array", "items": #{ "type": "string" } }, - "none_idle_ok": #{ "type": "boolean" }, - "notes": #{ "type": "string" }, - }, -}; - -let review_schema = #{ - "type": "object", - "required": ["pr", "verdict", "summary", "must_fix"], - "properties": #{ - "pr": #{ "type": "string" }, - "verdict": #{ "type": "string" }, - "summary": #{ "type": "string" }, - "must_fix": #{ "type": "array", "items": #{ "type": "string" } }, - "risk": #{ "type": "string" }, - }, -}; - -let merge_schema = #{ - "type": "object", - "required": ["pr", "merged", "reason"], - "properties": #{ - "pr": #{ "type": "string" }, - "merged": #{ "type": "boolean" }, - "reason": #{ "type": "string" }, - "sha": #{ "type": "string" }, - }, -}; - -let only_pr = ""; -if args != () && args.pr != () { - only_pr = args.pr; -} -let max_rounds = 5; -if args != () && args.max_fix_rounds != () { - // args may be number-like string; agents honor prompt default 5 - max_rounds = 5; -} -let allow_merge = true; -if args != () && args.allow_merge == false { - allow_merge = false; -} - -phase("Select"); -let sel_prompt = ""; -sel_prompt += "Select open PRs to babysit. Soft reds/blocks MUST appear in soft_reds_seen/blocks_seen even if not yet fixable.\n"; -sel_prompt += "Run gh pr list --state open --limit 30 --json number,title,mergeable,mergeStateStatus,headRefOid,url.\n"; -sel_prompt += "Read .grok/harness/lane-board.live.json for fleet_fix/soft_red/review_fix items.\n"; -sel_prompt += "Read .grok/harness/autonomy-merge.v1.json.\n"; -sel_prompt += "For each PR classify: waiting_ci, failed, behind, dirty, auth_fail, review_pending, merge_ready_candidate.\n"; -sel_prompt += "targets: ordered PR numbers as strings (tip-serial order if tip writers).\n"; -if only_pr != "" { - sel_prompt += "Focus only on PR "; - sel_prompt += only_pr; - sel_prompt += " but still list ALL soft reds/blocks seen fleet-wide in soft_reds_seen/blocks_seen.\n"; -} -sel_prompt += "none_idle_ok=true only if targets non-empty OR truly no open PRs and no board soft reds.\n"; -sel_prompt += "SILENCE FORBIDDEN: every BEHIND/DIRTY/fail/auth red must be in soft_reds_seen or blocks_seen.\n"; -let selected = agent(sel_prompt, #{ - label: "baby-select", - capability_mode: "execute", - output_schema: select_schema, -}); -if selected == () || !selected.success { - complete(#{ ok: false, stage: "select", error: "select failed" }); -} - -phase("Repair"); -let rep_prompt = ""; -rep_prompt += "Repair pass for babysit targets. Policy: full autonomy under human supervision.\n"; -rep_prompt += "Selection:\n"; -rep_prompt += json_encode(selected.output); -rep_prompt += "\nFor each target PR (tip-serial order for manifest/baseline/tip PRs):\n"; -rep_prompt += "1) If BEHIND/DIRTY/CONFLICTING: restack onto origin/main with SSH-signed C/T; tip prebind "; -rep_prompt += "(C ledger without ## Authority tip footer; manifest blob_sha = T-final; never naive generate --write after tip).\n"; -rep_prompt += "2) If CI/auth/preflight red: diagnose logs, fix code/process on branch, push force-with-lease when needed.\n"; -rep_prompt += "3) Soft reds (non-required fails, cancelled, stale): fix or convert to board item with evidence — do not ignore.\n"; -rep_prompt += "4) Do not merge in this phase.\n"; -rep_prompt += "Report per-PR actions and residual reds.\n"; -let repaired = agent(rep_prompt, #{ - label: "baby-repair", - capability_mode: "all", -}); -if repaired == () || !repaired.success { - complete(#{ ok: false, stage: "repair", error: "repair failed", select: selected.output }); -} - -phase("Review"); -let rev_prompt = ""; -rev_prompt += "Independent code review of the primary target PR (first in targets, or only_pr).\n"; -rev_prompt += "Use gh pr diff / gh pr view / read files. Adversarial but practical.\n"; -rev_prompt += "Selection:\n"; -rev_prompt += json_encode(selected.output); -rev_prompt += "\nRepair notes:\n"; -rev_prompt += json_encode(repaired.output); -rev_prompt += "\nverdict MUST be exactly one of: approve | changes_requested | comment\n"; -rev_prompt += "approve only if: Required-path safe, no HOLD violation, tip/authority sane, tests adequate for change, no serious defect.\n"; -rev_prompt += "If not approve, must_fix must be concrete actionable items.\n"; -rev_prompt += "This verdict gates autonomous merge (see autonomy-merge.v1.json).\n"; -let reviewed = agent(rev_prompt, #{ - label: "baby-review", - capability_mode: "execute", - output_schema: review_schema, -}); -if reviewed == () || !reviewed.success { - complete(#{ ok: false, stage: "review", error: "review failed" }); -} - -phase("FixLoop"); -let fix_prompt = ""; -fix_prompt += "Fix loop until approve. max_fix_rounds="; -fix_prompt += max_rounds.to_string(); -fix_prompt += "\nCurrent review:\n"; -fix_prompt += json_encode(reviewed.output); -fix_prompt += "\nIf verdict already approve, do nothing except confirm Required CI/Security green on exact head.\n"; -fix_prompt += "If not approve: implement must_fix on the PR branch, push, re-check CI, re-self-review.\n"; -fix_prompt += "Loop until approve OR rounds exhausted. On exhaust: leave PR open, board status blocked, list residuals — NEVER silent-drop.\n"; -fix_prompt += "Re-state final verdict: approve | changes_requested | blocked_exhausted.\n"; -fix_prompt += "Include PR number and head sha.\n"; -let fixed = agent(fix_prompt, #{ - label: "baby-fix-loop", - capability_mode: "all", -}); -if fixed == () || !fixed.success { - complete(#{ ok: false, stage: "fix-loop", error: "fix loop failed", review: reviewed.output }); -} - -phase("Merge"); -let merge_result = #{ merged: false, reason: "skipped", pr: "" }; -if allow_merge { - let merge_prompt = ""; - merge_prompt += "Autonomous merge gate (human supervise-only).\n"; - merge_prompt += "Read .grok/harness/autonomy-merge.v1.json.\n"; - merge_prompt += "Review+fix output:\n"; - merge_prompt += json_encode(fixed.output); - merge_prompt += "\nEarlier review:\n"; - merge_prompt += json_encode(reviewed.output); - merge_prompt += "\nMerge with gh pr merge N --squash ONLY IF all hold:\n"; - merge_prompt += "- agent verdict approve (from fix loop final or review)\n"; - merge_prompt += "- Required / CI success on exact head\n"; - merge_prompt += "- Required / Security success on exact head\n"; - merge_prompt += "- mergeable and not BEHIND\n"; - merge_prompt += "If not met: merged=false with reason; enqueue soft red/block on board.\n"; - merge_prompt += "If met: merge and report sha. No human approval step required.\n"; - let merged = agent(merge_prompt, #{ - label: "baby-merge", - capability_mode: "all", - output_schema: merge_schema, - }); - if merged != () && merged.success { - merge_result = merged.output; - } else { - merge_result = #{ merged: false, reason: "merge agent failed", pr: "" }; - } -} else { - merge_result = #{ merged: false, reason: "allow_merge=false", pr: only_pr }; -} - -phase("Board"); -let board_prompt = ""; -board_prompt += "Update .grok/harness/lane-board.live.json after babysit.\n"; -board_prompt += "Select soft reds/blocks seen:\n"; -board_prompt += json_encode(selected.output); -board_prompt += "\nRepair:\n"; -board_prompt += json_encode(repaired.output); -board_prompt += "\nReview:\n"; -board_prompt += json_encode(reviewed.output); -board_prompt += "\nFix:\n"; -board_prompt += json_encode(fixed.output); -board_prompt += "\nMerge:\n"; -board_prompt += json_encode(merge_result); -board_prompt += "\nRules:\n"; -board_prompt += "- Every soft_reds_seen and blocks_seen must still have a board item (upsert) unless truly resolved (mark merged/done with evidence).\n"; -board_prompt += "- Merged PR → status merged; close related beads if appropriate.\n"; -board_prompt += "- Non-approve residual → review_fix ready with fix_rounds incremented.\n"; -board_prompt += "- SILENCE FORBIDDEN.\n"; -board_prompt += "Write lane-board.live.json. Return brief summary text.\n"; -let board = agent(board_prompt, #{ - label: "baby-board", - capability_mode: "all", -}); - -complete(#{ - ok: true, - workflow: "pr-babysit", - select: selected.output, - review: reviewed.output, - fix: fixed.output, - merge: merge_result, - board: if board != () && board.success { board.output } else { "board update failed" }, - autonomy: "approve_then_merge", -}); diff --git a/.grok/workflows/process-upgrade.rhai b/.grok/workflows/process-upgrade.rhai deleted file mode 100644 index 6fa31b6ea..000000000 --- a/.grok/workflows/process-upgrade.rhai +++ /dev/null @@ -1,138 +0,0 @@ -// Process-upgrade — Bun "edit the process" for one failure class. -// Allowlist: workflows, harness, tools/ci, check scripts — NOT product crates. -// After 2 repeats of a class, this workflow should land a permanent control. -// Doctrine: .grok/programs/BUN-PARALLEL-DISCIPLINE.md + learning-loop.v1.json - -let meta = #{ - name: "process-upgrade", - description: "Map a failure class to a durable workflow/tool control (not a one-shot PR fix)", - when_to_use: "Hosted red mapped to failure-classes.v1.json; or repeat class >= 2; never for product features", - phases: [ - #{ title: "Map", detail: "class id + evidence URL + missing control" }, - #{ title: "Design", detail: "permanent control in admit/workflow/hook class" }, - #{ title: "Implement", detail: "edit process allowlist only" }, - #{ title: "Verify", detail: "detector fails open bug / passes fixed" }, - #{ title: "Handoff", detail: "PR body for process-only change" }, - ], -}; - -let map_schema = #{ - "type": "object", - "required": ["class_id", "evidence", "missing_control", "proposed_control", "one_shot_rejected"], - "properties": #{ - "class_id": #{ "type": "string" }, - "evidence": #{ "type": "string" }, - "missing_control": #{ "type": "string" }, - "proposed_control": #{ "type": "string" }, - "one_shot_rejected": #{ "type": "string" }, - }, -}; - -let class_id = ""; -let evidence = ""; -if args != () { - if args.class_id != () { class_id = args.class_id; } - if args.evidence != () { evidence = args.evidence; } -} -if class_id == () { - pause("verification", "Pass args.class_id (from failure-classes.v1.json) and optional args.evidence (CI URL)"); -} - -phase("Map"); -let map_prompt = ""; -map_prompt += "Process-upgrade MAP for Console failure class: " + class_id + "\n"; -map_prompt += "Evidence: " + evidence + "\n"; -map_prompt += "Read .grok/harness/failure-classes.v1.json and .grok/programs/BUN-PARALLEL-DISCIPLINE.md with read_file.\n"; -map_prompt += "State what permanent control is missing so agents cannot repeat this class.\n"; -map_prompt += "one_shot_rejected: describe the forbidden one-shot fix (e.g. only fixing one PR lens JSON without admit gate).\n"; -map_prompt += "proposed_control must be reusable (workflow phase, npm admit command, class-based hook matcher, harness catalog row).\n"; -let mapped = agent(map_prompt, #{ - label: "proc-map", - capability_mode: "read-only", - output_schema: map_schema, -}); -if mapped == () || !mapped.success || mapped.output == () { - complete(#{ ok: false, stage: "map", error: "map failed" }); -} - -phase("Design"); -let design = agent( - "Design the minimal permanent control for class " + class_id + ".\n" - + "Map result: " + json_encode(mapped.output) + "\n" - + "Allowlist only: .grok/workflows/**, .grok/harness/**, .grok/programs/**, tools/ci/**, scripts/check-*.mjs, scripts/console/**, package.json.\n" - + "Forbidden: docs/current/**, backend/crates/**, deploy/**, product features.\n" - + "Return plain text: files to touch, detector command, how domain-increment or ci-fleet-tick will call it.\n", - #{ - label: "proc-design", - capability_mode: "read-only", - }, -); -if design == () || !design.success { - complete(#{ ok: false, stage: "design", error: "design failed", map: mapped.output }); -} - -phase("Implement"); -let impl = agent( - "Implement the process control for class " + class_id + ".\n" - + "Design:\n" + json_encode(design.output) + "\n" - + "Map:\n" + json_encode(mapped.output) + "\n" - + "ONLY edit process allowlist paths. Update failure-classes.v1.json if detector/control text changes.\n" - + "If adding a hook, it must be class-based (path/tool matcher), never named for a single PR.\n" - + "Add or update a tip under .grok/memory/tips/ if useful.\n" - + "Run detector commands to show fail/pass behavior when possible.\n", - #{ - label: "proc-impl", - capability_mode: "execute", - isolation_worktree: true, - }, -); -if impl == () || !impl.success { - complete(#{ ok: false, stage: "implement", error: "implement failed", map: mapped.output }); -} - -phase("Verify"); -let ver = agent( - "Verify process-upgrade for " + class_id + ".\n" - + "Confirm controls are permanent (workflow/harness/tool), not a one-off content fix.\n" - + "Run relevant admit_commands from failure-classes.v1.json.\n" - + "Fail if only product files changed or docs/current edited.\n" - + "Plain text evidence.\n", - #{ - label: "proc-verify", - capability_mode: "execute", - agent_type: "agent-skills:test-engineer", - }, -); - -phase("Handoff"); -let handoff = agent( - "Write PR title and body for process-upgrade class " + class_id + ".\n" - + "State: process-only; Bun fix-the-process; links evidence " + evidence + ".\n" - + "Leader merges. Include files changed summary from implement.\n" - + "JSON fields: summary, pr_title, pr_body, candidate_notes.\n", - #{ - label: "proc-handoff", - capability_mode: "read-only", - output_schema: #{ - "type": "object", - "required": ["summary", "pr_title", "pr_body", "candidate_notes"], - "properties": #{ - "summary": #{ "type": "string" }, - "pr_title": #{ "type": "string" }, - "pr_body": #{ "type": "string" }, - "candidate_notes": #{ "type": "string" }, - }, - }, - }, -); - -complete(#{ - ok: true, - class_id: class_id, - map: mapped.output, - design: design.output, - implement: impl.output, - verify: if ver != () && ver.success { ver.output } else { () }, - handoff: if handoff != () && handoff.success { handoff.output } else { () }, - note: "Leader owns push/PR/merge. Pair fleet waits with product-process-tick, not more WAIT.", -}); diff --git a/.grok/workflows/product-process-tick.rhai b/.grok/workflows/product-process-tick.rhai deleted file mode 100644 index e716d4958..000000000 --- a/.grok/workflows/product-process-tick.rhai +++ /dev/null @@ -1,134 +0,0 @@ -// Product + process tick — other half of the anti-wait pair. -// Run when ci-fleet-tick reports waiting_ci OR whenever fleet is idle/CLEAN-only. -// Does product/backend progress OR process-upgrade work — never pure CI narration. -// Leader-only merge. PRODUCT HOLDs fail-closed. -// Doctrine: .grok/programs/BUN-PARALLEL-DISCIPLINE.md - -let meta = #{ - name: "product-process-tick", - description: "Product/process half of dual-track drive: claim ready work while CI runs — forbids pure WAIT", - when_to_use: "Always pair with ci-fleet-tick when any PR is waiting_ci; also when fleet is empty to advance roadmap", - phases: [ - #{ title: "Context", detail: "authority + beads ready + open leases" }, - #{ title: "Select", detail: "product increment OR process-upgrade class" }, - #{ title: "Dispatch", detail: "concrete next command or admission sketch" }, - ], -}; - -let select_schema = #{ - "type": "object", - "required": [ - "track", - "choice_id", - "rationale", - "allowlist", - "forbidden", - "holds_checked", - "next_commands", - "not_waiting_on_ci", - ], - "properties": #{ - "track": #{ "type": "string" }, - "choice_id": #{ "type": "string" }, - "rationale": #{ "type": "string" }, - "allowlist": #{ "type": "array", "items": #{ "type": "string" } }, - "forbidden": #{ "type": "array", "items": #{ "type": "string" } }, - "holds_checked": #{ "type": "string" }, - "next_commands": #{ "type": "array", "items": #{ "type": "string" } }, - "not_waiting_on_ci": #{ "type": "boolean" }, - "failure_class_if_process": #{ "type": "string" }, - }, -}; - -// Optional: fleet summary from paired ci-fleet-tick -let fleet_context = ""; -if args != () && args.fleet_json != () { - fleet_context = args.fleet_json; -} -let prefer = ""; -if args != () && args.prefer_track != () { - prefer = args.prefer_track; -} - -phase("Context"); -let ctx_prompt = ""; -ctx_prompt += "Console dual-track PRODUCT/PROCESS context (execute tools).\n"; -ctx_prompt += "1) Read docs/current/PRODUCT.md HOLDs and ROADMAP.md ordered work (read_file).\n"; -ctx_prompt += "2) Run: bd ready 2>/dev/null | head -40 ; bd list --status=in_progress 2>/dev/null | head -20.\n"; -ctx_prompt += "3) Run: gh pr list --state open --limit 15 --json number,title,headRefName.\n"; -ctx_prompt += "4) Note writers that must stay serial: ci.yml, authority tip, migrations, Cargo.lock, docs/current/**.\n"; -if fleet_context != "" { - ctx_prompt += "5) Paired fleet classification JSON:\n"; - ctx_prompt += fleet_context; - ctx_prompt += "\n"; -} -ctx_prompt += "Do not merge. Do not implement product code in this phase. Plain text inventory only.\n"; -let ctx = agent(ctx_prompt, #{ - label: "pp-context", - capability_mode: "execute", -}); -if ctx == () || !ctx.success { - complete(#{ ok: false, stage: "context", error: "context failed" }); -} - -phase("Select"); -let sel_prompt = ""; -sel_prompt += "Select exactly ONE next unit of work that is NOT 'wait for CI'.\n\n"; -sel_prompt += "Context inventory:\n"; -sel_prompt += json_encode(ctx.output); -sel_prompt += "\n\nPrefer track hint (may be empty): "; -sel_prompt += prefer; -sel_prompt += "\n\nTracks (pick one):\n"; -sel_prompt += "- product: path-disjoint backend lane under PRODUCT (ontology/policy/approval foundations, or next ROADMAP backend item not HOLD-blocked). Use domain-increment next.\n"; -sel_prompt += "- process: failure-class control upgrade (workflows/harness/tools/ci admit). Use process-upgrade next. Prefer if fleet shows repeated hosted reds fixable locally (lens, doc-manifest, residual buck, skip-admit).\n"; -sel_prompt += "- substrate: DN-0005 / cargo-nextest / residual cutover when CI tip free; not while second ci.yml writer fights an open CI PR.\n\n"; -sel_prompt += "Rules:\n"; -sel_prompt += "- not_waiting_on_ci MUST be true. Choosing wait is forbidden (ops.passive-wait).\n"; -sel_prompt += "- holds_checked must list PRODUCT HOLDs and confirm none cleared by this choice.\n"; -sel_prompt += "- forbidden always includes docs/current/** edits unless track=docs with explicit docs lane (default forbid).\n"; -sel_prompt += "- allowlist must be concrete globs.\n"; -sel_prompt += "- next_commands: 1-5 shell/workflow invocations (e.g. /workflow process-upgrade, bd update, worktree add).\n"; -sel_prompt += "- If fleet waiting_ci non-empty, still pick product or process work that does not require that PR's tip writer.\n"; -sel_prompt += "- COUNT open PRs that touch documentation-manifest, ledger/, or executed-tests-baseline. If count >= 2, do NOT choose a new tip-writing product PR; either stack on existing branch or process-only allowlist.\n"; - -sel_prompt += "- Read .grok/harness/failure-classes.v1.json if process track.\n"; -let selected = agent(sel_prompt, #{ - label: "pp-select", - capability_mode: "read-only", - output_schema: select_schema, -}); -if selected == () || !selected.success || selected.output == () { - complete(#{ ok: false, stage: "select", error: "select failed", context: ctx.output }); -} -if selected.output.not_waiting_on_ci != true { - complete(#{ - ok: false, - stage: "select", - error: "ops.passive-wait: selection resolved to waiting on CI", - selection: selected.output, - }); -} - -phase("Dispatch"); -let disp_prompt = ""; -disp_prompt += "Turn this selection into a concrete dispatch brief for the operator/agent.\n"; -disp_prompt += json_encode(selected.output); -disp_prompt += "\n\nInclude:\n"; -disp_prompt += "1) Which workflow to run next: domain-increment | process-upgrade | manual admit steps.\n"; -disp_prompt += "2) Worktree base: origin/main fetch SHA.\n"; -disp_prompt += "3) Success criteria (tests/commands).\n"; -disp_prompt += "4) Explicit: do not babysit CI as the only action; ci-fleet-tick owns fleet.\n"; -disp_prompt += "Plain text.\n"; -let dispatch = agent(disp_prompt, #{ - label: "pp-dispatch", - capability_mode: "read-only", -}); - -complete(#{ - ok: true, - pair_workflow: "ci-fleet-tick", - selection: selected.output, - dispatch: if dispatch != () && dispatch.success { dispatch.output } else { () }, - context: ctx.output, - doctrine: "While CI runs, this tick must advance product or process — never pure WAIT", -}); diff --git a/.grok/workflows/program-control.rhai b/.grok/workflows/program-control.rhai deleted file mode 100644 index ef10202e9..000000000 --- a/.grok/workflows/program-control.rhai +++ /dev/null @@ -1,202 +0,0 @@ -// program-control — meta productivity manager for the autonomous Console drive. -// Ensures work-manager, implement-lane, and pr-babysit stay productive. -// Soft reds/blocks must be on the queue. Anti-passive. Full autonomy under human supervision. - -let meta = #{ - name: "program-control", - description: "Meta-orchestrator: keep work-manager, implement-lane, pr-babysit productive; soft reds never silent", - when_to_use: "Primary autonomous heartbeat; session start; when drive may be idle", - phases: [ - #{ title: "Pulse", detail: "board + open PRs + beads health" }, - #{ title: "WorkManager", detail: "refresh lanes + enqueue soft reds/blocks" }, - #{ title: "Babysit", detail: "repair/review/approve/merge" }, - #{ title: "Implement", detail: "claim ready non-tip-thrash work if capacity" }, - #{ title: "Productivity", detail: "verify no idle/silent soft reds; next wake" }, - ], -}; - -let pulse_schema = #{ - "type": "object", - "required": ["open_pr_count", "board_item_count", "soft_red_count", "block_count", "idle_risk", "notes"], - "properties": #{ - "open_pr_count": #{ "type": "number" }, - "board_item_count": #{ "type": "number" }, - "soft_red_count": #{ "type": "number" }, - "block_count": #{ "type": "number" }, - "idle_risk": #{ "type": "boolean" }, - "notes": #{ "type": "string" }, - }, -}; - -let productivity_schema = #{ - "type": "object", - "required": [ - "productive", - "silence_check_ok", - "workflows_armed", - "next_wake_actions", - "summary", - ], - "properties": #{ - "productive": #{ "type": "boolean" }, - "silence_check_ok": #{ "type": "boolean" }, - "workflows_armed": #{ "type": "array", "items": #{ "type": "string" } }, - "next_wake_actions": #{ "type": "array", "items": #{ "type": "string" } }, - "summary": #{ "type": "string" }, - "defects": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -let skip_implement = false; -if args != () && args.skip_implement == true { - skip_implement = true; -} -let skip_babysit = false; -if args != () && args.skip_babysit == true { - skip_babysit = true; -} - -phase("Pulse"); -let pulse = agent( - "Console program-control pulse. Use tools.\n" - + "1) gh pr list --state open --limit 30 --json number,title,mergeStateStatus\n" - + "2) Read .grok/harness/lane-board.live.json — count items by kind/status\n" - + "3) bd ready | head -20\n" - + "4) Note any open PR that looks red/behind/dirty even if board empty (idle_risk true if board empty but open PR issues exist)\n" - + "5) Read autonomy-merge.v1.json one-liner policy\n" - + "6) Read .grok/ultragoal/active-goal.live.json — if active, note objective + dispatch_workflow (Stop hook will loop)\n" - + "7) If prd.live.json has stories with passes!=true, prefer ralph dispatch next\n" - + "Return structured counts. soft_red_count/block_count from board + quick PR scan.\n", - #{ - label: "ctrl-pulse", - capability_mode: "execute", - output_schema: pulse_schema, - }, -); -if pulse == () || !pulse.success { - complete(#{ ok: false, stage: "pulse", error: "pulse failed" }); -} - -phase("WorkManager"); -// Inline work-manager critical path (workflows cannot nest workflows) -let wm_prompt = ""; -wm_prompt += "You are work-manager (inlined by program-control). SILENCE OF SOFT REDS/BLOCKS IS FORBIDDEN.\n"; -wm_prompt += "Pulse:\n"; -wm_prompt += json_encode(pulse.output); -wm_prompt += "\nExecute work-manager duties:\n"; -wm_prompt += "- Inventory PRODUCT HOLDs, ROADMAP, beads ready, open PRs + checks, hindsight if available\n"; -wm_prompt += "- Enumerate every soft red and block (lane-board.v1.json sources)\n"; -wm_prompt += "- Upsert .grok/harness/lane-board.live.json (dedupe source_key)\n"; -wm_prompt += "- Propose parallel_ready and serialized_tip_queue\n"; -wm_prompt += "- silence_check_ok must be true\n"; -wm_prompt += "Return summary of board writes + next_babysit_prs + next_implement_ids.\n"; -let wm = agent(wm_prompt, #{ - label: "ctrl-work-manager", - capability_mode: "all", -}); -if wm == () || !wm.success { - complete(#{ ok: false, stage: "work-manager", error: "work-manager leg failed", pulse: pulse.output }); -} - -let baby = (); -if !skip_babysit { - phase("Babysit"); - let baby_prompt = ""; - baby_prompt += "You are pr-babysit (inlined by program-control). Full autonomy: fix → agent review → approve → merge.\n"; - baby_prompt += "Policy file: .grok/harness/autonomy-merge.v1.json\n"; - baby_prompt += "Work-manager output:\n"; - baby_prompt += json_encode(wm.output); - baby_prompt += "\nRequirements:\n"; - baby_prompt += "1) Address tip-serial queue head first if multiple tip writers.\n"; - baby_prompt += "2) Restack/fix CI/auth/prebind issues (C prebinds T-final blob).\n"; - baby_prompt += "3) Independent review verdict approve|changes_requested.\n"; - baby_prompt += "4) If not approve, fix until approve (max 5 rounds) or board-block with evidence.\n"; - baby_prompt += "5) On approve + Required CI/Security green + mergeable: gh pr merge --squash (no human gate).\n"; - baby_prompt += "6) Every soft red/block remains on board until resolved — never silent.\n"; - baby_prompt += "7) Human is supervising only; do not pause for approval.\n"; - baby = agent(baby_prompt, #{ - label: "ctrl-babysit", - capability_mode: "all", - }); - if baby == () || !baby.success { - complete(#{ ok: false, stage: "babysit", error: "babysit leg failed", wm: wm.output }); - } -} - -let impl = (); -if !skip_implement { - phase("Implement"); - let impl_prompt = ""; - impl_prompt += "You are implement-lane (inlined by program-control) for AT MOST one ready item if capacity allows.\n"; - impl_prompt += "Read lane-board.live.json. Prefer soft_red/hard_block code fixes not owned by open babysit repair,\n"; - impl_prompt += "else path-disjoint product/process that does not add tip writers while tip_writers>=2.\n"; - impl_prompt += "If only tip-serial work remains and babysit is handling it, idle with reason (productive idle).\n"; - impl_prompt += "If you implement: worktree, tests, signed commits, open PR, update board to pr_open.\n"; - impl_prompt += "Do not merge (babysit does).\n"; - impl_prompt += "Work-manager:\n"; - impl_prompt += json_encode(wm.output); - if baby != () && baby.success { - impl_prompt += "\nBabysit:\n"; - impl_prompt += json_encode(baby.output); - } - impl = agent(impl_prompt, #{ - label: "ctrl-implement", - capability_mode: "all", - isolation_worktree: true, - }); - if impl == () || !impl.success { - // implement failure is not total control failure if babysit progressed - log("implement leg failed — continuing to productivity check"); - } -} - -phase("Productivity"); -let prod_prompt = ""; -prod_prompt += "Productivity auditor for Console autonomous drive. FAIL if soft reds/blocks silent or all legs pure WAIT.\n\n"; -prod_prompt += "Pulse:\n"; -prod_prompt += json_encode(pulse.output); -prod_prompt += "\nWork-manager:\n"; -prod_prompt += json_encode(wm.output); -prod_prompt += "\nBabysit:\n"; -if baby != () && baby.success { - prod_prompt += json_encode(baby.output); -} else { - prod_prompt += "(skipped or failed)\n"; -} -prod_prompt += "\nImplement:\n"; -if impl != () && impl.success { - prod_prompt += json_encode(impl.output); -} else { - prod_prompt += "(skipped or failed)\n"; -} -prod_prompt += "\nRead final .grok/harness/lane-board.live.json.\n"; -prod_prompt += "silence_check_ok=false if any open PR is BEHIND/DIRTY/failed/auth-red without a board item source_key covering it.\n"; -prod_prompt += "productive=true if any of: merge happened, PR fixed/pushed, board soft reds enqueued+owned, implement opened PR, or explicit tip-serial wait with active babysit actions.\n"; -prod_prompt += "productive=false if only narration / pure wait / silent soft reds.\n"; -prod_prompt += "workflows_armed: list which of work-manager|pr-babysit|implement-lane should run next wake.\n"; -prod_prompt += "next_wake_actions: concrete commands (e.g. /workflow pr-babysit, /workflow work-manager).\n"; -let prod = agent(prod_prompt, #{ - label: "ctrl-productivity", - capability_mode: "execute", - output_schema: productivity_schema, -}); -if prod == () || !prod.success { - complete(#{ - ok: false, - stage: "productivity", - error: "productivity audit failed", - wm: wm.output, - baby: if baby != () && baby.success { baby.output } else { () }, - }); -} - -complete(#{ - ok: prod.output.productive == true && prod.output.silence_check_ok == true, - workflow: "program-control", - pulse: pulse.output, - work_manager: wm.output, - babysit: if baby != () && baby.success { baby.output } else { () }, - implement: if impl != () && impl.success { impl.output } else { () }, - productivity: prod.output, - autonomy: "approve_then_merge_human_supervises", -}); diff --git a/.grok/workflows/program-tick.rhai b/.grok/workflows/program-tick.rhai deleted file mode 100644 index dc6313a49..000000000 --- a/.grok/workflows/program-tick.rhai +++ /dev/null @@ -1,106 +0,0 @@ -// program-tick — thin orchestrator that FORCES the dual-track pair. -// Always plans: ci-fleet-tick + product-process-tick (unless args.skip_product true AND no waiting_ci). -// Does not merge. Does not implement. Prevents endless waiting-for-CI trap. -// Doctrine: .grok/programs/BUN-PARALLEL-DISCIPLINE.md - -let meta = #{ - name: "program-tick", - description: "Dual-track orchestrator: always pair CI fleet with product/process work (anti-wait)", - when_to_use: "Session start and every autonomous wake; replaces single-track CI babysit", - phases: [ - #{ title: "Fleet", detail: "ci-fleet-tick logic inline" }, - #{ title: "ProductProcess", detail: "product-process-tick when CI wait or always by default" }, - #{ title: "Synthesize", detail: "operator dual-track report" }, - ], -}; - -let skip_product = false; -if args != () && args.skip_product == true { - skip_product = true; -} - -phase("Fleet"); -let fleet = agent( - "You are the Console ci-fleet-tick inspector+classifier in one pass.\n" - + "Run gh pr list --state open --limit 25 --json number,title,headRefName,mergeStateStatus,headRefOid,url.\n" - + "For active PRs, check CI and Security on exact head (gh run list --branch …).\n" - + "Classify: clean_merge_ready (leader merges), waiting_ci, failed(+urls), behind, blocked.\n" - + "Set must_run_product_process_tick=true if any waiting_ci OR if nothing is clean and work remains.\n" - + "next_fleet_action must NOT be bare 'wait'.\n" - + "Do not merge. Do not edit files. Structured plain text report.\n", - #{ - label: "tick-fleet", - capability_mode: "execute", - }, -); -if fleet == () || !fleet.success { - complete(#{ ok: false, stage: "fleet", error: "fleet leg failed" }); -} - -phase("ProductProcess"); -let run_pp = true; -if skip_product == true { - run_pp = false; -} -// Always run product/process when fleet output mentions waiting — agent should set flag; we force by default true -let pp = (); -if run_pp { - let pp_prompt = ""; - pp_prompt += "You are product-process-tick. Fleet report:\n"; - pp_prompt += json_encode(fleet.output); - pp_prompt += "\n\nWhile any PR is waiting_ci, you MUST select product or process work that does not need that tip writer.\n"; - pp_prompt += "Read docs/current/PRODUCT.md HOLDs + ROADMAP.md; run bd ready | head -30.\n"; - pp_prompt += "Pick track product|process|substrate; allowlist; next_commands; holds_checked.\n"; - pp_prompt += "not_waiting_on_ci must be true — pure WAIT is ops.passive-wait and fails this tick.\n"; - pp_prompt += "If process: cite failure-classes.v1.json class and recommend /workflow process-upgrade.\n"; - pp_prompt += "If product: recommend /workflow domain-increment with lane args sketch.\n"; - pp_prompt += "Plain text dispatch brief.\n"; - pp = agent(pp_prompt, #{ - label: "tick-product-process", - capability_mode: "execute", - }); - if pp == () || !pp.success { - complete(#{ - ok: false, - stage: "product-process", - error: "product-process leg failed — do not fall back to CI-only wait", - fleet: fleet.output, - }); - } -} - -phase("Synthesize"); -let syn_prompt = ""; -syn_prompt += "Synthesize dual-track operator report.\n\nFLEET:\n"; -syn_prompt += json_encode(fleet.output); -syn_prompt += "\n\nPRODUCT/PROCESS:\n"; -if pp != () && pp.success { - syn_prompt += json_encode(pp.output); -} else { - syn_prompt += "(skipped — only valid if skip_product and no waiting_ci)\n"; -} -syn_prompt += "\n\nOutput format:\n"; -syn_prompt += "1) Fleet: merge-ready / failed / behind / waiting_ci\n"; -syn_prompt += "2) Required pair: workflows ci-fleet-tick + product-process-tick (this tick already did both if needed)\n"; -syn_prompt += "3) Next human: leader merge list\n"; -syn_prompt += "4) Next agent: exact workflow name + args\n"; -syn_prompt += "5) Anti-passive attestation: what productive work happens while CI runs\n"; -syn_prompt += "6) Tip-serial assessment: which open PRs touch documentation-manifest, ledger/, or executed-tests-baseline.json\n"; -syn_prompt += " List tip_writers count; if >=2 flag ops.tip-serial-contention and forbid opening another tip PR.\n"; -syn_prompt += "7) Wall-tax: estimate if batching pure-test PRs would cut runner hours.\n"; -syn_prompt += "8) One process improvement suggestion if any failure class repeated.\n"; -syn_prompt += "Plain text.\n"; -let syn = agent(syn_prompt, #{ - label: "tick-synthesize", - capability_mode: "read-only", -}); - -complete(#{ - ok: true, - dual_track: true, - workflows: ["ci-fleet-tick", "product-process-tick"], - fleet: fleet.output, - product_process: if pp != () && pp.success { pp.output } else { () }, - report: if syn != () && syn.success { syn.output } else { () }, - note: "Endless waiting-for-CI is a process bug; this tick forbids CI-only completion.", -}); diff --git a/.grok/workflows/ralph.rhai b/.grok/workflows/ralph.rhai deleted file mode 100644 index f5309f23c..000000000 --- a/.grok/workflows/ralph.rhai +++ /dev/null @@ -1,118 +0,0 @@ -// ralph — PRD story loop until all passes + independent review APPROVE. -// Ideas from OMC ralph ("the boulder never stops"). Grok-native. -// Soft reds remain on board. Merge via autonomy-merge policy after APPROVE. - -let meta = #{ - name: "ralph", - description: "PRD-driven execution loop: story-by-story until passes + reviewer APPROVE; hooks keep Stop looping while active", - when_to_use: "After ralplan APPROVE or well-specified task; pairs with ultragoal active-goal", - phases: [ - #{ title: "Prd", detail: "ensure task-specific acceptance criteria" }, - #{ title: "Execute", detail: "implement next failing story" }, - #{ title: "Verify", detail: "criteria + tests" }, - #{ title: "Review", detail: "independent APPROVE gate" }, - #{ title: "Learn", detail: "Hermes-style console-learn + progress.txt" }, - ], -}; - -let review_schema = #{ - "type": "object", - "required": ["verdict", "summary", "must_fix"], - "properties": #{ - "verdict": #{ "type": "string" }, - "summary": #{ "type": "string" }, - "must_fix": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -phase("Prd"); -let prd_prompt = ""; -prd_prompt += "Ralph PRD setup. Read .grok/ultragoal/prd.live.json and active-goal.live.json.\n"; -prd_prompt += "If stories empty or acceptanceCriteria are generic ('implementation complete'), REFINE into task-specific criteria.\n"; -prd_prompt += "If args.task present, build/refine stories from it.\n"; -if args != () && args.task != () { - prd_prompt += "Task: "; - prd_prompt += args.task; - prd_prompt += "\n"; -} -prd_prompt += "Write prd.live.json. Ensure console-goal activate --workflow ralph if ultragoal should loop Stop hooks.\n"; -prd_prompt += "Append progress.txt header for this run.\n"; -prd_prompt += "No PRODUCT HOLD clearance. Tip-serial: do not open tip thrash PRs if tip_writers>=2 unless serial head.\n"; -let prd = agent(prd_prompt, #{ - label: "ralph-prd", - capability_mode: "all", -}); -if prd == () || !prd.success { - complete(#{ ok: false, stage: "prd", error: "prd setup failed" }); -} - -phase("Execute"); -let ex_prompt = ""; -ex_prompt += "Pick highest-priority story with passes!=true from prd.live.json. Implement it (worktree ok).\n"; -ex_prompt += "PRD context:\n"; -ex_prompt += json_encode(prd.output); -ex_prompt += "\nRun tests for touched crates. Open/update PR if needed. Do not merge here.\n"; -ex_prompt += "If blocked by soft red on another PR, enqueue board item and still progress this story if path-disjoint.\n"; -let executed = agent(ex_prompt, #{ - label: "ralph-execute", - capability_mode: "all", - isolation_worktree: true, -}); -if executed == () || !executed.success { - complete(#{ ok: false, stage: "execute", error: "execute failed", prd: prd.output }); -} - -phase("Verify"); -let v_prompt = ""; -v_prompt += "Verify EACH acceptance criterion for the story with fresh evidence (commands/output).\n"; -v_prompt += "Execute output:\n"; -v_prompt += json_encode(executed.output); -v_prompt += "\nIf all met, set passes:true on that story in prd.live.json and append progress.txt learnings.\n"; -v_prompt += "If not met, leave passes false and list gaps — do not claim done.\n"; -let verified = agent(v_prompt, #{ - label: "ralph-verify", - capability_mode: "all", -}); -if verified == () || !verified.success { - complete(#{ ok: false, stage: "verify", error: "verify failed" }); -} - -phase("Review"); -let r_prompt = ""; -r_prompt += "Independent reviewer against SPECIFIC prd acceptance criteria (not vague done).\n"; -r_prompt += "verdict MUST be APPROVE or CHANGES_REQUESTED.\n"; -r_prompt += "Verify:\n"; -r_prompt += json_encode(verified.output); -r_prompt += "\nIf all stories passes:true and APPROVE: say ready_for_babysit_merge=true.\n"; -r_prompt += "If not all stories complete: say continue_ralph=true (boulder never stops).\n"; -let reviewed = agent(r_prompt, #{ - label: "ralph-review", - capability_mode: "execute", - output_schema: review_schema, -}); -if reviewed == () || !reviewed.success { - complete(#{ ok: false, stage: "review", error: "review failed" }); -} - -phase("Learn"); -let l_prompt = ""; -l_prompt += "Hermes-style learn. Run:\n"; -l_prompt += "`.grok/bin/console-learn from-event --id ralph-$(date +%Y%m%d) --summary '…' --classes '…'`\n"; -l_prompt += "If review not APPROVE, class ops.review-not-approve-stall.\n"; -l_prompt += "Review:\n"; -l_prompt += json_encode(reviewed.output); -l_prompt += "\nRecommend next: /workflow ralph (continue) or /workflow pr-babysit (merge path) or /workflow program-control.\n"; -l_prompt += "Do not deactivate ultragoal unless all stories pass AND quality gate met.\n"; -let learned = agent(l_prompt, #{ - label: "ralph-learn", - capability_mode: "all", -}); - -complete(#{ - ok: true, - workflow: "ralph", - review: reviewed.output, - verify: verified.output, - learn: if learned != () && learned.success { learned.output } else { () }, - boulder: "continue until prd all passes + APPROVE + Required green merge", -}); diff --git a/.grok/workflows/ralplan.rhai b/.grok/workflows/ralplan.rhai deleted file mode 100644 index 5dd61fa1f..000000000 --- a/.grok/workflows/ralplan.rhai +++ /dev/null @@ -1,172 +0,0 @@ -// ralplan — consensus planning before heavy execution (Planner → Architect → Critic). -// Ideas from OMC ralplan. Grok-native Rhai; plan pending approval unless args.execute_after_approve. -// Does not mutate product code unless args.execute_after_approve true AND critic APPROVE. - -let meta = #{ - name: "ralplan", - description: "Consensus plan: Planner + Architect + Critic until APPROVE; gate vague ralph/ultragoal execution", - when_to_use: "Vague large tasks before ralph/program-control fan-out; high-risk use args.deliberate", - phases: [ - #{ title: "Planner", detail: "principles, drivers, options, plan draft" }, - #{ title: "Architect", detail: "steelman + tradeoffs" }, - #{ title: "Critic", detail: "APPROVE|ITERATE|REJECT loop" }, - #{ title: "Finalize", detail: "write plan artifact; optional handoff to ralph" }, - ], -}; - -let task = ""; -if args != () && args.task != () { - task = args.task; -} -if task == "" && args != () && args.objective != () { - task = args.objective; -} -if task == "" { - pause("verification", "Pass args.task or args.objective for ralplan."); -} - -let deliberate = false; -if args != () && args.deliberate == true { - deliberate = true; -} -let execute_after = false; -if args != () && args.execute_after_approve == true { - execute_after = true; -} - -let plan_schema = #{ - "type": "object", - "required": ["principles", "decision_drivers", "options", "plan_summary", "acceptance_criteria"], - "properties": #{ - "principles": #{ "type": "array", "items": #{ "type": "string" } }, - "decision_drivers": #{ "type": "array", "items": #{ "type": "string" } }, - "options": #{ "type": "array", "items": #{ "type": "string" } }, - "plan_summary": #{ "type": "string" }, - "acceptance_criteria": #{ "type": "array", "items": #{ "type": "string" } }, - "premortem": #{ "type": "array", "items": #{ "type": "string" } }, - "allowlist": #{ "type": "array", "items": #{ "type": "string" } }, - "holds_checked": #{ "type": "string" }, - }, -}; - -let critic_schema = #{ - "type": "object", - "required": ["verdict", "reasons", "must_fix"], - "properties": #{ - "verdict": #{ "type": "string" }, - "reasons": #{ "type": "string" }, - "must_fix": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -phase("Planner"); -let p_prompt = ""; -p_prompt += "You are RALPLAN Planner for Console monorepo.\n"; -p_prompt += "Task:\n"; -p_prompt += task; -p_prompt += "\nRead docs/current/PRODUCT.md HOLDs and ROADMAP.md. Read .grok/programs/BUN-PARALLEL-DISCIPLINE.md.\n"; -p_prompt += "Produce RALPLAN-DR: principles (3-5), top 3 decision drivers, >=2 viable options with pros/cons,\n"; -p_prompt += "plan_summary, testable acceptance_criteria, allowlist paths, holds_checked.\n"; -if deliberate { - p_prompt += "DELIBERATE: include 3 pre-mortem scenarios and unit/integration/e2e/observability test plan notes in plan_summary.\n"; -} -p_prompt += "Planning only — no product file mutations.\n"; -let plan = agent(p_prompt, #{ - label: "ral-planner", - capability_mode: "execute", - output_schema: plan_schema, -}); -if plan == () || !plan.success { - complete(#{ ok: false, stage: "planner", error: "planner failed" }); -} - -phase("Architect"); -let a_prompt = ""; -a_prompt += "You are RALPLAN Architect. Steelman antithesis, at least one real tradeoff, synthesis.\n"; -a_prompt += "Plan:\n"; -a_prompt += json_encode(plan.output); -a_prompt += "\nFlag HOLD or tip-serial thrash risks. No product mutations.\n"; -let arch = agent(a_prompt, #{ - label: "ral-architect", - capability_mode: "execute", -}); -if arch == () || !arch.success { - complete(#{ ok: false, stage: "architect", error: "architect failed", plan: plan.output }); -} - -// Critic loop (max 3 in-workflow iterations; agents revise) -let verdict = "ITERATE"; -let critic_out = (); -let plan_out = plan.output; -let i = 0; -while i < 3 { - phase("Critic"); - let c_prompt = ""; - c_prompt += "You are RALPLAN Critic. verdict MUST be APPROVE | ITERATE | REJECT.\n"; - c_prompt += "Enforce principle-option consistency, fair alternatives, testable acceptance criteria,\n"; - c_prompt += "concrete verification, soft-red enqueue rule, no PRODUCT HOLD clearance.\n"; - c_prompt += "Plan:\n"; - c_prompt += json_encode(plan_out); - c_prompt += "\nArchitect:\n"; - c_prompt += json_encode(arch.output); - if deliberate { - c_prompt += "\nDeliberate mode: reject weak pre-mortem or missing expanded test plan.\n"; - } - critic_out = agent(c_prompt, #{ - label: "ral-critic-" + i.to_string(), - capability_mode: "execute", - output_schema: critic_schema, - }); - if critic_out == () || !critic_out.success { - complete(#{ ok: false, stage: "critic", error: "critic failed", iteration: i }); - } - verdict = critic_out.output.verdict; - if verdict == "APPROVE" { - break; - } - // revise - let r_prompt = ""; - r_prompt += "Revise plan per Critic must_fix. Keep HOLDs fail-closed.\n"; - r_prompt += json_encode(critic_out.output); - r_prompt += "\nPrior plan:\n"; - r_prompt += json_encode(plan_out); - let revised = agent(r_prompt, #{ - label: "ral-revise-" + i.to_string(), - capability_mode: "execute", - output_schema: plan_schema, - }); - if revised != () && revised.success { - plan_out = revised.output; - } - i += 1; -} - -phase("Finalize"); -let f_prompt = ""; -f_prompt += "Write plan artifact to .grok/ultragoal/plans/latest-ralplan.md (create dirs).\n"; -f_prompt += "Mark status pending approval unless execute_after_approve.\n"; -f_prompt += "Final plan:\n"; -f_prompt += json_encode(plan_out); -f_prompt += "\nCritic:\n"; -f_prompt += json_encode(critic_out.output); -f_prompt += "\nIf verdict APPROVE and execute_after: update prd.live.json stories from acceptance_criteria "; -f_prompt += "(task-specific), activate ralph via console-goal activate --workflow ralph.\n"; -f_prompt += "Else print handoff: /workflow ralph only after user or autonomy approve.\n"; -f_prompt += "execute_after_approve="; -f_prompt += if execute_after { "true" } else { "false" }; -f_prompt += " verdict="; -f_prompt += verdict; -let fin = agent(f_prompt, #{ - label: "ral-finalize", - capability_mode: "all", -}); - -complete(#{ - ok: verdict == "APPROVE", - workflow: "ralplan", - verdict: verdict, - plan: plan_out, - critic: if critic_out != () && critic_out.success { critic_out.output } else { () }, - finalize: if fin != () && fin.success { fin.output } else { () }, - next: if verdict == "APPROVE" { "/workflow ralph or /workflow program-control" } else { "revise plan / re-run ralplan" }, -}); diff --git a/.grok/workflows/ultragoal.rhai b/.grok/workflows/ultragoal.rhai deleted file mode 100644 index 7452452c7..000000000 --- a/.grok/workflows/ultragoal.rhai +++ /dev/null @@ -1,124 +0,0 @@ -// ultragoal — durable multi-story plan + native /goal handoff + activate hook loop. -// Ideas from OMC/GJC ultragoal. Grok-native: .grok/ultragoal/* + /goal + Stop hooks. -// Forbidden: omc/omx/gjc/hermes CLIs. - -let meta = #{ - name: "ultragoal", - description: "Create/activate durable ultragoal plan, print /goal handoff, arm Stop-hook loop into program-control/ralph", - when_to_use: "Large multi-session initiative; pair with ralplan then ralph; keep soft reds on board", - phases: [ - #{ title: "Brief", detail: "load brief / args objective" }, - #{ title: "Plan", detail: "stories in goals.json + optional ralplan gate" }, - #{ title: "Activate", detail: "active-goal.live.json + ledger + handoff" }, - #{ title: "Dispatch", detail: "next workflow recommendation" }, - ], -}; - -let out_schema = #{ - "type": "object", - "required": ["ok", "action", "objective", "handoff", "next_workflow", "native_goal_command"], - "properties": #{ - "ok": #{ "type": "boolean" }, - "action": #{ "type": "string" }, - "objective": #{ "type": "string" }, - "handoff": #{ "type": "string" }, - "next_workflow": #{ "type": "string" }, - "native_goal_command": #{ "type": "string" }, - "story_ids": #{ "type": "array", "items": #{ "type": "string" } }, - "ralplan_required": #{ "type": "boolean" }, - }, -}; - -let action = "status"; -if args != () && args.action != () { - action = args.action; -} -let objective = ""; -if args != () && args.objective != () { - objective = args.objective; -} -let brief_path = ".grok/ultragoal/brief.md"; -if args != () && args.brief_path != () { - brief_path = args.brief_path; -} -let dispatch_wf = "program-control"; -if args != () && args.workflow != () { - dispatch_wf = args.workflow; -} - -phase("Brief"); -let brief_prompt = ""; -brief_prompt += "Ultragoal Brief phase. action="; -brief_prompt += action; -brief_prompt += "\nRead .grok/harness/ultragoal.v1.json and .grok/ultragoal/*.\n"; -brief_prompt += "If action is status: run `.grok/bin/console-goal status` and report.\n"; -brief_prompt += "If action is create-goals or activate: read brief at "; -brief_prompt += brief_path; -brief_prompt += " and optional objective arg: "; -brief_prompt += objective; -brief_prompt += "\nRead PRODUCT HOLDs. Do not clear HOLDs.\n"; -brief_prompt += "Return plain text: objective candidate + whether ralplan is required (vague large scope).\n"; -let brief = agent(brief_prompt, #{ - label: "ug-brief", - capability_mode: "execute", -}); -if brief == () || !brief.success { - complete(#{ ok: false, stage: "brief", error: "brief failed" }); -} - -phase("Plan"); -let plan_prompt = ""; -plan_prompt += "Plan ultragoal stories into .grok/ultragoal/goals.json if action is create-goals or activate.\n"; -plan_prompt += "Brief agent output:\n"; -plan_prompt += json_encode(brief.output); -plan_prompt += "\nWrite goals as ordered list with id G00x-slug, title, objective, status pending/in_progress.\n"; -plan_prompt += "If scope is vague, set ralplan_required=true and do NOT start ralph yet.\n"; -plan_prompt += "Append ledger events via `.grok/bin/console-goal ledger-append`.\n"; -plan_prompt += "If action is status only, skip writes.\n"; -let planned = agent(plan_prompt, #{ - label: "ug-plan", - capability_mode: "all", -}); -if planned == () || !planned.success { - complete(#{ ok: false, stage: "plan", error: "plan failed", brief: brief.output }); -} - -phase("Activate"); -let act_prompt = ""; -act_prompt += "Activate ultragoal for hook loop if action is activate or create-goals.\n"; -act_prompt += "Run: `.grok/bin/console-goal activate --objective '…' --workflow "; -act_prompt += dispatch_wf; -act_prompt += "` (add --ralplan-required if needed).\n"; -act_prompt += "Then `.grok/bin/console-goal handoff`.\n"; -act_prompt += "Seed prd.live.json stories from first executable story if not ralplan-gated (task-specific acceptanceCriteria).\n"; -act_prompt += "Plan output:\n"; -act_prompt += json_encode(planned.output); -act_prompt += "\nIf action is status: only handoff if already active.\n"; -let activated = agent(act_prompt, #{ - label: "ug-activate", - capability_mode: "all", - output_schema: out_schema, -}); -if activated == () || !activated.success { - complete(#{ ok: false, stage: "activate", error: "activate failed" }); -} - -phase("Dispatch"); -let disp = ""; -disp += "Synthesize operator next steps. Include exact strings:\n"; -disp += "- native_goal_command like `/goal …`\n"; -disp += "- next_workflow like `/workflow ralplan` or `/workflow ralph` or `/workflow program-control`\n"; -disp += "Remind: Stop hook blocks while active-goal.live.json.active; Hermes learn after ticks.\n"; -disp += "Activate output:\n"; -disp += json_encode(activated.output); -let final = agent(disp, #{ - label: "ug-dispatch", - capability_mode: "execute", -}); - -complete(#{ - ok: true, - workflow: "ultragoal", - result: activated.output, - dispatch: if final != () && final.success { final.output } else { () }, -}); diff --git a/.grok/workflows/work-manager.rhai b/.grok/workflows/work-manager.rhai deleted file mode 100644 index 046aa5147..000000000 --- a/.grok/workflows/work-manager.rhai +++ /dev/null @@ -1,144 +0,0 @@ -// work-manager — discover parallel lanes + enqueue soft reds/blocks (no silence). -// Sources: Beads, ROADMAP/PRODUCT, durable goals, open PRs, Hindsight, live board. -// Does not implement product code. Does not merge. -// Doctrine: BUN-PARALLEL-DISCIPLINE + autonomy-merge.v1.json + lane-board.v1.json - -let meta = #{ - name: "work-manager", - description: "Discover parallel lanes from backlog/roadmap/beads/hindsight and enqueue soft reds/blocks — never silent-drop", - when_to_use: "Every program-control tick; start of autonomous drive; when board may be stale", - phases: [ - #{ title: "Inventory", detail: "authority + beads + open PRs + hindsight + live board" }, - #{ title: "SoftRedsBlocks", detail: "every soft red and block becomes a board item" }, - #{ title: "Lanes", detail: "path-disjoint ready lanes from roadmap/beads" }, - #{ title: "WriteBoard", detail: "update lane-board.live.json + optional beads creates" }, - ], -}; - -let board_schema = #{ - "type": "object", - "required": [ - "items_added", - "items_updated", - "soft_reds_enqueued", - "blocks_enqueued", - "parallel_ready", - "serialized_tip_queue", - "silence_check_ok", - "summary", - ], - "properties": #{ - "items_added": #{ "type": "array", "items": #{ "type": "string" } }, - "items_updated": #{ "type": "array", "items": #{ "type": "string" } }, - "soft_reds_enqueued": #{ "type": "array", "items": #{ "type": "string" } }, - "blocks_enqueued": #{ "type": "array", "items": #{ "type": "string" } }, - "parallel_ready": #{ "type": "array", "items": #{ "type": "string" } }, - "serialized_tip_queue": #{ "type": "array", "items": #{ "type": "string" } }, - "silence_check_ok": #{ "type": "boolean" }, - "summary": #{ "type": "string" }, - "next_implement_ids": #{ "type": "array", "items": #{ "type": "string" } }, - "next_babysit_prs": #{ "type": "array", "items": #{ "type": "string" } }, - }, -}; - -phase("Inventory"); -let inv_prompt = ""; -inv_prompt += "You are Console work-manager Inventory. Use tools (shell, read_file, gh, bd, hindsight MCP if available).\n"; -inv_prompt += "Collect and report as plain structured text:\n"; -inv_prompt += "1) docs/current/PRODUCT.md HOLDs (list exact HOLD lines).\n"; -inv_prompt += "2) docs/current/ROADMAP.md ordered work (summarize open items).\n"; -inv_prompt += "3) .grok/programs/ROADMAP-DURABLE-GOAL.md G001–G009 status hints.\n"; -inv_prompt += "4) bd ready + bd list --status=open + bd list --status=in_progress (run bd prime first if needed).\n"; -inv_prompt += "5) gh pr list --state open --limit 30 --json number,title,headRefName,mergeable,mergeStateStatus,headRefOid,url.\n"; -inv_prompt += "6) For each open PR: Required CI + Security + notable fail/pending checks (gh pr checks N).\n"; -inv_prompt += "7) Read .grok/harness/lane-board.live.json and .grok/harness/lane-board.v1.json soft_red_sources.\n"; -inv_prompt += "8) Hindsight recall if available: query 'console tip serial soft red CI fail process blockers'.\n"; -inv_prompt += "9) Tip-serial writers: PRs touching documentation-manifest, executed-tests-baseline, ci.yml, authority tip.\n"; -inv_prompt += "Do NOT edit product code. You MAY draft board updates in the next phases.\n"; -let inventory = agent(inv_prompt, #{ - label: "wm-inventory", - capability_mode: "execute", -}); -if inventory == () || !inventory.success { - complete(#{ ok: false, stage: "inventory", error: "inventory failed" }); -} - -phase("SoftRedsBlocks"); -let soft_prompt = ""; -soft_prompt += "From this inventory, enumerate EVERY soft red and EVERY hard/soft block. SILENCE IS FORBIDDEN.\n\n"; -soft_prompt += json_encode(inventory.output); -soft_prompt += "\n\nRead soft_red_sources and hard_block_sources from .grok/harness/lane-board.v1.json.\n"; -soft_prompt += "For each observation produce a board item draft:\n"; -soft_prompt += "- source_key: stable (e.g. soft_red:pr:591:auth_bootstrap_fail, block:pr:592:conflicting)\n"; -soft_prompt += "- kind: soft_red | hard_block | fleet_fix | review_fix\n"; -soft_prompt += "- title, priority P0-P2, pr_number if any, evidence (check URLs), status ready or fixing\n"; -soft_prompt += "- If already on live board with same source_key, mark update not duplicate silent skip.\n"; -soft_prompt += "Include: BEHIND, DIRTY, CONFLICTING, auth fail, preflight fail, non-required reds, cancelled runs,"; -soft_prompt += " stale waiting_ci, tip_writers>=2, unsigned tip, review not approve, beads blocked without owner.\n"; -soft_prompt += "Return a clear list. No empty claim without stating zero after exhaustive check.\n"; -let softs = agent(soft_prompt, #{ - label: "wm-soft-reds", - capability_mode: "execute", -}); -if softs == () || !softs.success { - complete(#{ ok: false, stage: "soft-reds", error: "soft red enumeration failed", inventory: inventory.output }); -} - -phase("Lanes"); -let lanes_prompt = ""; -lanes_prompt += "Propose path-disjoint parallel READY lanes from beads/roadmap that do NOT thrash tip-serial files "; -lanes_prompt += "while tip queue is non-empty. Prefer: pure domain tests on disjoint crates (batch), process allowlist, "; -lanes_prompt += "read-only audit, residual Buck design only if no other ci.yml writer.\n\n"; -lanes_prompt += "Inventory:\n"; -lanes_prompt += json_encode(inventory.output); -lanes_prompt += "\n\nSoft reds/blocks (must stay higher priority than new product fan-out):\n"; -lanes_prompt += json_encode(softs.output); -lanes_prompt += "\n\nRules from BUN-PARALLEL-DISCIPLINE: serialize tip/manifest/baseline/ci.yml; parallel path-disjoint crates.\n"; -lanes_prompt += "PRODUCT HOLDs fail-closed. Max 4 new product ready items unless tip_writers==0.\n"; -lanes_prompt += "Each lane: id, kind product|process|substrate, allowlist, forbidden, beads_id or goal_id, status ready.\n"; -let lanes = agent(lanes_prompt, #{ - label: "wm-lanes", - capability_mode: "execute", -}); -if lanes == () || !lanes.success { - complete(#{ ok: false, stage: "lanes", error: "lane planning failed" }); -} - -phase("WriteBoard"); -let write_prompt = ""; -write_prompt += "Update the Console lane board. SILENCE CHECK is mandatory.\n\n"; -write_prompt += "Soft reds/blocks drafts:\n"; -write_prompt += json_encode(softs.output); -write_prompt += "\n\nParallel lanes drafts:\n"; -write_prompt += json_encode(lanes.output); -write_prompt += "\n\nActions:\n"; -write_prompt += "1) Read .grok/harness/lane-board.live.json.\n"; -write_prompt += "2) Merge items by source_key (upsert). Never drop an observed soft red/block without writing it.\n"; -write_prompt += "3) Write the full updated JSON to .grok/harness/lane-board.live.json (valid JSON, version 1.0.0, updated_at ISO-ish, updated_by work-manager).\n"; -write_prompt += "4) Optionally bd create for new durable soft_red/block items not yet in beads (type=task, priority by P0/P1).\n"; -write_prompt += "5) silence_check_ok=true only if every soft red/block from step SoftReds is present on the board after write.\n"; -write_prompt += "6) next_implement_ids: ready items safe to implement (not tip-serial thrash).\n"; -write_prompt += "7) next_babysit_prs: PR numbers needing babysit/fix/review.\n"; -write_prompt += "Do not merge PRs. Do not expand PRODUCT HOLDs.\n"; -let written = agent(write_prompt, #{ - label: "wm-write-board", - capability_mode: "all", - output_schema: board_schema, -}); -if written == () || !written.success { - complete(#{ ok: false, stage: "write-board", error: "board write failed", softs: softs.output, lanes: lanes.output }); -} -if written.output.silence_check_ok != true { - complete(#{ - ok: false, - stage: "silence-check", - error: "soft reds/blocks would be silent — board write rejected", - board: written.output, - }); -} - -complete(#{ - ok: true, - workflow: "work-manager", - board: written.output, -}); From 62fc79ac8b69ff2b862ebbf81e49ec7b4125b845 Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Thu, 20 Aug 2026 08:07:19 -0400 Subject: [PATCH 2/7] chore(agents): stop tracking agent directories .claude (9), .codex (2), .cursor (53) and .beads (10) are untracked but LEFT ON DISK: agent tools read those paths from fixed locations, so deleting them would break local tooling for no gain. The requirement is that they not reach GitHub. Preserved at refs/preserved/{claude,codex,cursor,beads} before untracking. .beads carried executable hooks (post-checkout, pre-commit); those are the kind of load-bearing content that should not live in an agent directory at all. They remain on disk and are unaffected, but wiring them from a tracked location is follow-up work. Co-Authored-By: Claude Opus 5 (1M context) --- .beads/.gitignore | 77 - .beads/README.md | 81 - .beads/config.yaml | 70 - .beads/hooks/post-checkout | 33 - .beads/hooks/post-merge | 33 - .beads/hooks/pre-commit | 33 - .beads/hooks/pre-push | 33 - .beads/hooks/prepare-commit-msg | 33 - .beads/interactions.jsonl | 0 .beads/metadata.json | 7 - .claude/settings.json | 15 - .claude/workflows/backlog-audit.js | 690 --------- .claude/workflows/lane-fanout.js | 1154 -------------- .claude/workflows/lane-fanout.test.mjs | 1358 ----------------- .claude/workflows/program-tick.js | 435 ------ .claude/workflows/review-gate.js | 137 -- .claude/workflows/scout.js | 499 ------ .claude/workflows/slice.js | 528 ------- .claude/workflows/stale-take-audit.js | 243 --- .codex/config.toml | 2 - .codex/hooks.json | 51 - .cursor/PROCESS-SYNTHESIS.md | 79 - .cursor/agents/lane-critic.md | 47 - .cursor/agents/lane-implementer.md | 44 - .cursor/failure-classes-2026-08-10.md | 36 - .cursor/hooks.json | 37 - .cursor/hooks/cargo-scope-enforcer.sh | 40 - .cursor/hooks/forge-destructive-guard.sh | 67 - .cursor/hooks/git-lock-enforcer.sh | 142 -- .cursor/hooks/no-mm-role.sh | 28 - .cursor/hooks/push-admission.sh | 40 - .cursor/hooks/session-start.sh | 25 - .cursor/hooks/stop-receipt-gate.sh | 46 - .cursor/permissions.json | 13 - .cursor/receipts/09c-critic.json | 91 -- .cursor/receipts/09c.json | 55 - .cursor/receipts/5yn-pod-prefix-20260811.json | 59 - .cursor/receipts/6pl.json | 30 - .cursor/receipts/7sx-critic.json | 95 -- .cursor/receipts/7sx.json | 42 - .cursor/receipts/9sxn.json | 52 - .cursor/receipts/9ze.json | 64 - .cursor/receipts/ae5.json | 79 - .cursor/receipts/b4z-critic.json | 48 - .cursor/receipts/b4z.json | 67 - .../buck2-cas-everywhere-20260811.json | 64 - .cursor/receipts/console-8ma-cas-warm.json | 102 -- .cursor/receipts/cursor-worktree-layout.json | 37 - .cursor/receipts/dgo.2-critic.json | 52 - .cursor/receipts/dgo.2.json | 38 - .cursor/receipts/g14a.json | 65 - .cursor/receipts/i91.json | 72 - .cursor/receipts/pr-829-attestation.json | 17 - .cursor/receipts/q06.json | 43 - .cursor/receipts/tai.1.json | 24 - .cursor/receipts/uoh.json | 20 - ...-receipt-validator-20260812-critic-r3.json | 57 - ...ane-receipt-validator-20260812-critic.json | 81 - .../v-lane-receipt-validator-20260812.json | 75 - ...2-pilot-t2-g004-20260813-critic-final.json | 21 - .../receipts/w2-pilot-t2-g004-20260813.json | 58 - .cursor/receipts/wave1-20260810-critic.json | 41 - .../receipts/wave4-admit-writer-claim.json | 48 - .cursor/receipts/wave4-inventory.json | 35 - .cursor/receipts/we1-critic.json | 53 - .cursor/receipts/we1.json | 48 - .cursor/receipts/wnv.json | 59 - .cursor/receipts/xxp-critic.json | 96 -- .cursor/receipts/xxp.json | 49 - .cursor/receipts/y0n.json | 48 - .cursor/receipts/zd7.json | 58 - .cursor/rules/console-agent-ritual.mdc | 39 - .cursor/rules/console-base-lock.mdc | 70 - .cursor/rules/console-standing-lenses.mdc | 34 - .gitignore | 2 +- 75 files changed, 1 insertion(+), 8343 deletions(-) delete mode 100644 .beads/.gitignore delete mode 100644 .beads/README.md delete mode 100644 .beads/config.yaml delete mode 100755 .beads/hooks/post-checkout delete mode 100755 .beads/hooks/post-merge delete mode 100755 .beads/hooks/pre-commit delete mode 100755 .beads/hooks/pre-push delete mode 100755 .beads/hooks/prepare-commit-msg delete mode 100644 .beads/interactions.jsonl delete mode 100644 .beads/metadata.json delete mode 100644 .claude/settings.json delete mode 100644 .claude/workflows/backlog-audit.js delete mode 100644 .claude/workflows/lane-fanout.js delete mode 100644 .claude/workflows/lane-fanout.test.mjs delete mode 100644 .claude/workflows/program-tick.js delete mode 100644 .claude/workflows/review-gate.js delete mode 100644 .claude/workflows/scout.js delete mode 100644 .claude/workflows/slice.js delete mode 100644 .claude/workflows/stale-take-audit.js delete mode 100644 .codex/config.toml delete mode 100644 .codex/hooks.json delete mode 100644 .cursor/PROCESS-SYNTHESIS.md delete mode 100644 .cursor/agents/lane-critic.md delete mode 100644 .cursor/agents/lane-implementer.md delete mode 100644 .cursor/failure-classes-2026-08-10.md delete mode 100644 .cursor/hooks.json delete mode 100755 .cursor/hooks/cargo-scope-enforcer.sh delete mode 100755 .cursor/hooks/forge-destructive-guard.sh delete mode 100755 .cursor/hooks/git-lock-enforcer.sh delete mode 100755 .cursor/hooks/no-mm-role.sh delete mode 100755 .cursor/hooks/push-admission.sh delete mode 100755 .cursor/hooks/session-start.sh delete mode 100755 .cursor/hooks/stop-receipt-gate.sh delete mode 100644 .cursor/permissions.json delete mode 100644 .cursor/receipts/09c-critic.json delete mode 100644 .cursor/receipts/09c.json delete mode 100644 .cursor/receipts/5yn-pod-prefix-20260811.json delete mode 100644 .cursor/receipts/6pl.json delete mode 100644 .cursor/receipts/7sx-critic.json delete mode 100644 .cursor/receipts/7sx.json delete mode 100644 .cursor/receipts/9sxn.json delete mode 100644 .cursor/receipts/9ze.json delete mode 100644 .cursor/receipts/ae5.json delete mode 100644 .cursor/receipts/b4z-critic.json delete mode 100644 .cursor/receipts/b4z.json delete mode 100644 .cursor/receipts/buck2-cas-everywhere-20260811.json delete mode 100644 .cursor/receipts/console-8ma-cas-warm.json delete mode 100644 .cursor/receipts/cursor-worktree-layout.json delete mode 100644 .cursor/receipts/dgo.2-critic.json delete mode 100644 .cursor/receipts/dgo.2.json delete mode 100644 .cursor/receipts/g14a.json delete mode 100644 .cursor/receipts/i91.json delete mode 100644 .cursor/receipts/pr-829-attestation.json delete mode 100644 .cursor/receipts/q06.json delete mode 100644 .cursor/receipts/tai.1.json delete mode 100644 .cursor/receipts/uoh.json delete mode 100644 .cursor/receipts/v-lane-receipt-validator-20260812-critic-r3.json delete mode 100644 .cursor/receipts/v-lane-receipt-validator-20260812-critic.json delete mode 100644 .cursor/receipts/v-lane-receipt-validator-20260812.json delete mode 100644 .cursor/receipts/w2-pilot-t2-g004-20260813-critic-final.json delete mode 100644 .cursor/receipts/w2-pilot-t2-g004-20260813.json delete mode 100644 .cursor/receipts/wave1-20260810-critic.json delete mode 100644 .cursor/receipts/wave4-admit-writer-claim.json delete mode 100644 .cursor/receipts/wave4-inventory.json delete mode 100644 .cursor/receipts/we1-critic.json delete mode 100644 .cursor/receipts/we1.json delete mode 100644 .cursor/receipts/wnv.json delete mode 100644 .cursor/receipts/xxp-critic.json delete mode 100644 .cursor/receipts/xxp.json delete mode 100644 .cursor/receipts/y0n.json delete mode 100644 .cursor/receipts/zd7.json delete mode 100644 .cursor/rules/console-agent-ritual.mdc delete mode 100644 .cursor/rules/console-base-lock.mdc delete mode 100644 .cursor/rules/console-standing-lenses.mdc diff --git a/.beads/.gitignore b/.beads/.gitignore deleted file mode 100644 index f7738583f..000000000 --- a/.beads/.gitignore +++ /dev/null @@ -1,77 +0,0 @@ -# Dolt database (managed by Dolt, not git) -dolt/ -embeddeddolt/ -proxieddb/ - -# Runtime files -bd.sock -bd.sock.startlock -sync-state.json -last-touched -.exclusive-lock - -# Daemon runtime (lock, log, pid) -daemon.* - -# Push state (runtime, per-machine) -push-state.json - -# Lock files (various runtime locks) -*.lock - -# Credential key (encryption key for federation peer auth — never commit) -.beads-credential-key - -# Local version tracking (prevents upgrade notification spam after git ops) -.local_version - -proxied_server_client_info.json - -# Worktree redirect file (contains relative path to main repo's .beads/) -# Must not be committed as paths would be wrong in other clones -redirect - -# Sync state (local-only, per-machine) -# These files are machine-specific and should not be shared across clones -.sync.lock -export-state/ -export-state.json -last_pull - -# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned) -ephemeral.sqlite3 -ephemeral.sqlite3-journal -ephemeral.sqlite3-wal -ephemeral.sqlite3-shm - -# Dolt server management (auto-started by bd) -dolt-server.pid -dolt-server.log -dolt-server.lock -dolt-server.port -dolt-server.activity - -# Debug-mode pprof artifacts (written when dolt.debug: true in config.yaml) -dolt-pprof/ - -# Corrupt backup directories (created by bd doctor --fix recovery) -*.corrupt.backup/ - -# Backup data (auto-exported JSONL, local-only) -backup/ - -# Per-project environment file (Dolt connection config, GH#2520) -.env - -# Legacy files (from pre-Dolt versions) -*.db -*.db?* -*.db-journal -*.db-wal -*.db-shm -db.sqlite -bd.db -# NOTE: Do NOT add negation patterns here. -# They would override fork protection in .git/info/exclude. -# Config files (metadata.json, config.yaml) are tracked by git by default -# since no pattern above ignores them. diff --git a/.beads/README.md b/.beads/README.md deleted file mode 100644 index 63e8f4c23..000000000 --- a/.beads/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Beads - AI-Native Issue Tracking - -Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code. - -## What is Beads? - -Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git. - -**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads) - -## Quick Start - -### Essential Commands - -```bash -# Create new issues -bd create "Add user authentication" - -# View all issues -bd list - -# View issue details -bd show - -# Update issue status -bd update --claim -bd update --status done - -# Sync with Dolt remote -bd dolt push -``` - -### Working with Issues - -Issues in Beads are: -- **Git-native**: Stored in Dolt database with version control and branching -- **AI-friendly**: CLI-first design works perfectly with AI coding agents -- **Branch-aware**: Issues can follow your branch workflow -- **Sync-ready**: Uses Dolt remotes for backup and team sharing - -## Why Beads? - -✨ **AI-Native Design** -- Built specifically for AI-assisted development workflows -- CLI-first interface works seamlessly with AI coding agents -- No context switching to web UIs - -🚀 **Developer Focused** -- Issues live in your repo, right next to your code -- Works offline, syncs when you push -- Fast, lightweight, and stays out of your way - -🔧 **Git Integration** -- Dolt-native sync via bd dolt push / bd dolt pull -- Branch-aware issue tracking -- Dolt-native three-way merge resolution - -## Get Started with Beads - -Try Beads in your own projects: - -```bash -# Install Beads -curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash - -# Initialize in your repo -bd init - -# Create your first issue -bd create "Try out Beads" -``` - -## Learn More - -- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs) -- **Quick Start Guide**: Run `bd quickstart` -- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples) - ---- - -*Beads: Issue tracking that moves at the speed of thought* ⚡ diff --git a/.beads/config.yaml b/.beads/config.yaml deleted file mode 100644 index ddd52d8ae..000000000 --- a/.beads/config.yaml +++ /dev/null @@ -1,70 +0,0 @@ -# Beads Configuration File -# This file configures default behavior for all bd commands in this repository -# All settings can also be set via environment variables (BD_* prefix) -# or overridden with command-line flags - -# Issue prefix for this repository (used by bd init) -# If not set, bd init will auto-detect from directory name -# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. -# issue-prefix: "" - -# Use no-db mode: JSONL-only, no Dolt database -# When true, .beads/issues.jsonl is the only local store -# no-db: false - -# Enable JSON output by default -# json: false - -# Feedback title formatting for mutating commands (create/update/close/dep/edit) -# 0 = hide titles, N > 0 = truncate to N characters -# output: -# title-length: 255 - -# Default actor for audit trails (overridden by BEADS_ACTOR or --actor) -# actor: "" - -# Export events (audit trail) to .beads/events.jsonl on each flush/sync -# When enabled, new events are appended incrementally using a high-water mark. -# Use 'bd export --events' to trigger manually regardless of this setting. -# events-export: false - -# Multi-repo configuration (experimental - bd-307) -# Allows hydrating from multiple repositories and routing writes to the correct database -# repos: -# primary: "." # Primary repo (where this database lives) -# additional: # Additional repos to hydrate from (read-only) -# - ~/beads-planning # Personal planning repo -# - ~/work-planning # Work planning repo - -# Dolt-native backup (periodic backup for off-machine recovery) -# This is full database backup only. Cross-machine sync uses Dolt remotes. -# backup: -# enabled: false # Disable auto-backup entirely -# interval: 15m # Minimum time between auto-backups -# git-push: false # Disable git push (backup locally only) -# git-repo: "" # Separate git repo for backups (default: project repo) - -# Optional JSONL auto-export for viewers, interchange, and issue-level migration. -# Disabled by default; enable only when an integration needs fresh .beads/issues.jsonl. -# Use relative paths under .beads/ for JSONL import/export filenames. -# export: -# auto: false -# path: issues.jsonl -# interval: 60s -# git-add: false -# import: -# path: issues.jsonl - -# Integration settings (access with 'bd config get/set') -# Non-secret keys (stored in the database): -# - jira.url, jira.project -# - linear.team_id -# - github.org, github.repo -# -# Secret keys (stored in this file but prefer env vars to avoid git exposure): -# - linear.api_key → use LINEAR_API_KEY env var instead -# - github.token → use GITHUB_TOKEN env var instead - -sync.remote: "git+https://github.com/jason931225/console.git" -export: - auto: true diff --git a/.beads/hooks/post-checkout b/.beads/hooks/post-checkout deleted file mode 100755 index 42cc454b0..000000000 --- a/.beads/hooks/post-checkout +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh -# --- BEGIN BEADS INTEGRATION v1.1.2 --- -# This section is managed by beads. Do not remove these markers. -if command -v bd >/dev/null 2>&1; then - export BD_GIT_HOOK=1 - _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} - _bd_used_perl=0 - if command -v timeout >/dev/null 2>&1; then - timeout "$_bd_timeout" bd hooks run post-checkout "$@" - _bd_exit=$? - elif command -v gtimeout >/dev/null 2>&1; then - gtimeout "$_bd_timeout" bd hooks run post-checkout "$@" - _bd_exit=$? - elif command -v perl >/dev/null 2>&1; then - _bd_used_perl=1 - perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run post-checkout "$@" - _bd_exit=$? - else - echo >&2 "beads: hook 'post-checkout' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" - bd hooks run post-checkout "$@" - _bd_exit=$? - fi - if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then - echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s — continuing without beads" - _bd_exit=0 - fi - if [ $_bd_exit -eq 3 ]; then - echo >&2 "beads: database not initialized — skipping hook 'post-checkout'" - _bd_exit=0 - fi - if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi -fi -# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/post-merge b/.beads/hooks/post-merge deleted file mode 100755 index 37bdb30c5..000000000 --- a/.beads/hooks/post-merge +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh -# --- BEGIN BEADS INTEGRATION v1.1.2 --- -# This section is managed by beads. Do not remove these markers. -if command -v bd >/dev/null 2>&1; then - export BD_GIT_HOOK=1 - _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} - _bd_used_perl=0 - if command -v timeout >/dev/null 2>&1; then - timeout "$_bd_timeout" bd hooks run post-merge "$@" - _bd_exit=$? - elif command -v gtimeout >/dev/null 2>&1; then - gtimeout "$_bd_timeout" bd hooks run post-merge "$@" - _bd_exit=$? - elif command -v perl >/dev/null 2>&1; then - _bd_used_perl=1 - perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run post-merge "$@" - _bd_exit=$? - else - echo >&2 "beads: hook 'post-merge' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" - bd hooks run post-merge "$@" - _bd_exit=$? - fi - if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then - echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s — continuing without beads" - _bd_exit=0 - fi - if [ $_bd_exit -eq 3 ]; then - echo >&2 "beads: database not initialized — skipping hook 'post-merge'" - _bd_exit=0 - fi - if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi -fi -# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/pre-commit b/.beads/hooks/pre-commit deleted file mode 100755 index 952c254cc..000000000 --- a/.beads/hooks/pre-commit +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh -# --- BEGIN BEADS INTEGRATION v1.1.2 --- -# This section is managed by beads. Do not remove these markers. -if command -v bd >/dev/null 2>&1; then - export BD_GIT_HOOK=1 - _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} - _bd_used_perl=0 - if command -v timeout >/dev/null 2>&1; then - timeout "$_bd_timeout" bd hooks run pre-commit "$@" - _bd_exit=$? - elif command -v gtimeout >/dev/null 2>&1; then - gtimeout "$_bd_timeout" bd hooks run pre-commit "$@" - _bd_exit=$? - elif command -v perl >/dev/null 2>&1; then - _bd_used_perl=1 - perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run pre-commit "$@" - _bd_exit=$? - else - echo >&2 "beads: hook 'pre-commit' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" - bd hooks run pre-commit "$@" - _bd_exit=$? - fi - if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then - echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads" - _bd_exit=0 - fi - if [ $_bd_exit -eq 3 ]; then - echo >&2 "beads: database not initialized — skipping hook 'pre-commit'" - _bd_exit=0 - fi - if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi -fi -# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/pre-push b/.beads/hooks/pre-push deleted file mode 100755 index 6a61e9d01..000000000 --- a/.beads/hooks/pre-push +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh -# --- BEGIN BEADS INTEGRATION v1.1.2 --- -# This section is managed by beads. Do not remove these markers. -if command -v bd >/dev/null 2>&1; then - export BD_GIT_HOOK=1 - _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} - _bd_used_perl=0 - if command -v timeout >/dev/null 2>&1; then - timeout "$_bd_timeout" bd hooks run pre-push "$@" - _bd_exit=$? - elif command -v gtimeout >/dev/null 2>&1; then - gtimeout "$_bd_timeout" bd hooks run pre-push "$@" - _bd_exit=$? - elif command -v perl >/dev/null 2>&1; then - _bd_used_perl=1 - perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run pre-push "$@" - _bd_exit=$? - else - echo >&2 "beads: hook 'pre-push' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" - bd hooks run pre-push "$@" - _bd_exit=$? - fi - if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then - echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads" - _bd_exit=0 - fi - if [ $_bd_exit -eq 3 ]; then - echo >&2 "beads: database not initialized — skipping hook 'pre-push'" - _bd_exit=0 - fi - if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi -fi -# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/prepare-commit-msg b/.beads/hooks/prepare-commit-msg deleted file mode 100755 index d5b13ffc2..000000000 --- a/.beads/hooks/prepare-commit-msg +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh -# --- BEGIN BEADS INTEGRATION v1.1.2 --- -# This section is managed by beads. Do not remove these markers. -if command -v bd >/dev/null 2>&1; then - export BD_GIT_HOOK=1 - _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} - _bd_used_perl=0 - if command -v timeout >/dev/null 2>&1; then - timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@" - _bd_exit=$? - elif command -v gtimeout >/dev/null 2>&1; then - gtimeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@" - _bd_exit=$? - elif command -v perl >/dev/null 2>&1; then - _bd_used_perl=1 - perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run prepare-commit-msg "$@" - _bd_exit=$? - else - echo >&2 "beads: hook 'prepare-commit-msg' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" - bd hooks run prepare-commit-msg "$@" - _bd_exit=$? - fi - if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then - echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s — continuing without beads" - _bd_exit=0 - fi - if [ $_bd_exit -eq 3 ]; then - echo >&2 "beads: database not initialized — skipping hook 'prepare-commit-msg'" - _bd_exit=0 - fi - if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi -fi -# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl deleted file mode 100644 index e69de29bb..000000000 diff --git a/.beads/metadata.json b/.beads/metadata.json deleted file mode 100644 index c4e9046d4..000000000 --- a/.beads/metadata.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "database": "dolt", - "backend": "dolt", - "dolt_mode": "embedded", - "dolt_database": "console", - "project_id": "765ab4fa-2eb2-4e59-9377-ebabe8f748db" -} \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index c6907bfb0..000000000 --- a/.claude/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "command": "bd prime --hook-json", - "type": "command" - } - ], - "matcher": "" - } - ] - } -} \ No newline at end of file diff --git a/.claude/workflows/backlog-audit.js b/.claude/workflows/backlog-audit.js deleted file mode 100644 index 22bdc1b45..000000000 --- a/.claude/workflows/backlog-audit.js +++ /dev/null @@ -1,690 +0,0 @@ -export const meta = { - name: 'backlog-audit', - description: 'Audit the existing codebase by capability domain, triage every open GitHub issue against the current tree, and reconcile both into one backlog — findings become beads, dead issues get closed with evidence', - whenToUse: 'Periodically, when the issue tracker and the working backlog have drifted apart, or before planning a phase. NOT for reviewing a PR — that is lane-fanout.', - phases: [ - { title: 'Collect', detail: 'one agent gathers raw state; classification is done in-script' }, - { title: 'Read', detail: 'ONE wave: per-domain audit + cross-cutting sweeps + issue triage, all read-only' }, - { title: 'Reconcile', detail: 'single writer: create beads, close issues, sync the two' }, - ], -} - -// --------------------------------------------------------------------------- -// args = { -// repo: "/abs/path" // the checkout to audit -// ghRepo: "owner/name" // for gh issue operations -// ref?: "branch-or-sha" // what to audit; defaults to the checkout's HEAD -// domains?: [{ key, crates:[...], why }] // capability chunks; defaults below. -// Whatever this list is, any crate the census finds and no domain -// claims gets its own lane — coverage is checked, not assumed. -// issueBatch?: 8 // issues per triage agent; smaller = more lanes = same wall-clock -// apply?: false // false = report only. TRUE = actually mutate beads and issues. -// defaultBranch?: "origin/main" // what a CLOSE-* verdict's evidence must be reachable from -// } -// -// WHY THIS IS SEPARATE FROM lane-fanout: that harness reviews a DIFF against a tip, which is the -// right shape for work in flight and the wrong shape for standing code. Most defects in a mature -// tree are not in any recent diff — they are in the parts nobody has looked at since they landed. -// This one reads the tree as it stands, in chunks a reviewer can actually hold. -// -// THE DISCIPLINE THIS FILE EXISTS TO ENFORCE, learned the expensive way in this programme: -// - A finding without file:line evidence is an opinion. It does not become a bead. -// - An issue is NEVER closed on a guess. "Probably fixed" is not a verdict; the closing comment -// must name the commit, the test, or the code that makes it moot. -// - Wrongly closing a real issue is far worse than leaving a stale one open, because the stale one -// is visible and the closed one is not. When uncertain, the verdict is KEEP with a note. -// - One writer performs every mutation. Beads and gh are shared state; concurrent writers race. -// --------------------------------------------------------------------------- - -let ARGS = args -if (typeof ARGS === 'string') { - try { ARGS = JSON.parse(ARGS) } catch (e) { - throw new Error(`backlog-audit: args arrived as a string that is not valid JSON: ${e.message}`) - } -} -ARGS = ARGS || {} - -// An option this workflow does not read must abort rather than be silently dropped — the same rule -// lane-fanout learned from a sibling runner where an ignored option cost six lanes. -const KNOWN_ARGS = ['repo', 'ghRepo', 'ref', 'domains', 'issueBatch', 'apply', 'defaultBranch'] -{ - const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) - if (unknown.length) { - throw new Error( - `backlog-audit: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`, - ) - } -} - -const REPO = ARGS.repo -const GH = ARGS.ghRepo -const REF = ARGS.ref || 'HEAD' -// `|| 8` rescues 0 and undefined and NOTHING else: -1 yields negative-stride batching that produces -// no batches at all, and 1.5 yields overlapping slices that triage the same issue twice under two -// agents. Both exit cleanly, which is what makes them worth refusing here. -const BATCH = ARGS.issueBatch === undefined ? 8 : ARGS.issueBatch -if (!Number.isInteger(BATCH) || BATCH < 1) { - throw new Error(`backlog-audit: issueBatch must be a positive integer; got ${JSON.stringify(ARGS.issueBatch)}`) -} -const APPLY = ARGS.apply === true -// What "landed" means. An issue is closed against the branch everyone else will pull, not against -// whatever branch the evidence happens to sit on. -const DEFAULT_REF = ARGS.defaultBranch || 'origin/main' - -if (!REPO) throw new Error('backlog-audit: args.repo is required') -if (!GH) throw new Error('backlog-audit: args.ghRepo is required (owner/name)') - -const SAFETY = [ - '=== READ-ONLY UNLESS TOLD OTHERWISE ===', - APPLY - ? 'apply=TRUE. The Reconcile phase — and ONLY that phase — may mutate beads and GitHub issues.' - : 'apply=FALSE. NOTHING may be mutated. No bd create/close/update, no gh issue close/comment.', - 'No phase may edit source, commit, push, or open a PR. This workflow reports and files; it does', - 'not fix. A finding that is trivially fixable is still a finding — fixing it here would put an', - 'unreviewed change into a tree nobody is watching.', - '', - '=== EVIDENCE RULES, non-negotiable ===', - '1. Every finding carries file:line and the command that produced it. A claim you did not run is', - ' an opinion, and opinions do not become backlog items.', - '2. State what you SEARCHED for as well as what you found. A null result from the wrong query has', - ' twice become a written "not established" finding in this programme.', - '3. Beware the greps that silently match nothing here: `git grep -E` does NOT support \\b (POSIX', - ' ERE), and a trailing \\b makes the whole pattern match zero lines while exiting cleanly. That', - ' produced a false-clean writer census for a table with five known writers. Validate any regex', - ' against a case you KNOW matches before trusting a zero.', - '4. Stale worktrees are not the tree. This checkout contains abandoned worktrees under .worktrees/', - ' and .omx/team/**; five separate diagnostic bursts in this programme traced to them. Scope every', - ' search to the real source roots and say which roots you used.', -].join('\n') - -const FINDING_SCHEMA = { - type: 'object', - required: ['domain', 'findings', 'coverage'], - properties: { - domain: { type: 'string' }, - coverage: { type: 'string', description: 'what you actually read, and what you did NOT get to — an honest gap beats a claimed sweep' }, - findings: { - type: 'array', - items: { - type: 'object', - required: ['title', 'severity', 'evidence', 'failureScenario', 'provenByExecution'], - properties: { - title: { type: 'string', description: 'imperative and specific, as a backlog item should read' }, - severity: { type: 'string', enum: ['blocker', 'major', 'minor', 'nit'] }, - evidence: { type: 'string', description: 'file:line plus the command and its real output' }, - failureScenario: { type: 'string', description: 'concrete inputs or state -> wrong outcome. Not "could be unsafe".' }, - provenByExecution: { type: 'boolean', description: 'TRUE only if you RAN something and observed it. Reasoning from source is FALSE.' }, - suggestedFix: { type: 'string' }, - existingIssue: { type: 'string', description: 'an existing GitHub issue or bead this duplicates, if any — check BEFORE filing' }, - }, - }, - }, - strengths: { type: 'string', description: 'controls that are genuinely load-bearing, so a later reader does not "simplify" them away' }, - }, -} - -const TRIAGE_SCHEMA = { - type: 'object', - required: ['verdicts'], - properties: { - verdicts: { - type: 'array', - items: { - type: 'object', - required: ['number', 'verdict', 'evidence', 'reachableFromDefault'], - properties: { - number: { type: 'number' }, - title: { type: 'string' }, - // EVIDENCE NOBODY LANDED IS NOT EVIDENCE. A commit that exists only on an unmerged branch - // closes an issue against work the default branch does not have — and the issue, once - // closed, is invisible while the gap is still there. Required, and a boolean rather than - // prose, so that not answering is a NO instead of a blank the reconciler reads past. - reachableFromDefault: { - type: 'boolean', - description: `for CLOSE-FIXED and CLOSE-OBSOLETE: TRUE only if you RAN \`git -C merge-base --is-ancestor ${DEFAULT_REF}\` and it exited 0. FALSE for anything else, including "I could not check" and any verdict where it does not apply.`, - }, - verdict: { - type: 'string', - enum: ['KEEP', 'CLOSE-FIXED', 'CLOSE-OBSOLETE', 'CLOSE-DUPLICATE', 'NEEDS-OWNER'], - description: 'KEEP when uncertain. A wrongly closed issue is invisible; a stale open one is not.', - }, - evidence: { type: 'string', description: 'for any CLOSE: the commit, test or code that makes it moot, with file:line. "Looks done" is not evidence.' }, - duplicateOf: { type: 'number' }, - beadCandidate: { type: 'boolean', description: 'KEEP items that are real work and should exist in beads too' }, - staleness: { type: 'string', description: 'what in the issue text is now factually wrong about the tree' }, - }, - }, - }, - }, -} - -// --- Collect --------------------------------------------------------------- -// One agent gathers raw facts; everything that can be decided by a rule is decided in-script. -// Measured in this programme: a per-item agent loop over 89 worktrees took many minutes and 233KB -// of transcript, while one batched shell command did the same work in 5.2 seconds. Agents are for -// judgement, not for enumeration. -phase('Collect') -const collected = await agent( - `Gather the raw state of the backlog and the tree. Do NOT judge anything yet; this phase is a census. - -REPO: ${REPO} GH: ${GH} REF: ${REF} - -${SAFETY} - -Emit, in ONE batched pass each (not a loop of small commands): - 1. Every OPEN GitHub issue: number, title, labels, author, createdAt, updatedAt, comment count, and - the first 400 characters of the body. Use a single \`gh issue list --json\` call with --limit high - enough to get them all, and say how many you got. - 2. Every bead: id, title, status, priority, and its dependency edges. \`bd list\` and \`bd dep\`. - 3. The crate inventory under backend/crates, with each crate's line count, so domains can be sized. - (A separate in-script disk census measures \`find backend/crates -name Cargo.toml\` independently — - do NOT invent a matching \`cargoTomlPaths\` here; that would validate the census against itself.) - 4. Recently merged PRs (last 40) with number, title and merge commit — a closed issue often has its - fix sitting in one of these, and that is the cheapest evidence of CLOSE-FIXED there is. - 5. The abandoned-worktree roots that must be EXCLUDED from every later search, listed explicitly. - -Return raw structured data. No verdicts, no recommendations — later phases do that, and a census that -editorialises makes its own errors invisible.`, - // A SCHEMA, not free text. The first version of this file had none, so `collected` came back as a - // plain string and the regex below that scraped issue numbers out of it matched NOTHING. Ten - // triage lanes were therefore never dispatched, and the run reported "0 issues triaged" as though - // that were an answer. The census must hand the script DATA, not prose it has to parse. - { - label: 'collect', - phase: 'Collect', - schema: { - type: 'object', - required: ['openIssueNumbers', 'openIssueCount', 'issues', 'beads', 'crates'], - properties: { - openIssueNumbers: { type: 'array', items: { type: 'number' }, description: 'EVERY open issue number. This drives the triage fan-out, so an omission here silently un-audits that issue.' }, - openIssueCount: { type: 'number', description: 'what gh reported, so the script can catch a truncated list' }, - issues: { type: 'array', items: { type: 'object' }, description: 'number, title, labels, author, createdAt, updatedAt, comments, body excerpt' }, - beads: { type: 'array', items: { type: 'object' } }, - // This drives the domain-coverage check, so a missing crate is a domain nobody audits while - // the run still reports every domain covered. `name` is what makes it comparable. - crates: { - type: 'array', - items: { - type: 'object', - required: ['name'], - properties: { - name: { type: 'string', description: 'path relative to backend/crates, e.g. "identity" or "platform/db"' }, - lines: { type: 'number' }, - }, - }, - }, - mergedPrs: { type: 'array', items: { type: 'object' } }, - excludedRoots: { type: 'array', items: { type: 'string' } }, - }, - }, - }, -) - -// INDEPENDENT ON-DISK CRATE CENSUS. Workflow sandboxes have no Node filesystem API and reject -// `import()`, so the script cannot `ls`/`find` itself — but a control must not validate Collect's -// crate list against another field from the SAME Collect return (cargoTomlPaths co-emitted with -// crates). A coordinated partial list then reads as full coverage. Measure the disk set in a -// dedicated find-only agent whose sole schema field is the path list, then compare. -const diskCensus = await agent( - `Measure the on-disk crate set for REPO: ${REPO}. Do NOT audit, triage, or summarise. - -Run ONE command only (cwd = REPO): - find backend/crates -name Cargo.toml -print - -Return EVERY path it prints, repo-relative (e.g. "backend/crates/identity/Cargo.toml"). -Do not invent paths, do not truncate, do not dedupe by hand — emit the find output as cargoTomlPaths.`, - { - label: 'crate-disk-census', - phase: 'Collect', - schema: { - type: 'object', - required: ['cargoTomlPaths'], - properties: { - cargoTomlPaths: { - type: 'array', - items: { type: 'string' }, - description: 'EVERY path from `find backend/crates -name Cargo.toml` (repo-relative)', - }, - }, - }, - }, -) - -// --- The read fan-out ------------------------------------------------------ -// EVERY PHASE BELOW IS READ-ONLY, so width is nearly free and there is no collision to fear. -// Measured in this programme: wall-clock tracks per-step latency x DEPTH and is almost insensitive -// to WIDTH — 12 agents at depth 6 took 74 min, 36 agents at depth 6 took 63 min. So the audit, -// the cross-cutting sweeps and the issue triage all run in ONE parallel block rather than in -// sequence: three independent read phases stacked serially would triple the wall-clock and buy -// nothing, because none of them feeds another. Only Reconcile, which WRITES, is serialised. -// -// Domains are narrow on purpose. A reviewer holding one bounded capability finds the interaction -// defects that live in the seam, and a smaller root means fewer files skimmed rather than read. -const BASE_DOMAINS = ARGS.domains || [ - { key: 'identity', crates: ['identity'], why: 'principal resolution and the role/feature matrix — a fail-open here is silent and total' }, - { key: 'policy-authz', crates: ['policy'], why: 'Cedar is OBSERVE-ONLY and inert in production here; find what actually enforces' }, - { key: 'governance', crates: ['governance'], why: 'four-eyes, SoD, maker-checker, effective-dating — the controls that must not be bypassable' }, - { key: 'leave', crates: ['leave'], why: '§4-31: 연차 must have NO reason field and cannot be refused. The schema currently has reason NOT NULL — verify and size it' }, - { key: 'attendance', crates: ['attendance'], why: 'the 주52 cap and break rules are statutory, not policy' }, - { key: 'orgchange-eval', crates: ['orgchange', 'evaluation'], why: 'org lifecycle and the appraisal surface labour law constrains' }, - { key: 'payroll', crates: ['payroll'], why: 'money is irreversible; period locks, rounding and the minimum-wage instrument' }, - { key: 'finance', crates: ['finance-gl', 'financial', 'benefit'], why: 'ledger integrity and benefit entitlement' }, - { key: 'ontology', crates: ['ontology'], why: 'the substrate every other domain projects through' }, - { key: 'kernel-registry', crates: ['kernel', 'registry'], why: 'shared types and the object registry everything else trusts' }, - { key: 'workflow', crates: ['workflow'], why: 'schedules, drains and outboxes — where at-least-once quietly becomes at-least-twice' }, - { key: 'comms-egress', crates: ['comms', 'messenger', 'notices'], why: 'egress: the DLP and send-gate boundary, where a leak is external and permanent' }, - { key: 'docs-inbox', crates: ['docs', 'inbox', 'notifications'], why: 'document custody and the personal surfaces §4-37 separates' }, - { key: 'ops-field', crates: ['dispatch', 'facilities', 'equipment', 'inspection', 'logistics', 'workorder', 'production'], why: 'the 70% of staff who work on a client site' }, - { key: 'platform', crates: ['platform'], why: 'db, authz, request-context, audit-chain — every tenant boundary in one crate tree' }, -] - -// THE DOMAIN LIST IS HAND-WRITTEN AND THE TREE IS NOT. A crate added after this list was written -// belongs to no domain, so nothing reads it — while the headline still reports "15/15 domains -// audited", which is true and means nothing. The census already collects the crate inventory, so -// the gap is decidable here rather than discoverable later: everything the inventory names and no -// domain claims becomes its own lane. -const crateEntries = (collected && collected.crates) || [] -const crateNames = crateEntries - .map((c) => (c && typeof c.name === 'string' ? c.name : '')) - .map((n) => n.trim().replace(/^backend\/crates\//, '').replace(/\/+$/, '')) - .filter(Boolean) -// A control that examines zero subjects must FAIL. With no inventory — or one whose entries have no -// name — the coverage question cannot be asked at all, and answering it "complete" is the exact -// false green this file exists to refuse. -if (crateNames.length < crateEntries.length || !crateNames.length) { - throw new Error( - `backlog-audit: the census returned an unusable crate inventory (${crateEntries.length} entr(ies), ` + - `${crateNames.length} with a name). Domain coverage cannot be checked against it, so every domain ` + - 'reported as audited would be an unverified claim. Re-run Collect and have it report each crate ' + - 'under backend/crates with a `name`.', - ) -} -// THE CENSUS CAN OMIT A CRATE AND STILL LOOK WELL-FORMED. A partial list whose every entry has a -// name satisfies the guard above while the omitted crate receives no audit lane. The on-disk set -// comes from the dedicated crate-disk-census agent (find-only), NOT from Collect's own return — -// validating crates against a co-emitted cargoTomlPaths field is still validating the census -// against itself. Workflow sandboxes have no Node filesystem API, so find runs in that agent; -// examined-zero / dead oracle / omitted crate all fail closed here. -function cratesOmittedFromCensus(onDiskNames, censusNames) { - return onDiskNames.filter((c) => !censusNames.some((n) => c === n || c.startsWith(`${n}/`))) -} -function crateNamesFromCargoTomlPaths(paths) { - const out = [] - for (const raw of paths || []) { - if (typeof raw !== 'string') continue - let p = raw.trim().replace(/\\/g, '/') - if (!p) continue - p = p.replace(/^\.\//, '') - if (!p.endsWith('/Cargo.toml') && p !== 'Cargo.toml') continue - p = p.replace(/\/Cargo\.toml$/, '') - p = p.replace(/^backend\/crates\//, '') - if (!p || p.includes('..')) continue - out.push(p) - } - return [...new Set(out)].sort() -} -{ - if (!diskCensus) { - throw new Error( - 'backlog-audit: crate-disk-census agent returned nothing, so domain coverage cannot be ' + - 'cross-checked against an independent on-disk find. Re-run Collect.', - ) - } - const cargoTomlPaths = diskCensus.cargoTomlPaths - if (!Array.isArray(cargoTomlPaths)) { - throw new Error( - 'backlog-audit: crate-disk-census must return cargoTomlPaths (array) from ' + - '`find backend/crates -name Cargo.toml`. The harness cannot walk the crate tree itself ' + - 'inside the workflow sandbox.', - ) - } - const onDisk = crateNamesFromCargoTomlPaths(cargoTomlPaths) - if (!onDisk.length) { - throw new Error( - 'backlog-audit: crate-disk-census returned an empty cargoTomlPaths list — domain coverage ' + - 'cannot be cross-checked against the on-disk Cargo.toml census. Re-run with find output.', - ) - } - const omitted = cratesOmittedFromCensus(onDisk, crateNames) - if (omitted.length) { - throw new Error( - `backlog-audit: the census omitted ${omitted.length} crate(s) present under backend/crates ` + - `(${onDisk.length} on disk via crate-disk-census, ${crateNames.length} named). Omitted: ${omitted.slice(0, 20).join(', ')}` + - `${omitted.length > 20 ? ', ...' : ''}. Re-run Collect so every Cargo.toml is named.`, - ) - } -} -const claimed = (crate) => BASE_DOMAINS.some((d) => (d.crates || []).some((c) => crate === c || crate.startsWith(`${c}/`))) -const uncovered = [...new Set(crateNames.filter((c) => !claimed(c)))].sort() -const DOMAINS = uncovered.length - ? [...BASE_DOMAINS, { key: 'uncovered', crates: uncovered, why: 'crates the census found that no named domain claims — they would otherwise be read by nobody while the run reported full coverage' }] - : BASE_DOMAINS -if (uncovered.length) log(`domain coverage: ${uncovered.length} discovered crate(s) matched no named domain, audited in the "uncovered" lane: ${uncovered.join(', ')}`) - -// Cross-cutting sweeps read ACROSS the tree rather than down one crate. They exist because the -// worst defects in this programme were never inside one crate: a census that ran before migrations, -// a gate wired to nothing, a doc claiming a property the code had lost. No per-crate reviewer could -// have seen any of them, because each is a property of the SEAM between things. -const CROSS = [ - { key: 'x-migrations', why: 'all migrations as a sequence: RLS declared but not FORCED, a missing append-only trigger, a destructive DDL, contiguity, and any table whose properties diverge from the 0177/0213/0214 references' }, - { key: 'x-gates', why: 'every backend/ci/gates/** binary: is each REACHED by a CI step, and would it FAIL on a real violation? A gate wired to nothing is worse than no gate, because it reads as coverage' }, - { key: 'x-tenancy', why: 'the org boundary end to end: RLS FORCE, app.current_org arming, the CURRENT_ORG task-local (a bare tokio::spawn does not inherit it and the failure is ZERO ROWS, not an error), and any aggregate that merges sources and widens visibility' }, - { key: 'x-openapi', why: 'the 36k-line hand-maintained openapi.yaml against the handlers it claims to describe — a published contract the code stopped honouring is a live interop break, and two were just found' }, - { key: 'x-docs-drift', why: 'docs/** and module docs against the code: claims of the form "every / always / cannot / the N ways X can happen". Pick the load-bearing ones and check whether the code still holds them' }, - { key: 'x-compliance', why: '§4-31 labour law and §3.10 internal controls as properties of the whole tree: statutory periods and rates must be catalogue-derived with the instrument cited, never hardcoded; no reason field on 연차; no destructive delete where 보관=숨김 is required' }, - { key: 'x-supply-chain', why: 'dependency posture: unmaintained or advisory-bearing crates, git sources, licence outliers, and anything a cargo-deny ignore is silently carrying' }, -] - -// Issue batches are computed BEFORE the read block so triage can join the same parallel wave. -// Read the field the schema guarantees. The previous version scraped `"number": N` out of the -// stringified census with a regex; the census was prose, so it matched nothing and ten triage lanes -// were silently never dispatched. -const issueNumbers = [...new Set((collected && collected.openIssueNumbers) || [])] - .map(Number).filter((n) => Number.isFinite(n) && n > 0).sort((a, b) => a - b) -const claimedCount = (collected && collected.openIssueCount) || 0 - -// A PHASE THAT EXAMINES ZERO SUBJECTS MUST FAIL, NEVER PASS. This is the harness's own standing rule -// — "examined zero subjects MUST be a FAILURE" — applied to the harness itself, because the first run -// of this file broke exactly that way: 22 audit lanes did real work while the triage half quietly ran -// on an empty list and the headline read "0 issues triaged" as if it were a result. -// A VERIFIED empty tracker is a legitimate state, and the first version of this guard refused it. -// The false green was a census that returned NOTHING while the tracker held 78 issues; a census that -// returns nothing AND reports a count of zero is agreeing with itself, and blocking there would make -// the workflow unable to audit standing code precisely when the backlog is clean. The guard exists to -// catch a census that CONTRADICTS itself, not one that is merely empty. -if (!issueNumbers.length && claimedCount === 0) { - log('census reports zero open issues and lists none — consistent, so triage schedules no batches') -} else if (!issueNumbers.length) { - throw new Error( - 'backlog-audit: the census returned NO open issue numbers, so triage would examine nothing and ' + - 'report success. That is a false green, not an empty backlog. Either the repository genuinely ' + - 'has zero open issues (verify with `gh issue list --state open`), or the Collect agent did not ' + - 'populate openIssueNumbers. Fix the census rather than running a triage over nothing.', - ) -} -// A truncated list is the quieter version of the same failure: some issues get audited, the rest are -// silently dropped, and nothing in the output distinguishes that from a clean sweep. -if (claimedCount && issueNumbers.length < claimedCount) { - throw new Error( - `backlog-audit: census reported ${claimedCount} open issues but listed only ${issueNumbers.length} ` + - 'numbers. Triage would silently skip the remainder. Re-run Collect with a --limit high enough to ' + - 'return them all.', - ) -} - -const batches = [] -for (let i = 0; i < issueNumbers.length; i += BATCH) batches.push(issueNumbers.slice(i, i + BATCH)) - -phase('Read') -log(`read fan-out: ${DOMAINS.length} domains + ${CROSS.length} cross-cutting + ${batches.length} issue batches (${issueNumbers.length} issues) = ${DOMAINS.length + CROSS.length + batches.length} lanes, all read-only`) - -const auditThunks = DOMAINS.map((d) => () => - agent( - `Audit the ${d.key} capability domain of a production Korean B2B console. Find real defects. - -REPO: ${REPO} REF: ${REF} -CRATES: ${d.crates.map((c) => `backend/crates/${c}`).join(', ')} -WHY THIS DOMAIN MATTERS: ${d.why} - -${SAFETY} - -THE CENSUS THIS RUN COLLECTED (use it to avoid re-filing what is already tracked): -${JSON.stringify(collected).slice(0, 6000)} - -WHAT TO HUNT, in descending order of value. These are the classes this programme has actually been -bitten by, so they are worth more than a generic review: - - 1. FAIL-OPEN GUARDS. A check that cannot see its subject, or that exits 0 on the empty case. Ask of - every guard: where does it run in the sequence, does its subject exist yet, and what is the - finest distinction its data source can express? A census that runs before migrations examines - zero rows and passes. A per-crate rule enforced by a data source that only distinguishes roles - never draws the crate boundary. - 2. TENANT AND SCOPE BOUNDARIES. RLS declared but not FORCED; a policy that special-cases one scope - and leaves the others open; an aggregate that widens visibility by merging sources; a read that - fails OPEN rather than closed when no org is armed. Note that CURRENT_ORG is a tokio task-local - and a bare tokio::spawn does not inherit it — that failure returns ZERO ROWS, not an error. - 3. SECOND WRITERS AND DUAL SOURCES OF TRUTH. Two code paths writing one table; a value stored in - two places that can disagree; a hand-maintained list mirroring something derivable. The canonical - tables are already gated, so look at the ones that are NOT canonical. - 4. CLAIMS WIDER THAN THE CODE. A doc, comment or test NAME asserting a universal the code does not - hold — "every", "always", "cannot", "the three ways X can happen". Pick the load-bearing - assertion, break the code it guards, and say whether it actually goes red. - 5. LABOUR-LAW AND COMPLIANCE GUARDRAILS (§4-31), which are correctness here and not policy taste: - 연차 must have no reason field and cannot be refused (only 시기변경 협의); no overtime glorification; - no discriminatory recruiting fields; statutory periods and rates must be catalogue-derived with - the instrument cited, never hardcoded constants. - 6. IRREVERSIBILITY. Hard deletes where the charter requires 보관=숨김; a destructive path reachable - before dependent objects are settled; an egress that sends before an approval gate. - -DO NOT report style, naming, or "consider extracting a helper". Do not report a defect you cannot -demonstrate. An empty findings array from an honest sweep is a GOOD result and must be reported as -one — inventing findings to look productive is the failure mode this phase must avoid. - -Before filing anything, check the census for an existing issue or bead covering it and name it in -existingIssue rather than creating a duplicate. - -Also report STRENGTHS: controls that are genuinely load-bearing, so a later reader does not simplify -them away. This programme has twice nearly deleted a guard that looked redundant and was not.`, - { label: `audit:${d.key}`, phase: 'Read', schema: FINDING_SCHEMA }, - )) - -// Cross-cutting thunks: same evidence rules, but the unit of review is a PROPERTY of the tree -// rather than a directory in it. -const crossThunks = CROSS.map((c) => () => - agent( - `Sweep ONE cross-cutting property of a production Korean B2B console. This is not a per-crate -review — the unit is the property, and it is deliberately the shape a per-crate reviewer cannot see. - -REPO: ${REPO} REF: ${REF} -THE PROPERTY: ${c.key} -WHY IT IS ITS OWN LANE: ${c.why} - -${SAFETY} - -THE CENSUS THIS RUN COLLECTED: -${JSON.stringify(collected).slice(0, 5000)} - -The worst defects in this programme were never inside one crate. A canonical-writer census sat in a -reconcile script that runs BEFORE migrations, so it examined zero tables and passed in every -automated path. A gate existed, compiled and was tested, and no CI step ever invoked it. A module doc -enumerated "the three ways X can happen" while reviewers had already proven a fourth and a fifth. -Each is a property of the SEAM between things, and no reviewer holding one directory could have found -any of them. - -So: read ACROSS. Follow the property wherever it goes. Report the same evidence-bound findings the -per-domain lanes report — file:line, the command, its real output — and mark provenByExecution TRUE -only for what you actually ran. - -An empty findings array from an honest sweep is a GOOD result. Say what you covered and what you did -not reach, because a claimed sweep that skipped half the tree is worse than a partial one that says so.`, - { label: `cross:${c.key}`, phase: 'Read', schema: FINDING_SCHEMA }, - )) - -// --- Triage ---------------------------------------------------------------- -// Issues are batched so each agent holds a readable set and can compare within it for duplicates. -const triageThunks = batches.map((b, i) => () => - agent( - `Triage GitHub issues ${b.join(', ')} in ${GH} against the CURRENT tree. Batch ${i + 1} of ${batches.length}. - -REPO: ${REPO} REF: ${REF} - -${SAFETY} - -FOR EACH ISSUE: read it in full (\`gh issue view --comments\`), then go and LOOK at the tree. -The whole point is that issue text describes a repo that has moved on. Decide: - - KEEP — still a real, open problem. Say what remains true. - CLOSE-FIXED — the code now does what the issue asked. EVIDENCE REQUIRED: the commit, the test, - or the file:line that implements it. Prefer a merged PR from the census. - CLOSE-OBSOLETE — the thing it is about no longer exists, or a decision superseded it. Name the - deletion or the ADR. - CLOSE-DUPLICATE — another issue covers it. Give the number, and prefer keeping the one with more - evidence rather than the older one. - NEEDS-OWNER — real, but the next step is a human decision, not work. Say what the decision is. - -THE BAR FOR CLOSING, and it is deliberately high: a wrongly closed issue is INVISIBLE, while a stale -open one is merely noise. If you cannot point at the thing that makes it moot, the verdict is KEEP -with a staleness note. "Looks done", "probably superseded" and "no longer relevant" are not evidence. - -AND THE EVIDENCE MUST BE LANDED. A commit sitting on an unmerged branch, an integration branch or an -unmerged PR closes the issue against work nobody has — this checkout has dozens of worktrees whose -commits are on none of them. For every CLOSE-FIXED and CLOSE-OBSOLETE, name the commit and PROVE it: - - git -C ${REPO} merge-base --is-ancestor ${DEFAULT_REF} && echo REACHABLE - -Set reachableFromDefault=true ONLY when that exited 0 and you saw REACHABLE. If your evidence is a -file rather than a commit, take the commit that last touched it -(\`git -C ${REPO} log -1 --format=%H -- \`) and run the same check. Anything else is FALSE, -including "I could not check" — an unproven CLOSE is withheld and left open, which is the cheap -failure. - -ALSO RECORD STALENESS for KEEP items: what in the issue text is now factually wrong — a renamed file, -a moved line number, a crate that no longer exists, a fixed sub-part. That is what makes an old issue -expensive to pick up, and writing it down is most of the value of this pass. - -Mark beadCandidate=true for KEEP items that are real work someone should schedule, so the reconcile -phase can mirror them into the working tracker.`, - // Cheap tier: every verdict here passes through the single-writer Reconcile, which refuses to - // close anything whose evidence is not reachable from the default branch. A wrong KEEP costs a - // stale issue; a wrong CLOSE cannot get past that guard. - { label: `triage:${i + 1}`, phase: 'Read', schema: TRIAGE_SCHEMA, model: 'sonnet' }, - )) - -// ONE parallel wave. Audit, cross-cutting and triage are mutually independent reads, so stacking -// them in three phases would multiply the wall-clock by three and buy nothing. The harness caps -// concurrency itself, so a wide list queues rather than overloads — width costs latency only when -// it exceeds the cap, and even then it degrades linearly instead of serialising. -const all = await parallel([...auditThunks, ...crossThunks, ...triageThunks]) -const audits = all.slice(0, DOMAINS.length) -const crosses = all.slice(DOMAINS.length, DOMAINS.length + CROSS.length) -const triaged = all.slice(DOMAINS.length + CROSS.length) - -// A dead lane is not an absent finding. Name every one, so a partial sweep can never read as a -// complete one — the same rule the lane-fanout harness learned when session limits silently took -// 4 of 7 agents from one run and 3 of 3 from another. -const auditOk = audits.filter(Boolean) -const crossOk = crosses.filter(Boolean) -const deadDomains = DOMAINS.map((d, i) => (audits[i] ? null : d.key)).filter(Boolean) -const deadCross = CROSS.map((c, i) => (crosses[i] ? null : c.key)).filter(Boolean) -const deadBatches = batches.map((b, i) => (triaged[i] ? null : `#${b[0]}-${b[b.length - 1]}`)).filter(Boolean) -const dead = [...deadDomains, ...deadCross, ...deadBatches] -log(`read: ${auditOk.length}/${DOMAINS.length} domains, ${crossOk.length}/${CROSS.length} cross-cutting, ${triaged.filter(Boolean).length}/${batches.length} issue batches`) -if (dead.length) log(`read: DIED and therefore UNAUDITED — ${dead.join(', ')}`) - -const findingsAll = [...auditOk, ...crossOk] -const totalFindings = findingsAll.reduce((n, a) => n + (a.findings || []).length, 0) -const provenFindings = findingsAll.reduce((n, a) => n + (a.findings || []).filter((f) => f.provenByExecution).length, 0) -log(`read: ${totalFindings} findings, ${provenFindings} proven by execution`) - -const triageOk = triaged.filter(Boolean) -const verdicts = triageOk.flatMap((t) => t.verdicts || []) -const closing = verdicts.filter((v) => String(v.verdict).startsWith('CLOSE')) -// A CLOSE-FIXED or CLOSE-OBSOLETE verdict is a claim about the DEFAULT BRANCH: the code now does -// this, or the thing no longer exists. Evidence reachable only from an unmerged branch does not -// support that claim, and closing on it hides a live gap behind a closed issue. CLOSE-DUPLICATE -// cites another issue rather than the tree, so ancestry does not apply to it. -const NEEDS_LANDED_EVIDENCE = ['CLOSE-FIXED', 'CLOSE-OBSOLETE'] -const evidenceLanded = (v) => !NEEDS_LANDED_EVIDENCE.includes(String(v.verdict)) || v.reachableFromDefault === true -const unevidenced = closing.filter((v) => !v.evidence || v.evidence.trim().length < 40 || !evidenceLanded(v)) -const unlanded = closing.filter((v) => !evidenceLanded(v)).map((v) => v.number) -log(`triage: ${verdicts.length} verdict(s); ${closing.length} propose closing; ${unevidenced.length} of those lack real evidence${unlanded.length ? ` (${unlanded.length} cite work not reachable from ${DEFAULT_REF})` : ''}`) -if (unevidenced.length) log(`triage: WITHHELD from closing for want of LANDED evidence: ${unevidenced.map((v) => '#' + v.number).join(', ')}`) - -// --- Reconcile ------------------------------------------------------------- -// A blind `.slice(0, 24000)` over the serialised findings dropped everything past the cap without a -// word, and with ~32 read lanes the cap is reached routinely — so the single writer filed beads for a -// prefix of the audit and reported success. Silent truncation reads as "covered everything". Order by -// what a reconciler must not miss (proven, then severity), and when the budget still binds, SAY what -// was cut so the omission is visible in the run rather than discovered six weeks later. -function renderFindings(all) { - const rank = { blocker: 0, major: 1, minor: 2, nit: 3 } - const ordered = [...all].sort((a, b) => - (b.provenByExecution === true) - (a.provenByExecution === true) || - (rank[a.severity] ?? 9) - (rank[b.severity] ?? 9)) - const kept = [] - let budget = 24000 - for (const f of ordered) { - const line = JSON.stringify(f) - if (line.length > budget) break - budget -= line.length - kept.push(f) - } - const dropped = ordered.length - kept.length - return JSON.stringify(kept) + (dropped - ? `\n\n!! ${dropped} of ${ordered.length} findings DID NOT FIT this prompt and are NOT above. They are` + - ' the lowest-ranked ones, but they are unfiled. Your report MUST state that this run reconciled' + - ` ${kept.length} of ${ordered.length} findings, so the omission is visible.` - : '') -} - -phase('Reconcile') -const reconciled = await agent( - `You are the SINGLE WRITER for the backlog. Turn this audit into tracked work, and close what is dead. - -REPO: ${REPO} GH: ${GH} APPLY: ${APPLY} - -${SAFETY} - -AUDIT + CROSS-CUTTING FINDINGS (${totalFindings} total, ${provenFindings} proven by execution): -${renderFindings(findingsAll)} - -ISSUE VERDICTS (${verdicts.length}): -${JSON.stringify(verdicts).slice(0, 16000)} - -WITHHELD FOR WANT OF LANDED EVIDENCE — proposed for closing, and must NOT be closed. Either the -evidence is too thin to name anything, or it is not reachable from ${DEFAULT_REF}, which means it -closes the issue against work the default branch does not have: -${unevidenced.map((v) => `#${v.number} ${v.title || ''}${unlanded.includes(v.number) ? ` — evidence not reachable from ${DEFAULT_REF}` : ''}`).join('\n') || '(none)'} - -${dead.length ? `LANES THAT NEVER REPORTED — their scope is UNAUDITED and the summary MUST say so: ${dead.join(', ')}` : 'Every read lane reported.'} - -DO, in this order: - -1. DE-DUPLICATE ACROSS SOURCES before writing anything. The same defect may appear as an audit - finding, an open issue, and an existing bead. Collapse them and say which record wins. Filing a - fourth copy of a known problem makes the backlog worse, not better. - -2. RANK. Order by severity and blast radius, with proven-by-execution ahead of argued. A tenant or - money defect outranks a doc drift regardless of how neatly the doc drift is written up. - -3. ${APPLY ? 'CREATE BEADS' : 'DRAFT BEADS (do not create — apply is false)'} for confirmed findings. - Each bead must carry the EVIDENCE inline — file:line, the command, the observed output — so - nobody re-derives it. A bead whose body is a restatement of its title is worthless six weeks - later. Set priority from the ranking, and wire dependencies where one finding blocks another. - -4. ${APPLY ? 'CLOSE ISSUES' : 'LIST ISSUES TO CLOSE (do not close — apply is false)'} for - CLOSE-* verdicts that carry real evidence. The closing comment must state WHY, name the commit or - file that makes it moot, and be written for the person who filed it — this repository's sibling - project comments an explicit acceptance and then closes with the merged PR named, which is the - pattern to follow. Never close silently. - -5. SYNC THE TWO TRACKERS, and state the rule you applied rather than inventing one per item: - GitHub issues are the durable, public record; beads are the working queue. So every KEEP issue - marked beadCandidate should have a bead, and every bead representing work others should see - should reference an issue. Report the drift you found — this run began with ${issueNumbers.length} - open issues against a far smaller bead set, which is itself the finding. - -6. REPORT: what was filed, what was closed, what was WITHHELD and why, which domains went unaudited, - and the three things you would fix first. Be honest about coverage — a sweep that missed a domain - and says so is worth more than one that implies completeness it does not have.`, - { label: 'reconcile', phase: 'Reconcile' }, -) - -return { - headline: [ - `${auditOk.length}/${DOMAINS.length} domains + ${crossOk.length}/${CROSS.length} cross-cutting audited`, - `${totalFindings} findings (${provenFindings} proven)`, - `${verdicts.length} issues triaged, ${closing.length - unevidenced.length} closable, ${unevidenced.length} withheld`, - // Never claim APPLIED on the strength of the caller's flag alone. When the Reconcile agent dies, - // agent() returns null and the mutations are absent — or worse, a partial prefix of them landed - // and nothing records which. An operator reading APPLIED would take a success claim over an - // unknown state, which is the same false-green class this workflow exists to find. - APPLY ? (reconciled ? 'APPLIED' : 'RECONCILE DIED — mutations UNKNOWN, possibly partial; verify by hand') : 'REPORT ONLY (apply=false)', - ], - dead, - audits: auditOk, - cross: crossOk, - verdicts, - withheld: unevidenced.map((v) => v.number), - reconciled, -} diff --git a/.claude/workflows/lane-fanout.js b/.claude/workflows/lane-fanout.js deleted file mode 100644 index 64c0cfcce..000000000 --- a/.claude/workflows/lane-fanout.js +++ /dev/null @@ -1,1154 +0,0 @@ -export const meta = { - name: 'lane-fanout', - description: 'Reusable hardened lane fan-out: RED-baseline build, cross-lane defect ledger, adversarial diff-only review, independent re-verification, converge-or-escalate', - whenToUse: 'Any multi-lane implementation phase with path-disjoint owned roots. Parameterise with args; do not fork this file per phase.', - phases: [ - { title: 'Build', detail: 'one implementer per disjoint lane, failing test first' }, - { title: 'Review', detail: '2 diff-only adversarial reviewers + 1 independent re-runner per lane' }, - ], -} - -// --------------------------------------------------------------------------- -// args = { -// tip: "" // what each lane started from; ALL diffs are taken against this -// lanes: [{ key, bead, wt, owned, brief, accept, blockedTargets? }] -// Lanes must be pairwise disjoint in BOTH `wt` and `owned`, and both are refused at -// dispatch: a shared worktree collides while they build, a shared owned root collides -// at LAND. `owned` must name paths, not describe them. -// lockExtra?: "" // phase-specific additions to the lock contract -// maxRounds?: 3 -// lenses?: ["...", "..."] // review lenses; defaults below -// } -// -// This file exists because the same harness was re-derived four times by hand, and every defect -// found in it (HEAD~1 diffing the base commit, reviewer findings discarded with no feedback edge, -// a stale owned-root after a mid-run scope ruling, "empty diff = auto reject") had to be fixed in -// each copy separately. Bun's rule applies to the pipeline as much as to the code it produces: -// fix the process that generates the work, not each instance of the work. -// --------------------------------------------------------------------------- - -// args may arrive as a real object OR as a JSON-encoded string depending on how the caller passed -// it. A reusable harness should tolerate both rather than die on the caller's serialisation choice. -let ARGS = args -if (typeof ARGS === 'string') { - try { - ARGS = JSON.parse(ARGS) - } catch (e) { - throw new Error(`lane-fanout: args arrived as a string that is not valid JSON: ${e.message}`) - } -} -ARGS = ARGS || {} - -// AN OPTION THIS HARNESS DOES NOT READ MUST ABORT, NEVER BE SILENTLY DROPPED. -// Borrowed from a sibling runner where exactly this defect cost six lanes and ~2.3M tokens: a -// top-level option was accepted, ignored, and the run looked normal. The arg blobs passed here are -// kilobytes of hand-written JSON, so a single typo -- `lens` for `lenses`, `maxRound` for -// `maxRounds` -- silently changes what runs while every log line still looks right. Fail loudly at -// dispatch instead, where it costs seconds. -const KNOWN_ARGS = ['tip', 'lanes', 'maxRounds', 'lockExtra', 'lenses', 'land', 'integrationBranch', 'trial'] -// `blockedTargets` was documented in the args comment above but omitted here, so a caller -// following the documented interface aborted with "unknown option(s)". Documented and accepted -// must be the same set — a doc that describes an input the code rejects is the same defect class -// as a doc that claims a property the code lost. -const KNOWN_LANE_KEYS = ['key', 'bead', 'wt', 'owned', 'brief', 'accept', 'reviewOnly', 'priorResult', 'blockedTargets'] -{ - const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) - for (const l of ARGS.lanes || []) { - for (const k of Object.keys(l || {})) { - if (!KNOWN_LANE_KEYS.includes(k)) unknown.push(`lanes[${(l && l.key) || '?'}].${k}`) - } - } - if (unknown.length) { - throw new Error( - `lane-fanout: unknown option(s) ${unknown.join(', ')} — this harness would ignore them silently. ` + - `Known top-level: ${KNOWN_ARGS.join(', ')}. Known per-lane: ${KNOWN_LANE_KEYS.join(', ')}.`, - ) - } -} - -const TIP = ARGS.tip -const LANES = ARGS.lanes || [] -const MAX_ROUNDS = ARGS.maxRounds || 3 - -if (!TIP) throw new Error('lane-fanout: args.tip is required (the SHA every lane diffs against)') -if (!Array.isArray(LANES) || !LANES.length) throw new Error('lane-fanout: args.lanes must be a non-empty array') -for (const l of LANES) { - for (const f of ['key', 'wt', 'owned', 'brief', 'accept']) { - if (!l || !l[f]) throw new Error(`lane-fanout: lane ${l && l.key ? l.key : ''} is missing required field "${f}"`) - } -} - -// TWO LANES MAY NEVER SHARE A WORKTREE. Presence checks alone let two lanes carry the same `wt` -// through validation, and `parallel(LANES.map(...))` then dispatches both concurrently into one root -// — each told to edit and commit there. That is the two-writers-one-worktree failure this programme -// has already paid for twice: once as symptoms that looked like filesystem corruption (files edited -// at unclaimed timestamps, a deleted file reappearing, a bound silently changing), and once as 28 -// duplicate beads when a second writer was resurrected into live shared state. The lock contract -// tells each implementer to build on whatever the worktree contains, which makes the collision -// silent by design — the second lane treats the first lane's half-finished edits as its own baseline. -// Refuse at dispatch, where it costs nothing. -{ - const seen = new Map() - for (const l of LANES) { - const prior = seen.get(l.wt) - if (prior) { - throw new Error( - `lane-fanout: lanes "${prior}" and "${l.key}" both declare worktree ${l.wt}. Concurrent lanes ` + - 'must have disjoint worktrees — two implementers in one root overwrite each other silently, ' + - 'because the lock tells each to build on what it finds. Give them separate worktrees, or run ' + - 'them in separate invocations.', - ) - } - seen.set(l.wt, l.key) - } - const keys = new Set() - for (const l of LANES) { - if (keys.has(l.key)) throw new Error(`lane-fanout: duplicate lane key "${l.key}" — labels and telemetry would merge two lanes into one.`) - keys.add(l.key) - } - - // OVERLAPPING OWNED ROOTS ARE THE SAME COLLISION, DEFERRED TO LAND. Separate worktrees mean two - // lanes cannot corrupt each other's files while they build, so this one survives every reviewer - // and every verifier: each diff is correct in isolation. It fires at LAND time, on the - // integration branch, after the whole run has been paid for — two independent rewrites of the - // same files from the same base, handed to a lander with no authority to judge either. Refuse it - // where the duplicate worktree is refused, for the cost of a string compare. - // - // An owned root is a list of path patterns, sometimes with prose around it. Reduce each entry to - // the fixed path prefix before its first wildcard segment; two lanes collide when one prefix is - // the other, or is a PATH prefix of it (segment-aligned, so crates/ab does not collide with - // crates/a). - // Canonicalise before compare: `./backend/crates/foo` and `backend/crates/foo` must collide. - // Collapse `.` segments; reject `..` rather than silently rewriting ownership. - const canonicalizeOwnedPrefix = (t) => { - const segs = [] - for (const seg of String(t).split('/')) { - if (seg.includes('*')) break - if (!seg || seg === '.') continue - if (seg === '..') { - throw new Error( - `lane-fanout: owned path contains '..' (${JSON.stringify(t)}) — refuse rather than collapse ` + - 'ownership across directories.', - ) - } - segs.push(seg) - } - return segs.join('/').replace(/\/+$/, '') - } - const ownedPrefixes = (owned) => String(owned) - .split(/[\s,]+/) - .filter(Boolean) - // Path-shaped tokens only. Comparing every word would refuse two lanes for sharing "the". - .filter((t) => t.includes('/') || t.includes('*') || /\.\w+$/.test(t)) - .map(canonicalizeOwnedPrefix) - const pathOverlap = (a, b) => a === b || a === '' || b === '' || a.startsWith(`${b}/`) || b.startsWith(`${a}/`) - const roots = LANES.map((l) => ({ key: l.key, owned: l.owned, prefixes: ownedPrefixes(l.owned) })) - for (const r of roots) { - // A guard that examines zero subjects must FAIL. An owned root naming no path cannot be - // compared with anything, and it is also useless as the reviewer's IN-SCOPE PATHS list. - if (!r.prefixes.length) { - throw new Error( - `lane-fanout: lane "${r.key}" declares an owned root with no path in it (${JSON.stringify(r.owned)}). ` + - 'The overlap check would examine nothing and pass, and the reviewer would be handed prose as ' + - 'its scope list. Name the paths.', - ) - } - } - for (let i = 0; i < roots.length; i++) { - for (let j = i + 1; j < roots.length; j++) { - const hit = roots[i].prefixes - .flatMap((a) => roots[j].prefixes.map((b) => [a, b])) - .find(([a, b]) => pathOverlap(a, b)) - if (hit) { - throw new Error( - `lane-fanout: lanes "${roots[i].key}" and "${roots[j].key}" declare OVERLAPPING owned roots ` + - `(${hit[0] || ''} vs ${hit[1] || ''}). Separate worktrees keep them from ` + - 'corrupting each other while they build, so nothing before LAND can see this — the collision ' + - 'arrives on the integration branch after every reviewer has passed, as two independent ' + - 'rewrites of the same files from the same base. Narrow one lane, or run them in sequence.', - ) - } - } - } -} - -const BASE_LOCK = ` -=== LOCK CONTRACT (binding; violation = rejected work) === -GIT — these caused a real multi-agent collision in the Bun rewrite and again in this program: - NO stash / stash pop / reset (any mode) / checkout / rebase / merge / clean - NO push, NO force-push, NO git worktree add|remove, NO branch creation. - PERMITTED: git status, git diff, git log, 'git add ', 'git commit' of those paths. - Commit ONLY inside your owned root. If the worktree already contains work, BUILD ON IT — you may - not reset or revert it. Fix forward. -BUILD: - Run from the WORKTREE ROOT. Never 'cd backend'. Never a bare 'cargo test' or --workspace. - Scope every invocation: cargo test --locked --manifest-path backend/Cargo.toml -p ... - PostgreSQL-backed targets: tools/ci/cargo_needs_postgres.sh --only --num-threads=1 - *** NEVER pass --workflow-only. Dark targets carry in_workflow_postgres_job=false, so it selects - ZERO targets and exits 0. That is a FALSE GREEN, not a pass. Always --only. *** - sqlx::query! is COMPILE-TIME checked against a live schema; SQLX_OFFLINE=true compiles with no - database (offline cache committed at backend/.sqlx/). A hand-made createdb will NOT work — it - lacks the role topology and fails at compile time with 'role "anonymous" does not exist'. -CHANGE DISCIPLINE: - Minimal and mechanical. Do not refactor, tidy or rename anything near the fix. Improving while - fixing is what previously broke a lane into 99 errors across 8 crates it never opened. - If you need a paragraph-long comment to justify a workaround, the code is wrong — fix the code. -NEVER WEAKEN THE ORACLE: - No deleted tests, no #[ignore], no relaxed or loosened assertions, and above all NEVER make a - test pass by conforming it to the defect. Multiple lanes in this program were rejected for - exactly that, and in each case the "green" test was hiding a live production outage. -AN ENFORCEMENT MUST BE ABLE TO SEE ITS SUBJECT -RUN WHAT CI RUNS, BEFORE YOU REPORT DONE: - A defect that CI catches and the lane did not is a HARNESS failure, not a CI success. The lane had the - same tree, the same commands and more context; CI just had a checklist. The asymmetry is that accept - criteria are prose and CI is commands, so the lane satisfies a sentence while CI executes a gate. - Before status=done, find the commands CI will actually run over the files you touched -- read - .github/workflows/ci.yml rather than guessing -- and RUN THEM. At minimum, for the paths in your diff: - the formatter, the linter with the repo's own flags, the unit target, and any repo gate whose name - matches your area. Put the exact command lines and their exit codes in verification.commands. - If a gate cannot run locally, say WHICH and WHY there, rather than omitting it and reporting green. - A lane that reports done without having run the gates has reported an intention, not a result. - TESTS NAMED *_as_runtime_role.rs NEED A DATABASE, and a plain cargo test does NOT give them one -- - it fails ALL of them in about 0.01s, including tests that were passing, which reads like your - change broke everything when nothing ran at all. The invocation takes the repo root as its FIRST - argument and is easy to get wrong three times in a row: - tools/lanes/pgtest.sh "$PWD" cargo test -p --test - Without the leading repo root it tries to lstat a file named cargo and exits having run nothing. - A rule that cannot be followed without tribal knowledge is a rule that gets skipped, so the exact - line is written here rather than left in an ADR. - -A TEST THAT BUILDS ITS OWN SUBJECT MEASURES THE STUB, NOT THE DEPLOYMENT: - The sharper form of the rule above, and the one that actually shipped. A suite proved a dispatch - derivation TOTAL over every target in the contract -- six tests, all green, none of them naming a - target so none of them able to go stale. At the same moment the production composition root - registered ZERO of those thirteen targets. Both facts were true, because the tests constructed - their own registry from stub ports and then measured the thing they had just built. - The claim was about the DEPLOYED registry. The control could see a subject; it could not see THAT - subject. So when your evidence is a test, say plainly which of these it is: - - MECHANISM: the thing derives / fails closed / refuses the wrong payload. Stubs are correct - here, and a stub is the only way to test a fourteenth target that does not exist yet. - - WIRING: the composition root actually installs it. This one must drive the REAL constructor - -- "super::the_production_fn(...)" -- and must fail when a registration line is deleted. - A condition phrased as "X does not require hand-written Y per action" is a WIRING claim. Mechanism - evidence does not close it, however total the mechanism test is. If your redBaseline was produced - by deleting a line from a test fixture rather than from the composition root, you have proved the - fixture.: - If your change adds or modifies a gate, check, census, guard or invariant, answer TWO questions - in writing BEFORE you build it, and put the answers in enforcementPlacement: - (1) WHERE does it run in the sequence, and does its subject EXIST yet at that point? - (2) What is the FINEST distinction its data source can express? - Both have already shipped as no-ops in this program. A canonical-writer census was placed in a - reconcile script that runs BEFORE migrations, so it matched zero tables and its REVOKE loop - iterated nothing in every automated path — and the lane recorded "succeeds on a bare cluster" as a - feature, which is exactly how the no-op hid. Separately, a database-capability control was - specified to enforce per-CRATE ownership, but every crate connects as the same role (console_rt), - so the finest distinction available to it was per-ROLE and the crate boundary was never drawn. - A check that never runs and a check that runs blind both exit 0. Neither shows up in a test count. - THEREFORE: "examined zero subjects" MUST be a FAILURE, never a pass. And never claim a control - covers a distinction its data source cannot express — say what it actually enforces, and name the - residual gap in followUps. -PERIPHERALS ARE PART OF THE CHANGE, NOT A FOLLOW-UP: - A change is not done when the code compiles. Before you report done, find everything that - DESCRIBES the behaviour you changed and bring it with you: - - the module doc (//! and ///) of every file you touched, especially any comment that - ENUMERATES something you just made total, or claims a property you just changed; - - registries, rosters, baselines and ratchets that name what you added or removed; - - the bead / issue text, if the change makes its description wrong; - - any doc under docs/** that states the thing you changed as fact. - Docs here rot in ONE direction: they describe holds already lifted and problems already fixed, so - a stale doc reads as a live constraint and someone re-solves a solved problem. A module doc that - says "the three ways X can happen are each pinned separately" after you found a fourth is not an - inaccuracy, it is a false claim about a control. - SCOPE RULE, same as everywhere else: update the peripherals you OWN; for a leased one, report the - exact edit in followUps. Never leave a doc contradicting the code you just shipped, and never - silently widen scope to fix a doc you were not given. - - MECHANICAL, AND NOT OPTIONAL — some peripherals are GENERATED, and prose about keeping things - current does not update a checksum. A change that is otherwise entirely correct fails CI because a - file some script writes is now stale. - DO NOT work from a list of generators. The first version of this clause named exactly one (the - documentation manifest) and the very next lane was failed by a different one (the first-party BUCK - faces). A list of the faces you have been burned by is not a rule, it is a record of your own - history -- and this lock says two paragraphs down that the third spelling means the mechanism is - wrong. The mechanism is: REGENERATE, THEN ASK GIT. - run every generator the repo exposes for the areas your diff touches, then: - git status --porcelain # ANY output = your commit is incomplete - git is the oracle because it cannot be fooled by a face you did not think of. Two entry points - worth knowing, neither of which is the whole set: tools/buck/preflight.sh (what CI's preflight job - actually runs, covering every generated Buck face) and - node scripts/console/generate-documentation-manifest.mjs --write. - This is a POSTFLIGHT check: run it after your last edit, not before. It is here rather than left to - the reviewer because it is decidable by a command, and a lane that can run the command has no - business spending a review round on it. -THE THIRD SPELLING MEANS THE MECHANISM IS WRONG, NOT THE LIST: - If you are fixing the SAME class of bug for the third time in a different spelling, stop patching - and replace the mechanism. Measured: a gate hand-lexed Rust and was defeated by '} // end tests', - then 'use path::{A, B}', then a char literal, a block comment and a raw string; its hand-written - cfg rule was defeated by not(all(test)) and then by any(test, X). Each fix was correct and each - left a sibling live, because the set of spellings is open-ended. In both cases a total primitive - already existed (a real parser; has_table_privilege, which answers ownership, recursive - membership, column grants and superuser in one call) and replacing the enumeration DELETED more - code than it added. Before the third patch, ask: what already answers this question totally? - Say so in followUps if the total answer needs a dependency or a leased file -- a precise request - is a complete result. -TEST THE CONTROL BY EXECUTING IT, NOT BY READING IT: - A contains()/substring assertion over a gate's own source text is not evidence the gate works. A - reviewer inverted a census to 'IF leaked IS NOT NULL AND false THEN', killing it entirely, and all - 16 tests stayed green. Mutate the control itself and prove each mutation goes RED. -ROOT CAUSE, NOT SYMPTOM: - Before editing, grep every caller of the function you are about to touch. One guard in the shared - function beats a guard in each caller, and patching only the reported path leaves siblings broken. -CONTRACT TESTS ARE PART OF THE CHANGE: - Before you edit behaviour, find every test that ENCODES the behaviour you are changing — - including integration suites in other crates (backend/app/tests/** is the usual one). Changing a - contract necessarily breaks the tests asserting the old contract; that is the change, not a - regression. If such a test is OUTSIDE your owned root, STOP and report it in followUps with the - exact file and assertions BEFORE you build. Do not silently break it, and do not abandon the - work. This has been mis-scoped four times in this program: a lane authorised to fix a defect but - forbidden the crate holding it, authorised to create a crate but forbidden the workspace - manifest, and twice authorised to change a contract but forbidden the test that encodes it. -` - -const LOCK = BASE_LOCK + (ARGS.lockExtra || '') - -const BUILD_SCHEMA = { - type: 'object', - required: ['status', 'summary', 'filesChanged', 'redBaseline', 'verification', 'contractBreaches', 'enforcementPlacement', 'peripheralsUpdated'], - properties: { - status: { type: 'string', enum: ['done', 'partial', 'blocked'] }, - summary: { type: 'string' }, - filesChanged: { type: 'array', items: { type: 'string' } }, - redBaseline: { type: 'string', description: 'the failing test written FIRST and its exact failure output, before implementation' }, - verification: { type: 'string', description: 'EXACT commands run and EXACT pass/fail counts; an independent agent will re-run these' }, - commands: { type: 'array', items: { type: 'string' }, description: 'the verbatim commands an independent verifier should re-run' }, - contractBreaches: { type: 'string' }, - // Required, with an explicit n/a escape, so that OMITTING the answer is impossible rather than - // merely discouraged. A prose clause in the lock can be skimmed; a schema field cannot. - enforcementPlacement: { - type: 'string', - description: - 'If this change adds or modifies any gate/check/census/guard: WHERE in the sequence it runs and whether its subject exists at that point, and the FINEST distinction its data source can express. State how "examined zero subjects" fails. If the change adds no enforcement, write exactly: n/a - adds no enforcement.', - }, - // Required for the same reason as enforcementPlacement: a lock clause can be skimmed, a schema - // field cannot. Doc drift is invisible in a test count, which is exactly why it accumulates. - peripheralsUpdated: { - type: 'string', - description: - 'Every doc/comment/registry/bead that DESCRIBED the behaviour you changed: what you updated (you own it), and what you are reporting instead (leased). Include module docs whose claims your change invalidates. If nothing described this behaviour, write exactly: n/a - nothing described this behaviour, and say how you checked.', - }, - followUps: { type: 'string' }, - }, -} - -const REVIEW_SCHEMA = { - type: 'object', - required: ['verdict', 'findings'], - properties: { - verdict: { type: 'string', enum: ['reject', 'accept_with_findings', 'accept'] }, - findings: { - type: 'array', - items: { - type: 'object', - required: ['severity', 'claim', 'failureScenario', 'location', 'provenByExecution', 'ownerLease'], - properties: { - severity: { type: 'string', enum: ['blocker', 'major', 'minor'] }, - claim: { type: 'string' }, - failureScenario: { type: 'string' }, - location: { type: 'string' }, - // Severity alone is the wrong convergence signal, measured: a round converged on - // blockers=0 while both reviewers returned accept_with_findings carrying SIX distinct - // fail-opens they had each PROVEN BY RUNNING -- a census blind to the table owner, a - // partial-roster shrink that passed, a contains() wiring check defeated by a '#', a - // silent Docker-absent skip that certified an unexecuted census as green. Every one was - // labelled "major" and every one was waved through. What separates those from prose - // is not severity, it is whether the reviewer OBSERVED the failure. - provenByExecution: { - type: 'boolean', - description: 'TRUE only if YOU ran a command and OBSERVED the failure -- you have the output. Reasoning from source, however sound, is FALSE. Be strict: this field decides whether the lane rebuilds.', - }, - // The lease carve-out is a rule about WHOSE work it is, so it must survive the severity - // it was filed under; a lease item labelled blocker would otherwise make every - // test-adding lane permanently unconvergeable. - ownerLease: { - type: 'boolean', - description: 'TRUE if this is a companion edit the INTEGRATION OWNER must land (a leased path), not a defect in the lane. These never block convergence at any severity.', - }, - }, - }, - }, - oracleWeakened: { type: 'boolean' }, - scopeCreep: { type: 'boolean' }, - }, -} - -// An independent re-runner. Lanes self-report their own green; nobody checked that in the first -// four runs, and the integration owner had to re-run everything by hand afterwards. This makes the -// check part of the pipeline. -const VERIFY_SCHEMA = { - type: 'object', - // A GREEN MUST BE ANSWERED FOR, NOT MERELY ASSERTED. reproduced + contradictsClaim say the - // verifier ran something and agreed with it. They do not say WHAT it ran, whether any command - // selected zero tests and still exited 0, or whether the suite still proves as much as it did. - // Oracle integrity is the most common rejection cause in this programme and a STANDING review - // lens, yet this schema let a verifier certify a green without ever answering it — and a field - // that is optional is a field that gets skipped. Requiring the command list is the same - // discipline as `commands` on the build side: a claim nobody can re-run is a claim. - required: ['reproduced', 'actualResults', 'discrepancies', 'contradictsClaim', 'headSha', 'falseGreenRisk', 'commandsRun', 'oracleIntact'], - properties: { - // WHAT was verified, not just whether. Landing cherry-picks a worktree AFTER review finishes, - // and "clean working tree" is not a binding: a commit added after the reviewers finished is - // clean too, and would land as if reviewed. This is the immutable head the review round - // actually applies to, captured by the one agent that is read-and-run only. - headSha: { type: 'string', description: 'output of `git rev-parse HEAD` in the worktree, verbatim. This is the head the review round binds to; landing refuses anything else.' }, - reproduced: { type: 'boolean', description: 'true only if YOU ran the commands and saw the claimed results' }, - actualResults: { type: 'string', description: 'the exact output you observed, not what was claimed' }, - discrepancies: { type: 'string', description: 'any difference between claimed and observed, or "none"' }, - // The VERIFIER decides whether its findings matter -- not a regex over its prose. A previous - // version required discrepancies to be the literal string "none", so a conscientious verifier - // writing "four, all minor; none contradicts the verdict" failed the check and the lane was - // sent back for another build round. Convergence was effectively unreachable whenever the - // verifier ran: every lane with one reported converged=false while every review-only lane - // reported true. That defect manufactured rebuild rounds, which are the dominant cost here. - contradictsClaim: { type: 'boolean', description: 'TRUE only if what you observed CONTRADICTS the claimed result - a count that differs, a command that failed, a false green. Cosmetic differences (line numbers, timings, wording) are FALSE.' }, - falseGreenRisk: { type: 'string', description: 'did any command select ZERO tests and still exit 0? Answer for every command you ran; "none" is a valid answer, silence is not.' }, - commandsRun: { - type: 'array', - items: { type: 'string' }, - description: 'the commands you ACTUALLY ran, verbatim, in the order you ran them. Not the commands you were given — the ones you executed. An empty list means you verified nothing.', - }, - oracleIntact: { - type: 'boolean', - description: 'TRUE only if the suite still proves as much as it did: no test deleted, no #[ignore] added, no assertion loosened, and no test made to pass by conforming it to the defect. FALSE if you observed any of those OR could not tell.', - }, - }, -} - -// STANDING lenses run on EVERY lane, every round. They are not defaults. -// -// They used to be defaults -- `ARGS.lenses || [...]` -- and every single invocation of this harness -// passed `lenses`, so the fallback never once evaluated. Oracle integrity is the most common -// rejection cause in this program and it had never been reviewed for; it was only ever caught -// incidentally by a custom lens that happened to look. A default that is always overridden is not a -// default, it is dead code that reads as coverage. -// MODEL TIER: FRONTIER WHERE THE DECISION IS, CHEAP WHERE THE WORK IS. -// -// Every agent in this directory ran at the session model, which is the expensive one. Cursor's -// swarm measured the same worker fleet at $9,373 with a frontier model and $411 with a cheap one -// behind a frontier planner -- 23x -- on the argument that "few moments in a large task genuinely -// require frontier intelligence, such as the original decomposition, the design decisions, and -// certain trade-offs." This programme has now lost two runs to account limits, one of them 117 -// agents deep, so this is not a theoretical saving. -// -// The line we draw is NOT "cheap for reads, expensive for writes". It is: -// -// A CHEAPER TIER IS ALLOWED ONLY WHERE AN INDEPENDENT STRONGER PASS AUDITS THE RESULT. -// -// That is the same defence-in-depth argument as decorrelated review lenses: a weaker first pass is -// fine when a stronger one adjudicates it, and is NOT fine when its output is the final word. -// So: batched fan-out that feeds a challenge/reconcile phase may run cheap; anything whose verdict -// is terminal -- the adversarial reviewers, the independent verifier, the single writer, and any -// re-derivation of a dependency edge, where a reversed answer silently reschedules everything -- -// stays on the inherited model. -// NOT APPLIED TO THIS HARNESS'S OWN AGENTS, deliberately, and recorded because the first version of -// this block defined a WORKER constant and used it NOWHERE -- a rule written, documented, believed, -// and reachable by nothing, committed in the very change that added the rule. That is the defect the -// preflight beside this file exists to catch, and it caught it. -// -// A build agent's output DOES qualify under the rule above: adversarial reviewers and an independent -// verifier audit it. What stops it is arithmetic, not principle. This harness converges by ROUNDS, -// and a round costs a full build plus every reviewer plus the verifier, so a tier that needs even one -// extra round costs more than it saves. Unlike Cursor's workers, which execute a planner's explicit -// instructions, a lane here is handed an open-ended brief. Applying it needs a measurement nobody has -// made: rounds-to-converge per tier on the same briefs. Until then, the harnesses tiered are the ones -// whose fan-out is a mechanical read feeding a stronger pass, where a wrong answer costs one re-read. - -// TRIAL ONE LANE BEFORE COMMITTING THE FLEET. -// -// Bun's Zig-to-Rust port ran ONE implementer plus TWO adversarial reviewers over THREE files, and -// only then scaled to 64 agents across 4 worktrees. This harness has always dispatched every lane -// cold, and it has cost real runs: a wave where the brief was wrong in the same way for all four -// lanes is four wasted lanes, not one. -// -// `trial` names a lane key that must CONVERGE before the rest are dispatched. It is not a -// smoke test -- it is the same full build/review/verify cycle, so what it proves is that the LOCK, -// the accept criteria and the review lenses actually work against this tree, which is the part a -// wave gets wrong identically across every lane. -// -// Deliberately opt-in. For two independent lanes the serialisation costs more than it saves; the -// value appears when the lanes share a brief shape, which is exactly when they fail together. - -// Judges, verifiers and writers deliberately omit `model` so they inherit the session's. - -const STANDING_LENSES = [ - 'MAINTAINABILITY / COST OF CARRY — every other lens asks whether the change is CORRECT. This one asks what it costs to keep, and its default verdict is DELETE. (a) SPRAWL: does this add a doc, a crate, a script, a config, a workflow step or a process that duplicates one that exists? A second file describing the same fact is a future contradiction, not documentation. Name the existing thing it should have extended. (b) COMMENT BLOBBING: is there a paragraph of prose where a name would do? A comment that restates the code is noise that goes stale independently; a comment earning its place explains WHY, names a measurement, or records a rejected alternative. Twenty lines of comment over five lines of code is a defect in this repository, not thoroughness. (c) IDIOM: would a competent Rust/JS reader of this repo write it this way, or is it this author\'s private dialect? Hand-rolled parsing where a library exists, and enumerations where the language has a total construct, are the two that recur here. (d) AUTOMATION: is a human being asked to remember something a command could decide? If the accept criteria contain a step a script could run, that step WILL be skipped eventually. (e) UNDOCUMENTED-BUT-SHOULD-BE: the inverse of sprawl. A non-obvious constraint, a measured number, or a deliberate asymmetry that exists only in the author\'s head is undocumented, and the next lane will \'simplify\' it away. Report the NET line count of prose and config this change adds. A change that adds more explanation than behaviour needs a reason.', - 'CORRECTNESS + ORACLE INTEGRITY — does the change address the root cause, and does the suite still prove as much as before? Hunt for tests conformed to defects and assertions that would pass even if the behaviour were broken. Pick the load-bearing assertion, break the code it guards, and say whether it actually goes RED.', - 'PERIPHERAL DRIFT — read the diff, then go looking for what it made WRONG somewhere else. Does any module doc, /// comment, registry, roster, baseline, docs/** page or bead text still describe the behaviour as it was before this change? Pay closest attention to comments that ENUMERATE ("the three ways X can happen", "these are the cases") next to code this change made total or extended — those are false claims about a control, not stale prose. Verify the build agent\'s peripheralsUpdated field against the actual tree rather than trusting it, and check the reverse direction too: a doc updated to describe something the code does NOT do is worse than a stale one. Leased peripherals correctly reported in followUps are ownerLease=true, not defects.', - 'ENFORCEMENT PLACEMENT — for every gate/check/census/guard this change touches, ignore whether its LOGIC is right and ask only whether it can SEE its subject. (a) Where does it run in the sequence, and does its subject exist yet at that point? (b) What is the finest distinction its data source can express, and does the change claim a finer one? (c) Is the rule TOTAL over its domain, or is it an enumeration of spellings that a reviewer can always add one more to? If the change closes named cases rather than making the class unrepresentable, name the total primitive it should have used instead. (d) Does "examined zero subjects" fail, or pass? (d) Is it tested by EXECUTING it, or by a contains() over its own source text — mutate the control and check the tests go RED. Both failure modes have shipped here: a census that ran before migrations existed, and a per-crate rule enforced by a data source that only distinguishes roles. Verify the answers in enforcementPlacement rather than trusting them.', -] - -const LENSES = [ - ...STANDING_LENSES, - ...(ARGS.lenses || [ - 'BLAST RADIUS — does any public wire contract, stored format, or authorization outcome change shape? Who can now do what they could not before, or vice versa? Consider generated clients, rows already written under the old format, and callers in other crates.', - ]), -] - -// --- telemetry ------------------------------------------------------------- -// Measured across six runs of this harness: ~130 agents, ~12M tokens, ~5.7h wall-clock, at a ratio -// of 3.67 checkers per build. Wall-clock divided by builds is 7-13 min per ROUND, and reviews run -// in parallel, so a round costs roughly one build plus one review wave. -// -// That makes ROUNDS the scarce resource, not tokens. And an audit of why rounds were spent found -// most early ones went to defects in the HARNESS and the BRIEF, not in the lane's code: reviewers -// diffing HEAD~1, findings discarded with no feedback edge, empty-diff auto-reject, reviewers not -// told which paths were owner-leased, a lane authorised to create a crate but forbidden the -// workspace manifest. Each of those cost a full build cycle per affected lane. -// -// The lever follows directly: another reviewer is cheap, another BUILD ROUND is expensive. So -// classify what each round was actually spent on, and let the numbers say whether the next -// improvement belongs in the brief or in the code. -const TELEMETRY = { rounds: [], startedAt: null, checkers: [] } - -// What holds a lane open. Severity is the reviewer's opinion; provenByExecution is a fact about -// whether they watched it fail. A proven fail-open holds the lane regardless of the label it was -// filed under, and an owner lease releases it regardless -- because a lease is a statement about -// whose work it is, not about how bad it is. -function isBlocking(f) { - if (f.ownerLease === true) return false - if (f.severity === 'blocker') return true - return f.severity === 'major' && f.provenByExecution === true -} - -function classifyRejection(blockers, weakened, verifierOk, status, checkerDied) { - // Coarse, deliberately: the point is to see the SHAPE of wasted rounds, not to be precise. - const text = blockers.map((b) => `${b.claim} ${b.location}`).join(' ').toLowerCase() - // A round lost to a dead checker is an INFRASTRUCTURE cost, not a defect in the lane's claim. - // Filing it as unreproducible-claim would tell the human to fix a brief that was never wrong. - if (checkerDied) return 'checker-died' - if (!verifierOk) return 'unreproducible-claim' - if (weakened) return 'oracle-weakened' - if (/outside the authorised|in-scope path|scope list|not in scope/.test(text)) return 'scope-brief-defect' - if (/executes nowhere|ratchet|baseline|ci\.yml|workflow step|lease/.test(text)) return 'owner-lease' - if (status && status !== 'done') return 'incomplete' - if (blockers.length) return 'code-defect' - return 'unclassified' -} - -// --- cross-lane defect ledger: Bun's "fix the generator" ------------------- -const DEFECT_LEDGER = [] - -function recordDefects(laneKey, blockers, weakened) { - for (const b of blockers) { - const claim = (b.claim || '').trim() - if (!claim) continue - const key = claim.slice(0, 80).toLowerCase() - if (DEFECT_LEDGER.some((d) => d.key === key)) continue - DEFECT_LEDGER.push({ key, laneKey, claim, location: b.location || '' }) - } - if (weakened && !DEFECT_LEDGER.some((d) => d.key === '__oracle__')) { - DEFECT_LEDGER.push({ - key: '__oracle__', - laneKey, - claim: 'A reviewer judged that a change made the suite prove LESS than before (oracleWeakened=true).', - location: '', - }) - } -} - -function ledgerBlock(selfKey) { - const others = DEFECT_LEDGER.filter((d) => d.laneKey !== selfKey) - if (!others.length) return '' - return ` -=== DEFECT CLASSES ALREADY CAUGHT IN OTHER LANES THIS RUN === -Found by adversarial review of a DIFFERENT lane. They are CLASSES, not incidents — the same mistake -is available to you. Read them as things not to do. -${others.map((d, i) => `${i + 1}. (lane ${d.laneKey}) ${d.claim}${d.location ? `\n seen at: ${d.location}` : ''}`).join('\n')} -` -} - -function reviewPrompt(l, lens) { - return `You are an ADVERSARIAL CODE REVIEWER. Your ONLY job is to find bugs and reasons this change -does not work. You are NOT told the author's reasoning and must not go looking for it. -Assume the code is WRONG until the diff proves otherwise. - - cd ${l.wt} - git --no-pager diff ${TIP} - git status --porcelain - -*** Diff against ${TIP} exactly as above. Do NOT use 'git diff HEAD~1' — that shows an unrelated - earlier commit's own contents rather than the change under review. If what you are reading has - nothing to do with this lane's subject, you have the wrong diff; re-run the command above. *** - -An EMPTY diff is NOT automatically a rejection. Some work is a proof that no change was needed. -Judge whether the reported evidence supports that claim; reject only if it does not. - -YOUR REVIEW LENS: ${lens} - -Every finding needs a CONCRETE failure scenario: specific inputs or state producing wrong -behaviour. A finding without one is a label, not a finding, and is worthless. - -BLOCKER-level specifically: - - any test made to pass by conforming it to a defect instead of fixing the defect - - any deleted test, added #[ignore], or loosened assertion - - an expectation matching observed output rather than the intended contract, especially anything - environment-dependent (collation, locale, timezone, hash or iteration order) - - a security or authorization decision that FAILS OPEN on error, absence, staleness or - unavailability where the contract requires fail-closed - - a claimed invariant with no test, or a test that would still pass if the invariant were broken - - a backward-compatibility shim with no test proving it reads the legacy form - - edits outside the IN-SCOPE PATHS below - -*** NOT A BLOCKER — INTEGRATION-OWNER LEASES *** -Some files are deliberately withheld from every lane and are applied by the integration owner: - docs/program/executed-tests-baseline.json, .github/workflows/**, tools/ci/postgres-cargo-map.json, - backend/openapi/**, lockfiles, backend/crates/platform/db/migrations/** -A change that ADDS TESTS will therefore, by construction, leave the executed-tests ratchet red and -its new binaries unwired until the owner lands the companion edit. That is EXPECTED and is NOT a -defect in the lane's work. Report it with ownerLease=true and the exact companion edit required — -never as grounds to reject. Rejecting on it makes any test-adding lane permanently unconvergeable, -which would penalise exactly the changes that add the most coverage. -Do still reject if the lane EDITED one of those paths itself. - -*** TWO FIELDS DECIDE WHETHER THE LANE REBUILDS — SET THEM DELIBERATELY *** -provenByExecution: true ONLY if you ran a command and OBSERVED the failure, and you have the output - to show. A finding you reasoned out from the source is false here no matter how confident you are. - A "major" you PROVED forces another build round; a "major" you argued does not. This exists - because a previous round converged clean while both reviewers were holding six separately proven - fail-opens, each filed as an unproven-looking "major" and each waved through. -ownerLease: true for companion edits belonging to the integration owner, per the section above. - These never block at any severity, so a lease item is never a reason to withhold convergence. -If you proved a fail-open by running it, say so with provenByExecution=true even when you are -otherwise willing to accept the change. Your verdict and this field are independent. - -=== IN-SCOPE PATHS (authorised) === -${l.owned} -This is SCOPE, not rationale. It says which files were legitimately in play and nothing about -whether the author got them right. Edits inside are not scope creep by location alone but can -still be wrong. Edits outside are a contract breach — report as blockers. - -Set oracleWeakened=true if the suite now proves LESS than before. Default to rejecting when unsure.` -} - -function verifyPrompt(l, fix) { - const cmds = (fix.commands && fix.commands.length ? fix.commands : []).map((c) => ` ${c}`).join('\n') - return `You are an INDEPENDENT VERIFIER. You do not review code and you do not read the author's -reasoning. Your single job is to RE-RUN what was claimed and report what ACTUALLY happens. - - cd ${l.wt} - git rev-parse HEAD <- report this verbatim as headSha, FIRST, before anything else - -That SHA is what this whole review round binds to: the landing step will refuse to cherry-pick this -worktree if its head has moved since you read it. Report what you actually saw, never a guess. - -CLAIMED VERIFICATION: -${fix.verification} - -${cmds ? `COMMANDS TO RE-RUN VERBATIM:\n${cmds}` : 'The author listed no explicit commands. Derive them from the claimed verification above and say so.'} - -RULES: - - RUN the commands. Do not reason about whether they would pass. Reproduced=true only if you - personally observed the result. - - Report the EXACT output you saw, including counts, not the counts that were claimed. - - *** FALSE-GREEN CHECK: if any command reports running ZERO tests and still exits 0, that is a - false green — say so loudly in falseGreenRisk. In this repository --workflow-only selects zero - dark targets and exits 0, which is exactly this trap. *** - - If a command fails for an ENVIRONMENTAL reason (missing dependency, database not provisioned, - Docker down), say so explicitly rather than reporting the change as broken. - - Do NOT edit any file. You are read-and-run only. - -*** THREE FIELDS DECIDE WHETHER THIS GREEN COUNTS. A green you cannot answer for is not a green. *** - commandsRun: every command you ACTUALLY executed, verbatim and in order. Not the list you were - handed — the one you ran. An empty list means nothing was verified, and the lane - rebuilds. - falseGreenRisk: answer it for every command, not just the suspicious one. "none" is an answer. - oracleIntact: read the diff for deleted tests, added #[ignore], loosened assertions, and any - expectation rewritten to match observed output instead of the intended contract. - TRUE only if the suite still proves as much as before. If you could not tell, - that is FALSE — this is the most common rejection cause in this programme and - silence on it used to be free. - -Report any difference between what was claimed and what you observed, however small.` -} - -function buildPrompt(l, fb) { - // A rejection MUST arrive with something actionable. A previous run rejected a lane on verifier - // disagreement alone, rendered an empty "BLOCKERS:" list, and the lane reported: "the rejection - // came with no blocker text, so I re-derived the weakest point of my own diff". It recovered by - // luck. When there are no blockers, state the actual cause and fall back to lower-severity - // findings so the lane always has a concrete starting point. - let reasonLines = [] - if (fb) { - if (fb.weakened) reasonLines.push('A reviewer set oracleWeakened=true: the suite now proves LESS than before. Restoring the oracle is the highest priority.') - if (fb.verifierSaid) reasonLines.push(`The INDEPENDENT VERIFIER re-ran your commands and disagreed with your claimed result: ${fb.verifierSaid}`) - if (fb.notDone) reasonLines.push(`You reported status="${fb.notDone}" rather than "done". If that is genuinely blocked, say so precisely in followUps; if it is finishable, finish it.`) - if (!fb.blockers.length && !reasonLines.length) reasonLines.push('No blocker was recorded, which means the rejection came from a failed convergence check rather than a named finding. Re-derive the weakest point of your own diff and address it.') - } - - const blockerText = fb && fb.blockers.length - ? `BLOCKERS (must all be resolved):\n${fb.blockers.map((b, i) => `${i + 1}. [${b.severity}] ${b.claim}\n where: ${b.location}\n fails when: ${b.failureScenario}`).join('\n')}` - : (fb && fb.lesser && fb.lesser.length - ? `NO BLOCKERS were raised. Lower-severity findings, treat as the actionable list:\n${fb.lesser.map((b, i) => `${i + 1}. [${b.severity}] ${b.claim}\n where: ${b.location}\n fails when: ${b.failureScenario}`).join('\n')}` - : 'NO named findings were recorded this round.') - - const feedback = fb - ? ` -=== ROUND ${fb.round}: THIS LANE WAS REJECTED === -Your earlier change is already in the worktree. Amend it; do not revert wholesale. - -WHY IT WAS REJECTED: -${reasonLines.map((r) => ` - ${r}`).join('\n')} - -${blockerText} - -Fix the CAUSE, not the symptom named. If two findings share a root, fix the root once. -Reviewers see ONLY your diff, never your reasoning — if a finding looks like a misunderstanding, -that is itself a signal the change is not self-evident. Make the code clearer rather than arguing. -If a finding names a path you may not touch, say so in followUps rather than editing it. -` - : '' - - return `You are the IMPLEMENTER for lane "${l.key}"${l.bead ? ` (beads issue ${l.bead})` : ''}. -${feedback}${ledgerBlock(l.key)} -WORKTREE (yours alone, based on ${TIP}): - ${l.wt} -Work ONLY there. Do not touch any other worktree or the primary checkout. - -YOUR OWNED ROOT (the only place you may write): - ${l.owned} - -${l.brief} - -ACCEPTANCE: ${l.accept} - -METHOD — RED BASELINE FIRST, not optional: - 1. WRITE THE FAILING TEST FIRST. Encode the required behaviour as a test and RUN it. Capture the - exact failure output — that is your redBaseline. A lane that implements first and tests after - cannot demonstrate its test would have caught anything. - 2. Implement the smallest change that makes it pass. - 3. Re-run and report EXACT counts. Populate 'commands' with the verbatim commands you ran — an - independent verifier will re-run them and compare against what you claim. - 4. MUTATION-CHECK your own new assertions: break the thing you just fixed, confirm the suite goes - RED, then restore and confirm GREEN. An assertion that still passes when the behaviour is - broken proves nothing. Report this in verification. - 5. Commit only your owned paths. Do not push. - -Cold builds take several minutes; that is expected, not a hang. -If you cannot finish, report "partial" or "blocked" with the exact failure. An honest blocked -result is far more useful than a weakened test. - -${LOCK}` -} - -async function runLane(l) { - let fb = null - let last = { lane: l, fix: null, reviews: [], verify: null, blockers: [], rounds: 0, converged: false } - - // A lane whose implementer already finished gets REVIEW ONLY. Re-running it would duplicate - // committed work and risk a second writer in a worktree, and its diff still needs review. - if (l.reviewOnly) { - const checks = await parallel(LENSES.map((lens) => () => - agent(reviewPrompt(l, lens), { label: `review:${l.key}`, phase: 'Review', schema: REVIEW_SCHEMA }))) - // Same death accounting as the main path: a review-only lane that lost a standing lens has not - // been reviewed for the thing that lens exists to catch, and must not report converged. - const deadStanding = checks - .slice(0, STANDING_LENSES.length) - .map((r, i) => (r ? null : STANDING_LENSES[i].split(' —')[0])) - .filter(Boolean) - const reviews = checks.filter(Boolean) - const blockers = reviews.flatMap((v) => (v.findings || []).filter(isBlocking)) - const weakened = reviews.some((v) => v.oracleWeakened) - // scopeCreep blocks for the same reason oracleWeakened does: the review schema carries a - // dedicated flag, and the reviewer prompt defines an edit outside `l.owned` as a contract - // breach. A reviewer that sets the flag without ALSO restating it as a blocking finding was - // being ignored, so a lane could edit an unowned root, converge, and be handed to the lander. - // The flag is the reviewer's verdict; requiring them to say it twice is how it gets lost. - const creep = reviews.some((v) => v.scopeCreep) - recordDefects(l.key, blockers, weakened) - TELEMETRY.checkers.push({ dispatched: LENSES.length, returned: reviews.length, deadLenses: deadStanding }) - const converged = blockers.length === 0 && !weakened && !creep && deadStanding.length === 0 - log(`${l.key}: review-only -> ${reviews.length}/${LENSES.length} reviewers, ${blockers.length} blocker(s)${weakened ? ', ORACLE WEAKENED' : ''}${deadStanding.length ? `, STANDING LENS DIED: ${deadStanding.join(', ')}` : ''}`) - return { lane: l, fix: l.priorResult || null, reviews, verify: null, blockers, rounds: 0, converged } - } - - for (let round = 1; round <= MAX_ROUNDS; round++) { - const sfx = round > 1 ? `#${round}` : '' - - // Agent death is routine at this scale; retry once before giving up on the round. - let fix = await agent(buildPrompt(l, fb), { label: `build:${l.key}${sfx}`, phase: 'Build', schema: BUILD_SCHEMA }) - if (!fix) { - log(`${l.key}: implementer died in round ${round}, retrying once`) - fix = await agent(buildPrompt(l, fb), { label: `build:${l.key}${sfx}r`, phase: 'Build', schema: BUILD_SCHEMA }) - } - if (!fix) { - log(`${l.key}: implementer died twice in round ${round} — abandoning lane`) - last.rounds = round - break - } - - // Two adversarial readers plus, when there is something to falsify, one independent re-runner. - // - // The verifier exists to test a CLAIMED green by re-running the lane's own commands. A lane - // reporting "partial" or "blocked" is claiming nothing, so there is nothing to falsify and the - // round cannot converge regardless. Running it anyway re-executes full test suites for no - // decision value: in one measured phase, two consecutive rounds converged zero lanes, so six - // verifiers re-ran suites whose result could not have changed any outcome. Reviews still run - // on every round -- a rejected round's findings are exactly what the next build needs. - const claimsGreen = fix.status === 'done' - const checks = await parallel([ - ...LENSES.map((lens) => () => agent(reviewPrompt(l, lens), { label: `review:${l.key}${sfx}`, phase: 'Review', schema: REVIEW_SCHEMA })), - ...(claimsGreen ? [() => agent(verifyPrompt(l, fix), { label: `verify:${l.key}${sfx}`, phase: 'Review', schema: VERIFY_SCHEMA })] : []), - ]) - // A DEAD REVIEWER IS NOT AN ABSENT FINDING. parallel() resolves a died agent to null, and - // .filter(Boolean) used to make it disappear -- so a round could converge on one surviving - // reviewer while three others died, and the log said nothing. That is a false-green path, and - // it fired repeatedly in one measured session: session-limit kills took 4 of 7 agents from one - // run, 3 of 3 from another, and an adjacent workflow's synthesis step silently ran on 2 of 3 - // inputs. Convergence must know how many eyes actually reported. - // - // Index order from parallel() matches the dispatch order, so a null identifies WHICH lens died. - // STANDING lenses are non-negotiable: if one of them did not report, the lane has not been - // reviewed for oracle integrity, enforcement placement or peripheral drift, and it may not - // converge no matter what the survivors said. A custom lens dying is logged and tolerated, - // because forcing a whole rebuild round over it costs more than it saves. - const reviewSlots = checks.slice(0, LENSES.length) - const deadLenses = reviewSlots - .map((r, i) => (r ? null : LENSES[i].split(' —')[0].split(' ').slice(0, 3).join(' '))) - .filter(Boolean) - const deadStanding = reviewSlots - .slice(0, STANDING_LENSES.length) - .map((r, i) => (r ? null : STANDING_LENSES[i].split(' —')[0])) - .filter(Boolean) - const reviews = reviewSlots.filter(Boolean) - const verify = claimsGreen ? (checks[LENSES.length] || null) : null - if (!claimsGreen) log(`${l.key}: round ${round} reported "${fix.status}" — verifier skipped, nothing green to falsify`) - if (deadLenses.length) { - log(`${l.key}: round ${round} — ${reviews.length}/${LENSES.length} reviewers returned; DIED: ${deadLenses.join(', ')}`) - } - if (deadStanding.length) { - log(`${l.key}: round ${round} CANNOT CONVERGE — standing lens(es) never reported: ${deadStanding.join(', ')}`) - } - - const blockers = reviews.flatMap((v) => (v.findings || []).filter(isBlocking)) - const weakened = reviews.some((v) => v.oracleWeakened) - // scopeCreep blocks for the same reason oracleWeakened does: the review schema carries a - // dedicated flag, and the reviewer prompt defines an edit outside `l.owned` as a contract - // breach. A reviewer that sets the flag without ALSO restating it as a blocking finding was - // being ignored, so a lane could edit an unowned root, converge, and be handed to the lander. - // The flag is the reviewer's verdict; requiring them to say it twice is how it gets lost. - const creep = reviews.some((v) => v.scopeCreep) - recordDefects(l.key, blockers, weakened) - TELEMETRY.checkers.push({ dispatched: LENSES.length + (claimsGreen ? 1 : 0), returned: reviews.length + (verify ? 1 : 0), deadLenses }) - - // A lane is not green because it says so. The verifier must have reproduced it. - // The verifier judges whether its own findings contradict the claim; contradictsClaim is a - // required field, so there is no prose to parse and nothing to fall back to. - // - // A DEAD VERIFIER IS NOT A PASSED VERIFICATION. This used to read `!verify || (...)`, so a - // verifier killed by a session limit made verifierOk TRUE and the lane converged while the log - // said "independently re-verified" -- the exact false-green class this harness exists to - // remove, and worse than the rest because the log asserts the opposite of what happened. - // A dead CUSTOM lens is tolerated because four other eyes still read the diff; the verifier has - // no such substitute. It is the ONLY thing that re-runs a claimed green, so its death leaves - // the claim untested and the lane must rebuild. - // The deliberate exception stands: when the build claims nothing (`claimsGreen` false) no - // verifier is dispatched, there is nothing to falsify, and its absence is not a death. - // - // AND A GREEN NOBODY ANSWERED FOR IS NOT A GREEN. The schema now REQUIRES the command list, the - // false-green answer and an oracle-integrity verdict, but a schema is a request to an agent and - // this is the enforcement: a verifier that reproduced the result while leaving any of the three - // unanswered has told us it agreed without saying what it ran or whether the suite still proves - // anything. Absence is a NO here, never a yes. - const verifierDied = claimsGreen && !verify - // COUNT THE COMMANDS AGAINST WHAT WAS CLAIMED, not against zero. `length > 0` let a verifier - // that re-ran ONE of the implementer's five commands satisfy the check, and with reproduced=true - // and contradictsClaim=false the lane converged while four suites were never independently run. - // "Some of it was re-run" is not independent verification of a green; it is a sample of one. - const claimedCommands = Array.isArray(fix.commands) ? fix.commands.filter((c) => typeof c === 'string' && c.trim()) : [] - // Every commandsRun entry must be a non-empty string. Filtering blanks used to turn - // `commandsRun: [""]` (or `["cargo test", ""]`) into a quieter shape and let a hollow list - // look like "no commands named" rather than "the verifier answered with nothing". Fail closed: - // any blank or non-string entry means the verifier did not answer for what it ran. - const rawRan = verify && Array.isArray(verify.commandsRun) ? verify.commandsRun : [] - const commandsRunWellFormed = rawRan.length > 0 - && rawRan.every((c) => typeof c === 'string' && c.trim() !== '') - const ranCommands = commandsRunWellFormed ? rawRan.map((c) => c.trim()) : [] - // Compared as a SET over normalised text rather than by count, because a verifier that ran the - // same command five times would satisfy a count and prove nothing. Coverage is against EVERY - // claimed command, not merely "ran at least one". - const norm = (c) => c.replace(/\s+/g, ' ').trim() - const ranSet = new Set(ranCommands.map(norm)) - const unrun = claimedCommands.filter((c) => !ranSet.has(norm(c))) - // A done implementer that omits `commands` or returns only blanks used to make - // `unrun.length === 0` vacuously true: the verifier could name any command of its own and the - // lane converged without independently re-running anything the build claimed. Status=done - // requires a nonempty claimed-command set before coverage can be judged. - const verifierAnswered = !!verify - && claimedCommands.length > 0 - && commandsRunWellFormed - && ranCommands.length > 0 - && unrun.length === 0 - && typeof verify.falseGreenRisk === 'string' && verify.falseGreenRisk.trim() !== '' - && verify.oracleIntact === true - const verifierOk = !claimsGreen - || (!!verify && verify.reproduced === true && verify.contradictsClaim === false && verifierAnswered) - const verifierSaid = verifierOk - ? null - : (verifierDied - ? 'the INDEPENDENT VERIFIER died before reporting, so nothing re-ran your claimed green. Re-run your own commands and report the exact output.' - : (claimedCommands.length === 0 - ? `the build claimed status=done but named no well-formed commands (${JSON.stringify(fix.commands)}); a green with nothing claimed cannot be independently verified. List the commands you ran.` - : (unrun.length - ? `the verifier re-ran ${ranCommands.length} command(s) but the build claimed ${claimedCommands.length}; these were NEVER independently run: ${unrun.join(' | ')}. A green re-run of part of the evidence is a sample, not a verification.` - : (verify && verify.reproduced === true && verify.contradictsClaim === false - ? `the INDEPENDENT VERIFIER reproduced your result but could not answer for it: commandsRun=${JSON.stringify(verify.commandsRun)}, falseGreenRisk=${JSON.stringify(verify.falseGreenRisk)}, oracleIntact=${verify.oracleIntact}. Either it ran nothing it could name, or it judged the oracle no longer intact. Make the commands reproducible and show that the suite still proves what it did before.` - : `reproduced=${verify && verify.reproduced}; contradictsClaim=${verify && verify.contradictsClaim}; discrepancies=${verify && verify.discrepancies}; falseGreenRisk=${verify && verify.falseGreenRisk}`)))) - if (verifierDied) log(`${l.key}: round ${round} CANNOT CONVERGE — VERIFIER DIED; the claimed green was never re-run`) - - last = { lane: l, fix, reviews, verify, blockers, rounds: round, converged: false } - - if (fix.status === 'done' && blockers.length === 0 && !weakened && !creep && verifierOk && deadStanding.length === 0) { - last.converged = true - log(`${l.key}: CONVERGED round ${round} (independently re-verified by ${reviews.length}/${LENSES.length} reviewers)`) - break - } - - const cause = classifyRejection(blockers, weakened, verifierOk, fix.status, verifierDied || deadStanding.length > 0) - TELEMETRY.rounds.push({ lane: l.key, round, cause, blockers: blockers.length, weakened, verifierOk }) - log(`${l.key}: round ${round} -> status=${fix.status}, ${blockers.length} blocker(s)${weakened ? ', ORACLE WEAKENED' : ''}${verifierOk ? '' : ', VERIFIER DISAGREED'} [cause: ${cause}]`) - if (round === MAX_ROUNDS) { - log(`${l.key}: ESCALATION — hit MAX_ROUNDS=${MAX_ROUNDS} unconverged; ${blockers.length} blocker(s) survive`) - break - } - // Carry lower-severity findings and the status so the next round always has something concrete - // even when no blocker was raised. - const lesser = reviews.flatMap((v) => (v.findings || []).filter((f) => !isBlocking(f))).slice(0, 6) - fb = { - round: round + 1, - blockers, - lesser, - weakened, - verifierSaid, - notDone: fix.status === 'done' ? null : fix.status, - } - } - return last -} - -phase('Build') - -let results -if (ARGS.trial) { - const first = LANES.find((l) => l.key === ARGS.trial) - if (!first) { - throw new Error(`lane-fanout: trial names lane "${ARGS.trial}", which is not in lanes: ${LANES.map((l) => l.key).join(', ')}`) - } - if (LANES.length < 2) throw new Error('lane-fanout: trial with fewer than two lanes serialises for nothing') - log(`TRIAL: running lane "${first.key}" alone before committing the other ${LANES.length - 1}`) - const trial = await runLane(first) - const converged = trial && trial.converged - if (!converged) { - // The whole point: a brief that is wrong is usually wrong the SAME way for every lane, so - // dispatching the rest would multiply one defect by N rather than discover N defects. - log(`TRIAL FAILED — not dispatching the remaining ${LANES.length - 1} lane(s).`) - log('The lock, the accept criteria or the review lenses did not hold against this tree. Fix the') - log('brief, not the lane, then re-run: the other lanes would have failed the same way.') - return { - headline: [ - `TRIAL LANE "${first.key}" DID NOT CONVERGE — fleet not dispatched`, - `${LANES.length - 1} lane(s) held back deliberately, not dropped`, - ], - trial, - heldBack: LANES.filter((l) => l.key !== first.key).map((l) => l.key), - } - } - log(`TRIAL CONVERGED — dispatching the remaining ${LANES.length - 1} lane(s)`) - const rest = await parallel(LANES.filter((l) => l.key !== first.key).map((l) => () => runLane(l))) - results = [trial, ...rest] -} else { - results = await parallel(LANES.map((l) => () => runLane(l))) -} - -const out = results.filter(Boolean).map((r) => ({ - lane: r.lane.key, - bead: r.lane.bead || null, - converged: r.converged, - rounds: r.rounds, - status: r.fix ? r.fix.status : 'agent_failed', - summary: r.fix ? r.fix.summary : null, - redBaseline: r.fix ? r.fix.redBaseline : null, - filesChanged: r.fix ? r.fix.filesChanged : [], - claimedVerification: r.fix ? r.fix.verification : null, - independentlyReproduced: r.verify ? r.verify.reproduced : null, - verifierObserved: r.verify ? r.verify.actualResults : null, - verifierDiscrepancies: r.verify ? r.verify.discrepancies : null, - falseGreenRisk: r.verify ? r.verify.falseGreenRisk : null, - contractBreaches: r.fix ? r.fix.contractBreaches : null, - followUps: r.fix ? r.fix.followUps : null, - verdicts: (r.reviews || []).map((v) => v.verdict), - oracleWeakened: (r.reviews || []).some((v) => v.oracleWeakened), - remainingBlockers: r.blockers || [], -})) - -const conv = out.filter((o) => o.converged).map((o) => o.lane) -const stuck = out.filter((o) => !o.converged).map((o) => `${o.lane}(${o.rounds}r,${o.remainingBlockers.length}b)`) -log(`CONVERGED: ${conv.join(', ') || 'none'} | UNCONVERGED: ${stuck.join(', ') || 'none'}`) -if (DEFECT_LEDGER.length) log(`defect classes seen this run: ${DEFECT_LEDGER.length}`) - -// --- terminal phase: LAND --------------------------------------------------- -// This phase exists because the harness did not have one, and its absence was a process defect -// rather than an oversight. A lane used to converge and stop, leaving its commits on a detached -// HEAD in a worktree nobody landed. Accumulation was therefore guaranteed by construction: measured -// 2026-08-08, ZERO open PRs against a main that twelve-plus worktrees sat above -- one at +99, one -// at +95, one at +61 -- which is hours of correct work nobody could review. -// -// The fix is NOT a consolidation phase. Consolidation-as-an-institution is the bureaucracy you get -// from refusing to fix the thing that generates the debt. The fix is that landing is part of the -// pipeline, because the cost curve is superlinear: one lane's diff onto an integration branch is a -// minutes-long rebase and STAYS minutes; twelve lanes at once is not twelve times worse, because -// conflicts multiply and the authority train rebinds on every fix. -// -// So: neither N PRs nor one heroic merge. ONE long-lived integration branch that every converged -// lane lands on immediately, kept continuously mergeable, PR'd on a cadence the human chooses. -// -// ONE WRITER. A single agent lands every lane in sequence. Landing is the one place where parallel -// agents would share a write target, and two writers on one branch is the failure that has already -// cost this program a round. -const LAND = ARGS.land !== false && conv.length > 0 -const INTEGRATION_BRANCH = ARGS.integrationBranch || 'integration/lane-fanout' -let landed = null -if (LAND) { - phase('Land') - // Built from the ORIGINAL result objects, never from `out`. `out` flattens `lane` to a STRING - // (`lane: r.lane.key`), so the prompt's `o.lane.key` / `o.lane.wt` / `o.fix` were all undefined - // and the integration owner was handed "undefined — worktree undefined", no files, no follow-ups. - // The landing phase ran and told the owner nothing; a phase that runs on garbage has not run. - const landable = results.filter(Boolean).filter((r) => r.converged) - landed = await agent( - `You are the integration owner. ${landable.length} lane(s) converged and must be LANDED NOW, in one sequence, by you alone. - -INTEGRATION BRANCH: ${INTEGRATION_BRANCH} BASE: ${TIP} - -LANES TO LAND, in this order: -${landable.map((r, i) => `${i + 1}. ${r.lane.key} — worktree ${r.lane.wt} - REVIEWED HEAD: ${(r.verify && r.verify.headSha) || 'NOT CAPTURED — refuse this lane and report it'} - files: ${(r.fix && r.fix.filesChanged ? r.fix.filesChanged : []).join(', ') || '(see the worktree)'} - leased edits it reported instead of making: ${(r.fix && r.fix.followUps) || 'none'}`).join('\n')} - -WHY THIS RUNS AT ALL: work that converges but does not land accumulates, and the cost of landing it -grows faster than linearly. Landing one lane now is cheap; landing twelve later is not. - -BEFORE YOU TOUCH ANYTHING: - For each lane's worktree run 'git status --porcelain', 'git rev-parse HEAD' and 'git log --oneline -3'. - A worktree with uncommitted changes has a LIVE WRITER -- do not land it, report it as skipped and - say so. A finished workflow id is NOT evidence that nothing is writing; the working tree is. - - *** LAND ONLY THE REVIEWED HEAD. If 'git rev-parse HEAD' does not equal the REVIEWED HEAD listed - for that lane above, do NOT land it: skip it and report both SHAs. A clean worktree is NOT a - binding -- a commit added after the reviewers finished is clean too, and cherry-picking it - would land unreviewed code under a reviewed lane's name. The same applies to a lane whose - reviewed head was NOT CAPTURED: refuse it. Never resolve a mismatch by re-reviewing it - yourself; you are the lander, and a lane whose head moved goes back through the harness. *** - -HOW TO LAND: - Create or fast-forward ${INTEGRATION_BRANCH} from ${TIP} in the PRIMARY repo. For each lane in - order, apply that lane's commits (cherry-pick its range, or format-patch/am). After EACH lane: - build and test the packages that lane touched, and stop at the first failure rather than piling - the next lane on top of a broken tree. - PERMITTED: branch create, checkout of the integration branch, cherry-pick, am, add, commit, and - read-only git everywhere. - FORBIDDEN: push, force-push, PR creation, reset --hard, clean, stash, rebase of anything already - landed, and ANY write inside a lane worktree. Pushing and PR-opening are the human's call; your - job is to make the branch exist, be correct, and be continuously mergeable. - NEVER use a merge that would create an unsigned two-parent head -- that breaks the authority train - and the error will blame the signature. - -CONFLICTS: if a lane conflicts, resolve ONLY if the resolution is mechanical and obvious; otherwise -stop, leave the branch at the last good lane, and report the conflicting hunks exactly. A wrong -conflict resolution is far more expensive than a skipped lane. - -REPORT: the branch and its head SHA, which lanes landed and which were skipped and why, the exact -verification you ran after each lane with its real output, every leased edit still outstanding across -all lanes (deduplicated -- these are what the human must apply before the PR is green), and the -exact 'gh pr create' command you did NOT run.`, - { label: 'land', phase: 'Land', schema: { - type: 'object', - required: ['branch', 'headSha', 'landedLanes', 'skippedLanes', 'verification', 'outstandingLeasedEdits'], - properties: { - branch: { type: 'string' }, - headSha: { type: 'string' }, - landedLanes: { type: 'array', items: { type: 'string' } }, - skippedLanes: { type: 'array', items: { type: 'string' }, description: 'lane key + the reason, e.g. "p4-tai: live writer, 4 files dirty"' }, - verification: { type: 'string', description: 'exact commands and real output, per lane' }, - conflicts: { type: 'string' }, - outstandingLeasedEdits: { type: 'string', description: 'deduplicated across lanes; what the human must apply before a PR can be green' }, - prCommand: { type: 'string', description: 'the gh pr create command, NOT run' }, - }, - } }, - ) - if (landed) { - log(`LANDED on ${landed.branch} @ ${landed.headSha}: ${(landed.landedLanes || []).join(', ') || 'none'}`) - if ((landed.skippedLanes || []).length) log(`NOT LANDED: ${landed.skippedLanes.join(' | ')}`) - } else { - log('LAND phase returned nothing — converged work is still sitting in worktrees; land it by hand') - } -} else if (conv.length === 0) { - log('nothing converged, so nothing to land') -} else { - log(`land disabled by args; ${conv.length} converged lane(s) left in their worktrees — this is how the +99 backlog happened`) -} - -// Where did the rounds go? Rounds are the scarce resource, so this is the number that should drive -// the next improvement. A run dominated by scope-brief-defect means fix the BRIEF; by code-defect -// means the lanes are genuinely hard; by owner-lease means the lease boundary is drawn wrong. -const byCause = {} -for (const r of TELEMETRY.rounds) byCause[r.cause] = (byCause[r.cause] || 0) + 1 -const wasted = (byCause['scope-brief-defect'] || 0) + (byCause['owner-lease'] || 0) -const buildRounds = out.reduce((n, o) => n + (o.rounds || 0), 0) - -// SERIAL DEPTH is what wall-clock actually tracks — measured across this harness's runs, per-step -// latency sits at 9-14 min and total time follows depth almost exactly while ignoring width: -// 12 agents at depth 6 took 74 min; 36 agents at depth 6 took 63 min. Three times the width, no -// slower. So adding reviewers is nearly free and adding a serial step is not. -// This harness is already flat WITHIN a round -- build, then one parallel block of reviewers plus -// the verifier -- so depth is 2 per round and nothing here can be unstacked further. All remaining -// depth is ROUNDS, which makes brief quality the only real lever on wall-clock. -const maxRounds = out.reduce((n, o) => Math.max(n, o.rounds || 0), 0) -const depth = maxRounds * 2 -log(`telemetry: ${buildRounds} build round(s); serial depth ~${depth} (${maxRounds} round(s) x 2: build then one parallel check block)`) -log(`telemetry: rejection causes ${JSON.stringify(byCause)}`) -if (wasted) log(`telemetry: ${wasted}/${TELEMETRY.rounds.length} rejected round(s) were BRIEF or LEASE defects, not code — fix those in the brief, not the lane`) - -// Headline FIRST and compact, because the result file gets truncated on long runs and the journal -// stores content-hash labels rather than the lane keys passed in — so a truncated tail leaves -// verdicts unattributable to lanes. One line per lane, before anything verbose. -const headline = out.map((o) => - `${o.lane}: ${o.converged ? 'CONVERGED' : 'UNCONVERGED'} r${o.rounds} ${o.status} blockers=${(o.remainingBlockers || []).length} weakened=${o.oracleWeakened} verified=${o.independentlyReproduced}`) -log(`HEADLINE | ${headline.join(' | ')}`) - -return { - headline, - lanes: out, - // The landing result must be RETURNED, not merely logged. Caught by the offline preflight on its - // first run: the Land phase executed and its branch and head SHA went nowhere, so the caller could - // not tell where the work went -- a phase that runs and reports nothing is barely better than the - // missing phase it replaced. - landed, - defectClasses: DEFECT_LEDGER.map((d) => ({ lane: d.laneKey, claim: d.claim })), - telemetry: { - buildRounds, - serialDepth: depth, - // Report what ACTUALLY reported, not what was dispatched. The previous expression was - // ((LENSES.length + 1) * buildRounds) / buildRounds -- algebraically just LENSES.length + 1, - // a constant that could never observe a dead agent. Telemetry that cannot be wrong is not - // telemetry, and this harness lost checkers to session limits in most of its measured runs. - checkersDispatched: TELEMETRY.checkers.reduce((n, c) => n + c.dispatched, 0), - checkersReturned: TELEMETRY.checkers.reduce((n, c) => n + c.returned, 0), - checkersPerBuild: TELEMETRY.checkers.length - ? +(TELEMETRY.checkers.reduce((n, c) => n + c.returned, 0) / TELEMETRY.checkers.length).toFixed(2) - : 0, - deadCheckers: TELEMETRY.checkers.flatMap((c) => c.deadLenses), - rejectionCauses: byCause, - briefOrLeaseRounds: wasted, - rounds: TELEMETRY.rounds, - }, -} diff --git a/.claude/workflows/lane-fanout.test.mjs b/.claude/workflows/lane-fanout.test.mjs deleted file mode 100644 index a7ebd8cbc..000000000 --- a/.claude/workflows/lane-fanout.test.mjs +++ /dev/null @@ -1,1358 +0,0 @@ -// Offline preflight for lane-fanout.js. Run it BEFORE every dispatch: -// -// node .claude/workflows/lane-fanout.test.mjs -// -// It exists because two classes of defect are invisible to reading, and both shipped here: -// -// 1. SYNTAX. An unescaped backtick inside a prompt template literal ends the literal. It -// silently truncated BASE_LOCK once; the run looked normal and the lock was half gone. -// `node --check` cannot see this — it rejects the top-level await and tells you nothing. -// 2. WIRING. A rule can be written, documented, believed, and reachable by nothing: -// - `LENSES = ARGS.lenses || [...]` meant the standing lenses NEVER ran, because every -// invocation passed `lenses`. Oracle integrity, the most common rejection cause in this -// program, had never once been reviewed for. -// - `verifierOk` matched the literal string "none" in the verifier's prose, so a verifier -// writing "four, all minor; none contradicts the verdict" failed the check. Convergence -// was unreachable whenever the verifier ran, and the harness manufactured rebuild rounds. -// - convergence keyed on `severity === 'blocker'`, so a run reported CONVERGED while both -// reviewers held six separately PROVEN fail-opens, every one filed "major". -// -// The harness is driven with STUB agents, so this proves the ENFORCEMENT LOGIC, not any agent's -// judgement. It is offline, free, and takes under a second. - -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' - -const HERE = path.dirname(fileURLToPath(import.meta.url)) -// The RED baseline for any change to these dispatchers is THIS file run against the PRE-FIX -// sources (`git show :.claude/workflows/.js` into a scratch dir, then point this at it). -// Without the override the preflight can only ever measure the tree it ships with, so "it would -// have gone red on the old code" is a claim nobody can re-run. CI passes nothing and gets HERE. -const SRCDIR = process.env.LANE_FANOUT_SRCDIR || HERE -const SRC = fs.readFileSync(path.join(SRCDIR, 'lane-fanout.js'), 'utf8') - -// Compile exactly as the harness evaluates it: an async function body with these globals, so -// top-level await and return are legal. This is the only honest syntax check. -const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor -let run -try { - run = new AsyncFunction( - 'args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', - SRC.replace(/^export const meta = /m, 'const meta = '), - ) -} catch (e) { - console.error('FAIL compile —', e.message) - console.error(' An unescaped ` inside a prompt template literal is the usual cause.') - process.exit(1) -} -console.log('PASS compile — body parses as the harness evaluates it') - -let failures = 0 // eslint-disable-line prefer-const -const check = (name, ok, detail) => { - // JSON.stringify(undefined) is undefined, not "undefined", so a FAILING assertion called without a - // detail threw TypeError here and killed the whole preflight mid-run — losing every assertion after - // it AND the failure count, so the harness reported nothing rather than a red. The reporter's own - // failure path was the one path no assertion exercised. It is exercised at the bottom of this file. - const shown = detail === undefined ? '(no detail)' : String(JSON.stringify(detail)).slice(0, 500) - console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${ok ? '' : ' :: ' + shown}`) - if (!ok) failures++ -} - -// Sequential is fine for a logic test; parallel() only needs to run every thunk and collect. -const parallel = async (thunks) => { - const out = [] - for (const t of thunks) { try { out.push(await t()) } catch { out.push(null) } } - return out -} -const pipeline = async (items, ...stages) => { - const out = [] - for (const [i, it] of items.entries()) { - let v = it - for (const s of stages) v = await s(v, it, i) - out.push(v) - } - return out -} - -const BUILD = (over = {}) => ({ - status: 'done', summary: 's', filesChanged: ['x.rs'], redBaseline: 'RED', verification: 'ok', - contractBreaches: 'none', enforcementPlacement: 'n/a - adds no enforcement', - // Default must name a real command that VERIFY re-runs. `commands: []` used to converge - // vacuously against any verifier command — that is the fail-open under test below. - peripheralsUpdated: 'n/a - nothing described this behaviour', followUps: '', - commands: ['cargo test -p x'], ...over, -}) -const FINDING = (over = {}) => ({ - severity: 'major', claim: 'c', failureScenario: 'f', location: 'l', - provenByExecution: false, ownerLease: false, ...over, -}) -const REVIEW = (over = {}) => ({ verdict: 'accept', findings: [], oracleWeakened: false, scopeCreep: false, ...over }) -const VERIFY = (over = {}) => ({ - reproduced: true, actualResults: 'a', discrepancies: 'none', contradictsClaim: false, - falseGreenRisk: 'none', commandsRun: ['cargo test -p x'], oracleIntact: true, headSha: 'cafe1234', ...over, -}) - -// Records every label the harness actually dispatched, which is how we prove a rule is REACHABLE -// rather than merely written. -const mkAgent = (opts = {}) => { - const seen = [] - const fn = async (prompt, o = {}) => { - const label = o.label || '' - seen.push({ label, prompt }) - if (label.startsWith('build:')) return opts.build ? opts.build(seen) : BUILD() - if (label.startsWith('review:')) return opts.review ? opts.review(prompt, seen) : REVIEW() - if (label.startsWith('verify:')) return opts.verify ? opts.verify() : VERIFY() - if (label === 'land') return opts.land ? opts.land() : { branch: 'integration/x', headSha: 'deadbee', landedLanes: ['a'], skippedLanes: [], verification: 'v', outstandingLeasedEdits: 'none', prCommand: 'gh pr create ...' } - return 'REPORT' - } - fn.seen = seen - return fn -} - -const LANE = (over = {}) => ({ key: 'a', bead: 'b', wt: '/w', owned: 'x/**', brief: 't', accept: 'a', ...over }) -const ARGS = (over = {}) => ({ tip: 'abc1234', lanes: [LANE()], maxRounds: 1, land: false, ...over }) - -const go = (args, agent = mkAgent(), logs = []) => - run(args, agent, parallel, pipeline, (m) => logs.push(m), () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => {}) - -const threw = async (args) => { - try { await go(args); return null } catch (e) { return e.message } -} - -// --- 1. An option the harness does not read must ABORT, never be silently dropped. ---------- -{ - const m = await threw(ARGS({ prose_hardening: false })) - check('unknown top-level arg aborts', !!m && /prose_hardening/.test(m), m) - - const m2 = await threw(ARGS({ lanes: [LANE({ scopes: ['x/'] })] })) - check('unknown per-lane key aborts', !!m2 && /scopes/.test(m2), m2) - - const m3 = await threw(ARGS({ lens: ['typo'] })) - check('a typo in `lenses` aborts rather than silently using defaults', !!m3 && /lens\b/.test(m3), m3) - - const ok = await go(ARGS()) - check('every documented arg is accepted', !!ok && Array.isArray(ok.headline), ok && Object.keys(ok)) -} - -// --- 2. Standing lenses must survive custom lenses (the dead-default defect). --------------- -{ - const agent = mkAgent() - await go(ARGS({ lenses: ['CUSTOM ONE', 'CUSTOM TWO'] }), agent) - const reviews = agent.seen.filter((s) => s.label.startsWith('review:')) - const hasOracle = reviews.some((r) => /ORACLE INTEGRITY/.test(r.prompt)) - const hasPlacement = reviews.some((r) => /ENFORCEMENT PLACEMENT/.test(r.prompt)) - const hasDrift = reviews.some((r) => /PERIPHERAL DRIFT/.test(r.prompt)) - const hasCustom = reviews.some((r) => /CUSTOM ONE/.test(r.prompt)) - const hasMaintainability = reviews.some((r) => /COST OF CARRY/.test(r.prompt)) - // Sized against the standing set rather than pinned to a literal. The count was hard-coded to 5, - // so ADDING a standing lens -- the intended way to strengthen review -- read as a regression and - // blocked dispatch. An assertion that a legitimate improvement breaks is an assertion that will - // eventually be "fixed" by deleting the improvement. Two lanes have already learned this shape: - // a rule stated as a list of members instead of a relationship over the set. - const standing = SRC.match(/const STANDING_LENSES = \[([\s\S]*?)\n\]/) - const standingCount = standing ? (standing[1].match(/^\s{2}'/gm) || []).length : -1 - check('custom lenses ADD to standing lenses, never replace them', - standingCount > 0 && reviews.length === standingCount + 2 && - hasOracle && hasPlacement && hasDrift && hasMaintainability && hasCustom, - { count: reviews.length, standingCount, hasOracle, hasPlacement, hasDrift, hasMaintainability, hasCustom }) -} - -// --- 3. Convergence keys on PROOF, not on the severity label. ------------------------------- -{ - const provenMajor = mkAgent({ review: () => REVIEW({ verdict: 'accept_with_findings', findings: [FINDING({ severity: 'major', provenByExecution: true })] }) }) - const r1 = await go(ARGS(), provenMajor) - check('a PROVEN major blocks convergence', r1.lanes[0].converged === false, r1.headline) - - const arguedMajor = mkAgent({ review: () => REVIEW({ verdict: 'accept_with_findings', findings: [FINDING({ severity: 'major', provenByExecution: false })] }) }) - const r2 = await go(ARGS(), arguedMajor) - check('an ARGUED major does not block convergence', r2.lanes[0].converged === true, r2.headline) - - const leasedBlocker = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker', provenByExecution: true, ownerLease: true })] }) }) - const r3 = await go(ARGS(), leasedBlocker) - check('an owner lease releases at ANY severity', r3.lanes[0].converged === true, r3.headline) - - const realBlocker = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker' })] }) }) - const r4 = await go(ARGS(), realBlocker) - check('a real blocker blocks', r4.lanes[0].converged === false, r4.headline) -} - -// --- 4. The oracle may never be weakened, whatever the findings say. ------------------------ -{ - const weakened = mkAgent({ review: () => REVIEW({ oracleWeakened: true }) }) - const r = await go(ARGS(), weakened) - check('oracleWeakened blocks convergence with zero findings', r.lanes[0].converged === false, r.headline) -} - -// --- 5. The verifier decides via a BOOLEAN, not a regex over its prose. --------------------- -{ - const conscientious = mkAgent({ verify: () => VERIFY({ discrepancies: 'four, all minor; none contradicts the verdict', contradictsClaim: false }) }) - const r = await go(ARGS(), conscientious) - check('a wordy but non-contradicting verifier still converges', r.lanes[0].converged === true, r.headline) - - const contradicts = mkAgent({ verify: () => VERIFY({ contradictsClaim: true }) }) - const r2 = await go(ARGS(), contradicts) - check('contradictsClaim=true blocks convergence', r2.lanes[0].converged === false, r2.headline) - - const notReproduced = mkAgent({ verify: () => VERIFY({ reproduced: false }) }) - const r3 = await go(ARGS(), notReproduced) - check('reproduced=false blocks convergence', r3.lanes[0].converged === false, r3.headline) -} - -// --- 6. Nothing green to falsify => no verifier. Rounds are the scarce resource. ------------ -{ - const partial = mkAgent({ build: () => BUILD({ status: 'partial' }) }) - await go(ARGS(), partial) - check('a non-done build skips the verifier', partial.seen.filter((s) => s.label.startsWith('verify:')).length === 0, - partial.seen.map((s) => s.label)) - - const done = mkAgent() - await go(ARGS(), done) - check('a done build runs the verifier', done.seen.filter((s) => s.label.startsWith('verify:')).length === 1, - done.seen.map((s) => s.label)) -} - -// --- 7. Agent death is routine at this scale; retry once, then abandon. --------------------- -{ - let n = 0 - const flaky = mkAgent({ build: () => { n++; return n === 1 ? null : BUILD() } }) - const r = await go(ARGS(), flaky) - check('a dead implementer is retried once and the round proceeds', n === 2 && r.lanes[0].status === 'done', { n, r: r.headline }) - - const dead = mkAgent({ build: () => null }) - const r2 = await go(ARGS(), dead) - check('two deaths abandon the lane without throwing', r2.lanes[0].converged === false, r2.headline) -} - -// --- 8. Rejected rounds must feed the next build, or findings are collected and discarded. -- -{ - const seenPrompts = [] - const agent = mkAgent({ - build: (seen) => { seenPrompts.push(seen[seen.length - 1].prompt); return BUILD() }, - review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker', claim: 'THE-DISTINCTIVE-BLOCKER' })] }), - }) - await go(ARGS({ maxRounds: 2 }), agent) - check('round 2 receives round 1 blockers as feedback', - seenPrompts.length === 2 && /THE-DISTINCTIVE-BLOCKER/.test(seenPrompts[1]), seenPrompts.length) -} - -// --- 9. Reviewers diff the TIP, never HEAD~1 (which once diffed the base commit). ----------- -{ - const agent = mkAgent() - await go(ARGS(), agent) - const reviews = agent.seen.filter((s) => s.label.startsWith('review:')) - check('reviewers are told to diff the tip, and HEAD~1 appears only as a warning', - reviews.every((r) => r.prompt.includes('abc1234')), reviews.length) -} - -// --- 10. Landing is part of the pipeline. Its absence is what produced a +99 backlog. ------- -{ - const agent = mkAgent() - // Distinctive key and worktree: 'a' and '/w' occur in English prose, so they cannot tell a - // populated prompt from a broken one. - const r = await go(ARGS({ land: true, lanes: [LANE({ key: 'LANE-KEY-Z', wt: '/wt/lane-z' })] }), agent) - check('a converged lane LANDS by default', agent.seen.some((s) => s.label === 'land') && !!r.landed, r.headline) - - // THE PROMPT MUST CONTAIN THE LANE, NOT undefined. Asserting only that landing was INVOKED is - // what let this ship: the prompt read o.lane.key / o.lane.wt / o.fix off the flattened summary, - // whose `lane` is a STRING, so the integration owner was told "undefined — worktree undefined" - // with no files and no follow-ups. A phase that runs on garbage is not a phase that runs. - const landPrompt = (agent.seen.find((s) => s.label === 'land') || {}).prompt || '' - check('the landing prompt names the real lane key and its worktree', - landPrompt.includes('LANE-KEY-Z') && landPrompt.includes('/wt/lane-z'), landPrompt.slice(0, 400)) - check('the landing prompt carries the lane\'s changed files', landPrompt.includes('x.rs'), landPrompt.slice(0, 400)) - check('the landing prompt interpolates no undefined field', !/undefined/.test(landPrompt), - (landPrompt.split('\n').filter((x) => /undefined/.test(x)) || []).slice(0, 4)) - - // LANDING MUST BE BOUND TO THE REVIEWED HEAD. Clean-worktree + recent-log is not a binding: a - // commit added after the reviewers finished is clean and would be cherry-picked as reviewed. - check('the landing prompt carries the head SHA captured at review time', - landPrompt.includes('cafe1234'), landPrompt.slice(0, 400)) - check('and orders a refusal when the worktree no longer matches it', - /rev-parse HEAD/.test(landPrompt) && /do NOT land|REFUSE/.test(landPrompt), landPrompt.slice(0, 400)) - - const noConverge = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker' })] }) }) - await go(ARGS({ land: true }), noConverge) - check('nothing converged => nothing lands', !noConverge.seen.some((s) => s.label === 'land'), - noConverge.seen.map((s) => s.label)) - - const off = mkAgent() - await go(ARGS({ land: false }), off) - check('land:false is honoured (and warns)', !off.seen.some((s) => s.label === 'land'), off.seen.map((s) => s.label)) -} - -// --- 10b. A DEAD REVIEWER IS NOT AN ABSENT FINDING. ----------------------------------------- -// Measured: session-limit kills took 4 of 7 agents from one run and 3 of 3 from another, and -// `.filter(Boolean)` made them vanish — a lane could converge on one surviving reviewer in silence. -{ - const standingDied = mkAgent({ review: (prompt) => (/ORACLE INTEGRITY/.test(prompt) ? null : REVIEW()) }) - const logs = [] - const r = await go(ARGS(), standingDied, logs) - check('a dead STANDING lens blocks convergence', r.lanes[0].converged === false, r.headline) - check('and says which one died', logs.some((m) => /CANNOT CONVERGE.*ORACLE INTEGRITY/.test(m)), - logs.filter((m) => /CONVERGE|DIED/.test(m))) - - const customDied = mkAgent({ review: (prompt) => (/CUSTOM A/.test(prompt) ? null : REVIEW()) }) - const logs2 = [] - const r2 = await go(ARGS({ lenses: ['CUSTOM A', 'CUSTOM B'] }), customDied, logs2) - check('a dead CUSTOM lens is tolerated (a rebuild round costs more than it saves)', - r2.lanes[0].converged === true, r2.headline) - check('but the death is still logged', logs2.some((m) => /DIED/.test(m)), - logs2.filter((m) => /DIED|reviewers returned/.test(m))) -} - -// --- 10d. A DEAD VERIFIER IS NOT A PASSED VERIFICATION. ------------------------------------- -// The sibling of 10b, and worse: `!verify` read a died verifier as "no verifier was needed", so the -// lane converged and the log asserted "independently re-verified" having verified NOTHING. A -// standing lens is one voice among several; the verifier is the ONLY thing that reproduces a -// claimed green, so a dead one has no substitute and cannot be tolerated the way a custom lens is. -{ - const verifierDied = mkAgent({ verify: () => null }) - const logs = [] - const r = await go(ARGS(), verifierDied, logs) - check('a dead VERIFIER blocks convergence', r.lanes[0].converged === false, r.headline) - check('and never claims the lane was independently re-verified', - !logs.some((m) => /independently re-verified/.test(m)), logs.filter((m) => /re-verified|VERIFIER/.test(m))) - check('and says the verification never happened', - logs.some((m) => /VERIFIER DIED/.test(m)), logs.filter((m) => /VERIFIER/.test(m))) - - // The deliberate exception must survive: a lane claiming nothing has nothing to falsify, so the - // verifier is SKIPPED, and a skip is not a death. - const partial = mkAgent({ build: () => BUILD({ status: 'partial' }) }) - const logs2 = [] - await go(ARGS(), partial, logs2) - check('a build claiming nothing is skipped, not accused of a dead verifier', - logs2.some((m) => /verifier skipped/.test(m)) && !logs2.some((m) => /VERIFIER DIED/.test(m)), logs2) -} - -// --- 10c. Telemetry must be able to be WRONG, i.e. must observe reality. -------------------- -// The old expression was ((LENSES.length + 1) * buildRounds) / buildRounds — algebraically a -// constant. It could not detect a dead agent no matter how many died. -{ - const oneDied = mkAgent({ review: (prompt) => (/BLAST RADIUS/.test(prompt) ? null : REVIEW()) }) - const r = await go(ARGS(), oneDied) - const t = r.telemetry - check('telemetry reports dispatched > returned when a checker dies', - t.checkersDispatched > t.checkersReturned, { d: t.checkersDispatched, r: t.checkersReturned }) - check('telemetry names the dead checker', (t.deadCheckers || []).length === 1, t.deadCheckers) - - const allLived = mkAgent() - const r2 = await go(ARGS(), allLived) - check('telemetry reports equality when none die', - r2.telemetry.checkersDispatched === r2.telemetry.checkersReturned, - { d: r2.telemetry.checkersDispatched, r: r2.telemetry.checkersReturned }) -} - -// --- 10d. Review-round findings: three defects the reviewer caught that the preflight did not. --- -{ - // Two lanes in ONE worktree passed validation and were dispatched concurrently, each told by the - // lock to build on whatever it found — so the second treats the first's half-finished edits as - // its baseline. Silent by design. - const m = await threw(ARGS({ lanes: [LANE({ key: 'a', wt: '/same' }), LANE({ key: 'b', wt: '/same' })] })) - check('two lanes sharing a worktree abort', !!m && /both declare worktree/.test(m), m) - - const m2 = await threw(ARGS({ lanes: [LANE({ key: 'dup' }), LANE({ key: 'dup', wt: '/other' })] })) - check('duplicate lane keys abort', !!m2 && /duplicate lane key/.test(m2), m2) - - // Documented and accepted must be the same set: the args comment advertised blockedTargets while - // the allowlist rejected it, so a caller following the docs aborted. - const ok = await go(ARGS({ lanes: [LANE({ blockedTargets: ['x'] })] })) - check('a documented per-lane key is accepted', !!ok && Array.isArray(ok.headline), ok && Object.keys(ok || {})) - - // scopeCreep is a first-class verdict, not a hint. A reviewer setting it without ALSO restating it - // as a blocking finding was ignored, so a lane could edit an unowned root and still converge. - const creeper = mkAgent({ review: () => REVIEW({ verdict: 'accept', scopeCreep: true }) }) - const r = await go(ARGS(), creeper) - check('scopeCreep alone blocks convergence', r.lanes[0].converged === false, r.headline) - - const clean = await go(ARGS(), mkAgent()) - check('and does not block when unset', clean.lanes[0].converged === true, clean.headline) -} - -// --- 11. The required-field trio must stay required, or the clause is skimmable again. ------ -{ - const req = (SRC.match(/required: \[[^\]]*'enforcementPlacement'[^\]]*\]/) || [''])[0] - check('enforcementPlacement is a REQUIRED build field', /enforcementPlacement/.test(req), req.slice(0, 200)) - check('peripheralsUpdated is a REQUIRED build field', /peripheralsUpdated/.test(req), req.slice(0, 200)) - check('redBaseline is a REQUIRED build field', /redBaseline/.test(req), req.slice(0, 200)) - const rf = (SRC.match(/required: \[[^\]]*'provenByExecution'[^\]]*\]/) || [''])[0] - check('provenByExecution and ownerLease are REQUIRED per finding', - /provenByExecution/.test(rf) && /ownerLease/.test(rf), rf.slice(0, 200)) -} - -// --- 12. The lock must not be silently truncated by a nested backtick. ---------------------- -{ - const lock = SRC.slice(SRC.indexOf('const BASE_LOCK = `') + 19, SRC.indexOf('\n`\n\nconst LOCK')) - check('BASE_LOCK contains no nested backtick', (lock.match(/`/g) || []).length === 0, (lock.match(/`/g) || []).length) - for (const clause of ['NEVER WEAKEN THE ORACLE', 'CONTRACT TESTS ARE PART OF THE CHANGE', - 'AN ENFORCEMENT MUST BE ABLE TO SEE ITS SUBJECT', 'PERIPHERALS ARE PART OF THE CHANGE', - 'THE THIRD SPELLING MEANS THE MECHANISM IS WRONG', 'NEVER pass --workflow-only']) { - check(`lock clause survives: ${clause}`, lock.includes(clause)) - } -} - -// --- 13. The caller that dispatches this harness must not bake in one machine's paths. ------ -// program-tick.js chains into lane-fanout and used to send every selected lane to a hard-coded -// /Users//... worktree, ignoring both the workspace it was given and the worktree inventory -// it had just collected. On CI, on Linux, on any other machine, every implementer was pointed at a -// path that does not exist — and nothing here could see it, because the preflight only ever -// compiled lane-fanout.js. A dispatcher is part of the harness. -{ - const TICK = fs.readFileSync(path.join(SRCDIR, 'program-tick.js'), 'utf8') - try { - new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', - TICK.replace(/^export const meta = /m, 'const meta = ')) - check('program-tick compiles as the harness evaluates it', true) - } catch (e) { - check('program-tick compiles as the harness evaluates it', false, e.message) - } - const homePaths = TICK.split('\n').filter((l) => /(\/Users\/|\/home\/)[A-Za-z0-9_.-]+\//.test(l)) - check('program-tick hard-codes no machine-specific worktree path', homePaths.length === 0, homePaths) -} - -// The unknown-option guard was written for lane-fanout, repeated in backlog-audit, and skipped in -// program-tick. A rule present in two of three sibling harnesses is not a rule, it is a coincidence, -// so the preflight now asserts it across ALL of them rather than for each file someone remembers. -{ - // ENUMERATE THE DIRECTORY, DO NOT LIST IT. This was a hardcoded array of the harnesses someone - // remembered, which is how program-tick went without the guard while its two siblings had it, and - // how review-gate.js and slice.js sat in this directory referenced-but-never-compiled. A new - // harness must be covered by existing here, not by being added to a list a future author edits. - const dispatchers = fs.readdirSync(HERE) - .filter((f) => f.endsWith('.js')) - .map((f) => f.replace(/\.js$/, '')) - .sort() - check('every harness in the directory is swept, not a hardcoded subset', - dispatchers.length >= 4 && dispatchers.includes('scout'), dispatchers) - for (const name of dispatchers) { - const src = fs.readFileSync(path.join(SRCDIR, `${name}.js`), 'utf8') - .replace(/^export const meta = /m, 'const meta = ') - const fn = new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', src) - const stub = async () => ({}) - // Every required field is supplied; ONLY the bogus key should be able to fail this. - const base = { tip: 'a'.repeat(40), lanes: [LANE()], candidateWt: '/w', candidateTip: 'b'.repeat(40), - base: 'main', repo: '/r', ghRepo: 'o/n', maxLanes: 2 } - let threw = null - try { - await fn({ ...base, thisOptionDoesNotExist: true }, stub, async (t) => Promise.all(t.map((f) => f())), - async (i) => i, () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, stub) - } catch (e) { threw = e.message } - check(`${name} refuses an option it does not read`, - !!threw && /unknown option/i.test(threw), threw) - } -} - -// A batch size of -1 produces no batches and 1.5 produces overlapping slices; both exit cleanly. -{ - const src = fs.readFileSync(path.join(SRCDIR, 'backlog-audit.js'), 'utf8') - .replace(/^export const meta = /m, 'const meta = ') - const fn = new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', src) - for (const bad of [-1, 0, 1.5, 'eight']) { - let threw = null - try { - await fn({ repo: '/r', ghRepo: 'o/n', issueBatch: bad }, async () => ({}), - async (t) => Promise.all(t.map((f) => f())), async (i) => i, () => {}, () => {}, - { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) - } catch (e) { threw = e.message } - check(`backlog-audit refuses issueBatch=${JSON.stringify(bad)}`, - !!threw && /issueBatch must be a positive integer/.test(threw), threw) - } -} - -// The reconciler was handed `JSON.stringify(findings).slice(0, 24000)`. With ~32 read lanes the cap -// binds routinely, so the single writer filed beads for a prefix of the audit and reported success. -{ - const src = fs.readFileSync(path.join(SRCDIR, 'backlog-audit.js'), 'utf8') - check('backlog-audit no longer truncates the findings payload blindly', - !/JSON\.stringify\(findingsAll\)\.slice\(/.test(src)) - const body = src.match(/function renderFindings\(all\) \{[\s\S]*?\n\}/) - check('backlog-audit exposes renderFindings to the preflight', !!body) - if (body) { - const renderFindings = new Function(`${body[0]}; return renderFindings`)() - const many = Array.from({ length: 400 }, (_, i) => ({ - title: `finding ${i} ${'x'.repeat(200)}`, severity: i ? 'minor' : 'blocker', - provenByExecution: i === 399, evidence: 'f.rs:1', - })) - const out = renderFindings(many) - check('an over-budget findings set says how many it dropped', /DID NOT FIT/.test(out), out.slice(-200)) - check('the proven finding survives truncation regardless of its position', - out.includes('finding 399'), 'the last-listed proven finding was cut') - const few = [{ title: 'only one', severity: 'blocker', provenByExecution: true }] - check('a set that fits carries no truncation notice', !/DID NOT FIT/.test(renderFindings(few))) - } -} - -// The reporter must survive its own failure path. Proven by capturing stdout rather than by reading -// it: a detail-less FAIL used to throw TypeError and abort the run, which is worse than a red because -// it looks like a crash in the harness instead of a defect in the code under test. -{ - const realLog = console.log - const lines = [] - console.log = (l) => lines.push(l) - let crashed = null - const before = failures - try { check('self-test: a failing assertion carries no detail', false) } catch (e) { crashed = e.message } - console.log = realLog - failures = before // this deliberate FAIL must not colour the real result - check('a detail-less failure reports instead of crashing the preflight', - crashed === null && lines.length === 1 && lines[0].startsWith('FAIL'), crashed || lines) -} - -// A doc edit that fails CI on a stale generated checksum is the cheapest possible review round: the -// fix is one command, and the lane that made the edit could have run it. This assertion exists -// because a correct change was turned red by exactly that, twice. -{ - const need = ['REGENERATE, THEN ASK GIT', 'git status --porcelain', 'POSTFLIGHT', 'tools/buck/preflight.sh', 'tools/lanes/pgtest.sh'] - for (const fragment of need) { - check(`the lock names a TOTAL generated-face check, not a list: ${fragment}`, SRC.includes(fragment)) - } -} - -// --- 14. OVERLAPPING OWNED ROOTS: the duplicate-worktree collision, deferred to LAND. -------- -// Separate worktrees mean two lanes cannot corrupt each other's files, so every reviewer and every -// verifier passes. The collision arrives on the integration branch afterwards, as two independent -// rewrites of the same files from the same base — the most expensive possible moment. Refuse it -// where the duplicate `wt` is refused: at dispatch. -{ - const nested = await threw(ARGS({ lanes: [ - LANE({ key: 'outer', wt: '/w1', owned: 'backend/crates/x/**' }), - LANE({ key: 'inner', wt: '/w2', owned: 'backend/crates/x/sub/**' }), - ] })) - check('a lane owning a subtree of another lane aborts', - !!nested && /overlapping owned root/i.test(nested) && /outer/.test(nested) && /inner/.test(nested), nested) - - const same = await threw(ARGS({ lanes: [ - LANE({ key: 'a1', wt: '/w1', owned: 'docs/x/**' }), - LANE({ key: 'a2', wt: '/w2', owned: 'docs/x/**' }), - ] })) - check('two lanes owning the SAME root abort', !!same && /overlapping owned root/i.test(same), same) - - // An owned root is routinely a LIST. One colliding member is enough, and checking only the first - // would be the same defect with a smaller blast radius. - const multi = await threw(ARGS({ lanes: [ - LANE({ key: 'm1', wt: '/w1', owned: 'a/**, b/**' }), - LANE({ key: 'm2', wt: '/w2', owned: 'c/**\nb/deep/**' }), - ] })) - check('one colliding path inside a multi-path owned root is enough', !!multi && /overlapping owned root/i.test(multi), multi) - - // A guard that examines nothing must FAIL, not pass: an owned root naming no path cannot be - // compared, and it is also unusable as the reviewer's IN-SCOPE PATHS list. - const prose = await threw(ARGS({ lanes: [LANE({ key: 'p1', owned: 'everything in the crate' })] })) - check('an owned root with no path in it aborts rather than being silently unguarded', - !!prose && /no path/i.test(prose), prose) - - // `./backend/crates/foo` and `backend/crates/foo` are the same root; dispatch must refuse. - const dotted = await threw(ARGS({ lanes: [ - LANE({ key: 'bare', wt: '/w1', owned: 'backend/crates/foo/**' }), - LANE({ key: 'dot', wt: '/w2', owned: './backend/crates/foo/**' }), - ] })) - check('./ and bare owned roots collide at dispatch', - !!dotted && /overlapping owned root/i.test(dotted), dotted) - - const parentDots = await threw(ARGS({ lanes: [ - LANE({ key: 'up', wt: '/w1', owned: 'backend/crates/foo/../bar/**' }), - LANE({ key: 'other', wt: '/w2', owned: 'docs/x/**' }), - ] })) - check('owned roots containing .. are refused', - !!parentDots && /\.\./.test(parentDots), parentDots) - - // ...and it must not OVER-refuse, or it becomes a thing people work around. - const sibling = await go(ARGS({ lanes: [ - LANE({ key: 'd1', wt: '/w1', owned: 'backend/crates/ab/**' }), - LANE({ key: 'd2', wt: '/w2', owned: 'backend/crates/a/**' }), - ] })) - check('a shared string prefix that is not a PATH prefix is not an overlap', - !!sibling && Array.isArray(sibling.headline), sibling && sibling.headline) - - const disjoint = await go(ARGS({ lanes: [ - LANE({ key: 'e1', wt: '/w1', owned: 'backend/crates/a/**' }), - LANE({ key: 'e2', wt: '/w2', owned: 'docs/**' }), - ] })) - check('disjoint owned roots dispatch normally', !!disjoint && Array.isArray(disjoint.headline), disjoint && disjoint.headline) -} - -// --- 15. A GREEN THE VERIFIER DID NOT ANSWER FOR IS NOT A GREEN. ----------------------------- -// reproduced+contradictsClaim say the verifier ran something and agreed. They do not say WHAT it -// ran, whether any command selected zero tests and exited 0, or whether the suite still proves as -// much as it did. Oracle integrity is the most common rejection cause in this programme and a -// standing review lens, yet the schema let a verifier certify a green without ever answering it. -{ - const req = (SRC.match(/const VERIFY_SCHEMA = \{[\s\S]*?required: \[([^\]]*)\]/) || ['', ''])[1] - for (const f of ['falseGreenRisk', 'commandsRun', 'oracleIntact']) { - check(`${f} is a REQUIRED verifier field`, req.includes(f), req.slice(0, 300)) - } - - // The schema is a request, not an enforcement — the harness must refuse the green itself. - const noCommands = mkAgent({ verify: () => VERIFY({ commandsRun: [] }) }) - check('a verifier that lists no command it RAN cannot certify a green', - (await go(ARGS(), noCommands)).lanes[0].converged === false) - - const noRisk = mkAgent({ verify: () => VERIFY({ falseGreenRisk: '' }) }) - check('a verifier that leaves the false-green risk blank cannot certify a green', - (await go(ARGS(), noRisk)).lanes[0].converged === false) - - const silent = mkAgent({ verify: () => VERIFY({ oracleIntact: undefined }) }) - check('a verifier that never answers oracle integrity cannot certify a green', - (await go(ARGS(), silent)).lanes[0].converged === false) - - const weakened = mkAgent({ verify: () => VERIFY({ oracleIntact: false }) }) - check('a verifier that OBSERVED a weakened oracle blocks convergence', - (await go(ARGS(), weakened)).lanes[0].converged === false) - - // A rejection with no actionable text manufactures a wasted round, so the next build must be told. - const seenPrompts = [] - const partialAnswer = mkAgent({ - build: (seen) => { seenPrompts.push(seen[seen.length - 1].prompt); return BUILD() }, - verify: () => VERIFY({ commandsRun: [] }), - }) - await go(ARGS({ maxRounds: 2 }), partialAnswer) - check('and the next round is told the verification was unanswered, not merely "disagreed"', - seenPrompts.length === 2 - && (/commandsRun/.test(seenPrompts[1]) - || /NEVER independently run/.test(seenPrompts[1]) - || /named no well-formed commands/.test(seenPrompts[1])), - { n: seenPrompts.length, round2: (seenPrompts[1] || '').slice(0, 400) }) - - // A field nobody is asked for is a field nobody fills in. - const asked = mkAgent() - await go(ARGS(), asked) - const vp = (asked.seen.find((s) => s.label.startsWith('verify:')) || {}).prompt || '' - check('the verifier is ASKED for the commands it ran and for an oracle verdict', - /commandsRun/.test(vp) && /oracleIntact/.test(vp), vp.slice(0, 300)) - - const full = await go(ARGS(), mkAgent()) - check('a fully answered verification still converges', full.lanes[0].converged === true, full.headline) -} - -// --- 16. program-tick is the CALLER, so a collision must be refused where the set is BUILT. --- -// Refusing downstream in lane-fanout is necessary and late: by then the agents are already chosen. -// These assertions drive the REAL program-tick body, with a genuinely concurrent parallel(), and -// observe what it dispatches. -{ - const compileWorkflow = (name) => new AsyncFunction( - 'args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', - fs.readFileSync(path.join(SRCDIR, `${name}.js`), 'utf8').replace(/^export const meta = /m, 'const meta = ')) - const tick = compileWorkflow('program-tick') - - const WT = (over = {}) => ({ path: '/ws/x', head: 'abc', dirtyCount: 0, prunable: false, filesVsBase: [], commitsAheadOfCandidate: 0, ...over }) - const RAW = (over = {}) => ({ candidateFiles: ['f.rs'], worktrees: [], prs: [], beads: [], ...over }) - const JUDGED = (over = {}) => ({ startNow: [], holdBack: [], alreadyDone: [], coverageRisks: [], ...over }) - const PR = (n, over = {}) => ({ number: n, title: `pr${n}`, checkConclusion: 'FAILURE', reviewDecision: '', mergeable: 'MERGEABLE', mergeStateStatus: 'CLEAN', headSha: `sha${n}`, failingChecks: ['t'], isDraft: false, ...over }) - const SEL = (n) => Array.from({ length: n }, (_, i) => ({ key: `l${i + 1}`, bead: `b${i + 1}`, owned: `p${i + 1}/**`, brief: 'concrete', accept: 'a', briefConfidence: 'grounded' })) - - const runTick = async (over = {}, raw = RAW(), judged = JUDGED()) => { - const inflight = new Set() - const overlaps = [] - const dispatched = [] - const logs = [] - const workflows = [] - const agentFn = async (prompt, o = {}) => { - const label = o.label || '' - dispatched.push(label) - if (label === 'collect') return raw - if (label === 'judge') return judged - // A PR disposition agent. `fix-then-merge` is WORK: it edits files in the candidate worktree. - inflight.add(label) - if (inflight.size > 1) overlaps.push([...inflight]) - await new Promise((r) => setTimeout(r, 5)) - inflight.delete(label) - return { pr: 1, done: true, outcome: 'x' } - } - // REAL concurrency. A sequential stub would make serialisation indistinguishable from its - // absence, which is how a test measures the fixture instead of the code. - const par = async (thunks) => Promise.all(thunks.map((t) => t())) - const plan = await tick( - { candidateWt: '/ws/cand', candidateTip: 'c'.repeat(40), base: 'main', ...over }, - agentFn, par, async (i) => i, (m) => logs.push(m), () => {}, - { total: null, spent: () => 0, remaining: () => Infinity }, - async (name, a) => { workflows.push({ name, args: a }); return { headline: [] } }, - ) - return { plan, overlaps, dispatched, logs, workflows } - } - - const twoFixes = await runTick({}, RAW({ prs: [PR(1), PR(2)] })) - // Both halves matter: a guard that dispatched ZERO PR lanes would also report zero overlaps. - check('two PR-fix lanes are both dispatched', - twoFixes.dispatched.filter((l) => l.startsWith('pr:')).length === 2, twoFixes.dispatched) - check('...and never run concurrently in the one candidate worktree they both edit', - twoFixes.overlaps.length === 0, twoFixes.overlaps) - - const overCap = await runTick({ fanout: true, maxLanes: 4 }, - RAW({ worktrees: [1, 2, 3, 4, 5].map((i) => WT({ path: `/ws/l${i}` })) }), JUDGED({ startNow: SEL(5) })) - check('more selected lanes than maxLanes refuses the fanout instead of ignoring the cap', - overCap.workflows.length === 0 && !!overCap.plan.fanoutBlocked, - { workflows: overCap.workflows.length, blocked: overCap.plan.fanoutBlocked }) - check('and says the cap is why', /maxLanes/.test(JSON.stringify(overCap.plan.fanoutBlocked || '')), overCap.plan.fanoutBlocked) - - const withinCap = await runTick({ fanout: true, maxLanes: 4 }, - RAW({ worktrees: [1, 2, 3, 4].map((i) => WT({ path: `/ws/l${i}` })) }), JUDGED({ startNow: SEL(4) })) - check('a lane set within the cap still fans out', - withinCap.workflows.length === 1 && (withinCap.workflows[0].args.lanes || []).length === 4, - withinCap.workflows.map((w) => w.name)) - - const wts = await runTick({}, RAW({ worktrees: [ - WT({ path: '/ws/cand' }), - WT({ path: '/ws/idle' }), - WT({ path: '/ws/unreadable', commitsAheadOfCandidate: -1 }), - WT({ path: '/ws/capped', filesVsBase: [], filesVsBaseCount: 900 }), - ] })) - const safe = wts.plan.worktrees.safeToRemove - check('the ACTIVE candidate worktree is never offered as safe to remove', !safe.includes('/ws/cand'), safe) - check('an unreadable worktree is not "unused"', !safe.includes('/ws/unreadable'), safe) - check('a worktree whose file list was capped is not "empty"', !safe.includes('/ws/capped'), safe) - // ...and the list is not simply emptied, which would pass all three above and help nobody. - check('a genuinely empty worktree is still removable', safe.includes('/ws/idle'), safe) -} - -// --- 17. backlog-audit: evidence nobody landed is not evidence, and coverage must be real. ---- -{ - const audit = new AsyncFunction( - 'args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', - fs.readFileSync(path.join(SRCDIR, 'backlog-audit.js'), 'utf8').replace(/^export const meta = /m, 'const meta = ')) - - const CENSUS = (over = {}) => { - const base = { - openIssueNumbers: [1], openIssueCount: 1, issues: [], beads: [], - crates: [{ name: 'identity' }, { name: 'policy' }], - cargoTomlPaths: ['backend/crates/identity/Cargo.toml', 'backend/crates/policy/Cargo.toml'], - mergedPrs: [], excludedRoots: [], - } - const merged = { ...base, ...over } - // Keep cargoTomlPaths consistent with crates unless the caller overrides either explicitly. - if (!('cargoTomlPaths' in over) && 'crates' in over) { - merged.cargoTomlPaths = (merged.crates || []).map((c) => `backend/crates/${c.name}/Cargo.toml`) - } - return merged - } - const VERDICT = (over = {}) => ({ - number: 1, title: 't', verdict: 'CLOSE-FIXED', - evidence: 'implemented in backend/crates/identity/src/lib.rs:12 by commit deadbeefcafe1234 — verified by reading it', - ...over, - }) - - const runAudit = async (census, verdicts, over = {}) => { - const dispatched = [] - const logs = [] - const agentFn = async (prompt, o = {}) => { - const label = o.label || '' - dispatched.push({ label, prompt }) - if (label === 'collect') return census - // Independent disk oracle — must not reuse Collect's crates as its only source. - if (label === 'crate-disk-census') { - return { cargoTomlPaths: census.cargoTomlPaths || [] } - } - if (label.startsWith('triage:')) return { verdicts } - if (label === 'reconcile') return { ok: true } - return { domain: label, findings: [], coverage: 'read it all' } - } - let res = null - let err = null - try { - res = await audit({ repo: '/r', ghRepo: 'o/n', ...over }, agentFn, - async (t) => Promise.all(t.map((f) => f())), async (i) => i, (m) => logs.push(m), () => {}, - { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) - } catch (e) { err = e.message } - return { res, err, dispatched, logs } - } - - const unmerged = await runAudit(CENSUS(), [VERDICT({ reachableFromDefault: false })]) - check('a CLOSE-FIXED whose evidence is not on the default branch is WITHHELD', - !!unmerged.res && (unmerged.res.withheld || []).includes(1), unmerged.res && unmerged.res.withheld) - - const unanswered = await runAudit(CENSUS(), [VERDICT()]) - check('a CLOSE-FIXED that never answered reachability is WITHHELD too — absence is a NO', - !!unanswered.res && (unanswered.res.withheld || []).includes(1), unanswered.res && unanswered.res.withheld) - - const landedEv = await runAudit(CENSUS(), [VERDICT({ reachableFromDefault: true })]) - check('a CLOSE-FIXED reachable from the default branch is still closable', - !!landedEv.res && !(landedEv.res.withheld || []).includes(1), landedEv.res && landedEv.res.withheld) - - const keep = await runAudit(CENSUS(), [VERDICT({ verdict: 'KEEP', evidence: 'this is still broken, here is the file and line that shows it' })]) - check('a KEEP verdict is not withheld — the rule is about CLOSING', - !!keep.res && (keep.res.withheld || []).length === 0, keep.res && keep.res.withheld) - - check('triage is told to PROVE reachability by running merge-base --is-ancestor', - landedEv.dispatched.some((d) => d.label.startsWith('triage:') && /merge-base --is-ancestor/.test(d.prompt)), - landedEv.dispatched.map((d) => d.label)) - - const extraCrate = await runAudit(CENSUS({ crates: [{ name: 'identity' }, { name: 'brand-new-crate' }] }), - [VERDICT({ reachableFromDefault: true })]) - // Match the lane's OWN crate list, not the census echoed into every audit prompt — the echo made - // this assertion pass against the unfixed source, which is a test measuring its own fixture. - check('a discovered crate that no named domain claims is still audited', - extraCrate.dispatched.some((d) => /^audit:/.test(d.label) && /CRATES:[^\n]*brand-new-crate/.test(d.prompt)), - extraCrate.dispatched.map((d) => d.label)) - - const allCovered = await runAudit(CENSUS({ crates: [{ name: 'identity' }] }), [VERDICT({ reachableFromDefault: true })]) - check('and no lane is invented when every discovered crate is claimed', - !allCovered.dispatched.some((d) => d.label === 'audit:uncovered'), allCovered.dispatched.map((d) => d.label)) - - const blind = await runAudit(CENSUS({ crates: [] }), [VERDICT({ reachableFromDefault: true })]) - check('a census with no crate inventory cannot claim coverage and must abort', - !!blind.err && /crate inventory/i.test(blind.err), blind.err) - - // crate-disk-census is the independent oracle (workflow sandbox has no Node fs). Omitting a - // path that find would have returned must abort — same fail-closed class as a partial crates list. - // A co-emitted Collect.cargoTomlPaths is NOT enough: Collect can omit from both fields together. - const omittedDisk = await runAudit(CENSUS({ - crates: [{ name: 'identity' }], - cargoTomlPaths: ['backend/crates/identity/Cargo.toml', 'backend/crates/brand-new/Cargo.toml'], - }), [VERDICT({ reachableFromDefault: true })]) - check('a census that omits a crate-disk-census crate aborts', - !!omittedDisk.err && /omitted/i.test(omittedDisk.err), omittedDisk.err) - check('coverage uses a dedicated crate-disk-census agent, not Collect alone', - omittedDisk.dispatched.some((d) => d.label === 'crate-disk-census')) - - const emptyToml = await runAudit(CENSUS({ - crates: [{ name: 'identity' }], - cargoTomlPaths: [], - }), [VERDICT({ reachableFromDefault: true })]) - check('an empty crate-disk-census list cannot cross-check coverage and must abort', - !!emptyToml.err && /cargoTomlPaths|crate-disk-census/i.test(emptyToml.err), emptyToml.err) - - // Hostile: Collect's crates list is internally consistent and would have matched a co-emitted - // cargoTomlPaths — the old self-validation false green. The independent disk census still sees - // the omitted crate and must abort. - const coordinatedPartial = await runAudit(CENSUS({ - crates: [{ name: 'identity' }, { name: 'policy' }], - cargoTomlPaths: [ - 'backend/crates/identity/Cargo.toml', - 'backend/crates/policy/Cargo.toml', - 'backend/crates/brand-new/Cargo.toml', - ], - }), [VERDICT({ reachableFromDefault: true })]) - check('a Collect list that omits an on-disk crate aborts even when well-formed', - !!coordinatedPartial.err && /omitted/i.test(coordinatedPartial.err) - && /brand-new/.test(coordinatedPartial.err), coordinatedPartial.err) -} - -// Six green tests over a self-built registry coexisted with a production root that wired none of it. -// The lock must name the distinction, because "all the tests pass" is exactly how it presented. -{ - for (const fragment of ['BUILDS ITS OWN SUBJECT', 'MECHANISM:', 'WIRING:', 'composition root']) { - check(`the lock separates mechanism evidence from wiring evidence: ${fragment}`, SRC.includes(fragment)) - } -} - -// Sprawl and CI-caught-it-first are the two costs the harness never charged for. -{ - for (const fragment of ['MAINTAINABILITY / COST OF CARRY', 'COMMENT BLOBBING', 'RUN WHAT CI RUNS']) { - check(`the lock charges for cost of carry: ${fragment}`, SRC.includes(fragment)) - } - // A lens that is defined but unreachable is the defect this file exists to catch. - const standing = SRC.match(/const STANDING_LENSES = \[([\s\S]*?)\n\]/) - check('the maintainability lens is a STANDING lens, not an opt-in', - !!standing && standing[1].includes('MAINTAINABILITY')) -} - -// scout.js was added to the unknown-option sweep and NOTHING ELSE, so the harness that decides what -// every other lane works on was the least tested one in the directory. Both of its judgement calls -// shipped defective and both were caught by a real run rather than here: the packing produced two -// lanes owning the same territory, and agent-authored paths reached the emitted plan unvalidated. -// These assertions extract the two pure functions and drive them with the EXACT strings that run -// produced, so neither can regress silently. -{ - const SCOUT = fs.readFileSync(path.join(HERE, 'scout.js'), 'utf8') - - const normSrc = SCOUT.match(/function normaliseRoot\(raw\) \{[\s\S]*?\n\}/) - check('scout exposes normaliseRoot to the preflight', !!normSrc) - if (normSrc) { - const REPO = '/Users/x/wt' - const normaliseRoot = new Function('REPO', 'MIN_ROOT_SEGMENTS', `${normSrc[0]}; return normaliseRoot`)(REPO, 2) - const cases = [ - [`${REPO}/backend/crates/platform/audit-chain/src/`, 'backend/crates/platform/audit-chain/src/', 'absolute path made repo-relative'], - [' i) - const find = (i) => (parent[i] === i ? i : (parent[i] = find(parent[i]))) - for (let i = 0; i < items.length; i++) { - for (let j = i + 1; j < items.length; j++) { - if (overlaps(items[i].roots, items[j].roots)) parent[find(i)] = find(j) - } - } - check('scout packing: transitively-linked territory collapses to ONE group', - new Set(items.map((_, i) => find(i))).size === 1, - items.map((_, i) => find(i))) - } - - // A guard that only exists in scout.js text is a guard nobody proved runs. - check('scout still refuses to emit a plan whose lanes overlap', - /own overlapping roots/.test(SCOUT)) - check('scout defers a bead whose paths are all unusable rather than inventing a root', - /every reported path was unusable as an owned root/.test(SCOUT)) -} - -// TRIAL GATE. Bun proved its port method on three files before scaling to 64 agents; this harness -// always dispatched every lane cold, so a brief that is wrong the same way for every lane wastes -// the whole wave instead of one lane. -{ - const TWO = (over = {}) => ARGS({ - // Distinct worktrees AND distinct owned roots: the overlap guard is a sibling rule and this - // fixture must not trip it while testing something else. - lanes: [LANE({ key: 'a', wt: '/w1', owned: 'aa/**' }), LANE({ key: 'b', wt: '/w2', owned: 'bb/**' })], - ...over, - }) - - // A failing trial must hold the fleet back, and must NOT dispatch lane b at all. - { - const agent = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker' })] }) }) - const out = await go(TWO({ trial: 'a', maxRounds: 1 }), agent) - const bDispatched = agent.seen.some((x) => /:b\b/.test(x.label)) - check('a failed trial holds the fleet back', !!out && /DID NOT CONVERGE/.test(out.headline[0]), out && out.headline) - check('a failed trial dispatches NO other lane', !bDispatched, - agent.seen.map((x) => x.label)) - check('the held-back lanes are named, not silently dropped', - !!out && Array.isArray(out.heldBack) && out.heldBack.includes('b'), out && out.heldBack) - } - - // A converged trial must go on to dispatch the rest. - { - const agent = mkAgent() - const out = await go(TWO({ trial: 'a', maxRounds: 1 }), agent) - const bDispatched = agent.seen.some((x) => /:b\b/.test(x.label)) - check('a converged trial dispatches the remaining lanes', bDispatched, - agent.seen.map((x) => x.label)) - } - - // Naming a lane that does not exist is a typo, not a silent no-trial run. - { - const m = await threw(TWO({ trial: 'nope' })) - check('trial naming an unknown lane aborts', !!m && /nope/.test(m), m) - } - { - const m = await threw(ARGS({ trial: 'a' })) - check('trial with a single lane aborts rather than serialising for nothing', - !!m && /serialises for nothing/.test(m), m) - } -} - -// The tier rule must stay a RULE, not a preference someone reverses on a slow day. -{ - check('the lock states when a cheaper tier is allowed', - SRC.includes('A CHEAPER TIER IS ALLOWED ONLY WHERE AN INDEPENDENT STRONGER PASS AUDITS THE RESULT')) - const judgePhases = SRC.match(/label: `verify:[\s\S]{0,160}/g) || [] - check('the independent verifier does NOT run on a cheaper tier', - judgePhases.every((frag) => !/model:/.test(frag)), judgePhases.length) -} - -// KNOWN_ARGS IS A CLAIM ABOUT THE SOURCE, SO CHECK IT AGAINST THE SOURCE. -// The guard's whole purpose is "an option this harness does not read must abort". Two of these -// lists were hand-written and both were wrong in BOTH directions at once: slice.js omitted five -// options it genuinely reads (so the guard rejected every real invocation) while review-gate.js -// listed two it never reads (so the guard fell open on exactly what it exists to catch). A -// hand-maintained list of what the code reads is a second copy of the code. -{ - for (const name of fs.readdirSync(HERE).filter((f) => f.endsWith('.js')).map((f) => f.replace(/\.js$/, ''))) { - const src = fs.readFileSync(path.join(HERE, `${name}.js`), 'utf8') - const declared = src.match(/const KNOWN_ARGS = \[([^\]]*)\]/) - if (!declared) { check(`${name} declares KNOWN_ARGS`, false); continue } - const listed = new Set([...declared[1].matchAll(/'([^']+)'/g)].map((m) => m[1])) - - // Whichever accessor this harness uses for its parsed args. - const holder = /const KNOWN_ARGS[\s\S]{0,400}?\b(ARGS|A)\b\s*\)/.exec(src)?.[1] - || (src.includes('const ARGS') || src.includes('let ARGS') ? 'ARGS' : 'A') - const read = new Set( - [...src.matchAll(new RegExp(`\\b${holder}\\.([a-zA-Z_][a-zA-Z0-9_]*)`, 'g'))] - .map((m) => m[1]) - .filter((k) => !['length', 'lanes'].includes(k) || k === 'lanes'), - ) - // Object.keys(ARGS) inside the guard itself is not an option read. - read.delete('keys') - - const unread = [...listed].filter((k) => !read.has(k)) - const undeclared = [...read].filter((k) => !listed.has(k)) - check(`${name}: KNOWN_ARGS lists nothing the harness never reads`, unread.length === 0, unread) - check(`${name}: every option the harness reads is declared`, undeclared.length === 0, undeclared) - } -} - -// stale-take-audit.js had NO logic coverage here — only the generic KNOWN_ARGS sweep — and its -// Confirm phase failed open exactly the way the Audit phase does not. A dead agent yielded -// `refuted: null`, which is neither `=== false` nor truthy, so the suspicion fell out of BOTH result -// lists and the headline still printed "full coverage". This is the step that decides whether a -// reported reversion is REAL, and the reversion it exists to catch is a file whose un-wiring means -// the check that would have caught it does not run. -{ - const STA = fs.readFileSync(path.join(HERE, 'stale-take-audit.js'), 'utf8') - .replace(/^export const meta = /m, 'const meta = ') - const fn = new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', STA) - - const drive = async (confirmReturns) => { - const agent = async (prompt, o = {}) => { - if ((o.label || '').startsWith('audit:')) { - return { results: [{ file: '.github/workflows/ci.yml', verdict: 'STALE', evidence: 'main has a step HEAD lacks', missingFromHead: 'the step', wouldBreak: 'the suite un-wires' }] } - } - return confirmReturns() - } - return fn({ repo: '/r', main: 'origin/main', files: ['.github/workflows/ci.yml'] }, - agent, async (t) => Promise.all(t.map((f) => f().catch(() => null))), async (i) => i, - () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) - } - - const dead = await drive(() => null) - check('a dead confirmation does not erase the suspicion', - !!dead && Array.isArray(dead.unconfirmed) && dead.unconfirmed.length === 1, - dead && { stale: dead.stale, refuted: dead.refuted, unconfirmed: dead.unconfirmed }) - check('a dead confirmation stops the report claiming full coverage', - !!dead && !dead.headline.some((h) => /full coverage/.test(h)), dead && dead.headline) - - // A live agent that omits the field despite the schema must land in the same bucket: the schema is - // a request to the model, not an enforcement. - const fieldless = await drive(() => ({ file: '.github/workflows/ci.yml', reasoning: 'no verdict' })) - check('a confirmation without a verdict is unresolved, not clean', - !!fieldless && fieldless.unconfirmed.length === 1, fieldless && fieldless.unconfirmed) - - // Controls: the live paths must still work, or the fix is an over-block. - // Upholding STALE also requires this pass to attest the graft payload — publishing the - // first agent's missingFromHead unseen is how a wrong quote becomes the "confirmed" patch. - const kept = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: false, reasoning: 'real', missingFromHead: 'the step (attested)' })) - check('a live confirmation that fails to refute still reports STALE', - !!kept && kept.stale.length === 1 && kept.unconfirmed.length === 0 - && kept.stale[0].missingFromHead === 'the step (attested)', - kept && { s: kept.stale, u: kept.unconfirmed }) - const noPayload = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: false, reasoning: 'real but no graft' })) - check('an upheld STALE without an attested graft payload is unresolved', - !!noPayload && noPayload.stale.length === 0 && noPayload.unconfirmed.length === 1, - noPayload && { s: noPayload.stale, u: noPayload.unconfirmed }) - // console-zd7: a confirmed-stale verdict that never attested content must not publish the - // first agent's text under the graft-shaped key. Unconfirmed may retain the claim under a - // distinctly-named field so operators see the accusation without a ready-to-apply payload. - check('an upheld STALE without attestation does not publish first-pass text as missingFromHead', - !!noPayload && noPayload.stale.length === 0 - && !JSON.stringify(noPayload.stale).includes('the step') - && noPayload.unconfirmed.length === 1 - && !Object.prototype.hasOwnProperty.call(noPayload.unconfirmed[0], 'missingFromHead') - && noPayload.unconfirmed[0].claimedMissingFromHead === 'the step', - noPayload && { s: noPayload.stale, u: noPayload.unconfirmed }) - const blankPayload = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: false, reasoning: 'real', missingFromHead: ' ' })) - check('an upheld STALE with a blank graft payload is unresolved', - !!blankPayload && blankPayload.stale.length === 0 && blankPayload.unconfirmed.length === 1, - blankPayload && { s: blankPayload.stale, u: blankPayload.unconfirmed }) - check('a blank confirmer payload does not expose a graft-shaped missingFromHead either', - !!blankPayload && blankPayload.unconfirmed.length === 1 - && !Object.prototype.hasOwnProperty.call(blankPayload.unconfirmed[0], 'missingFromHead') - && blankPayload.unconfirmed[0].claimedMissingFromHead === 'the step', - blankPayload && blankPayload.unconfirmed) - // Confirmer attests a DIFFERENT payload: publish only that. Publishing the first-pass quote - // when the confirmation returned something else is the "mismatched" fail-open. - check('confirmed stale publishes the confirmer payload, never the first-pass quote', - !!kept && kept.stale[0].missingFromHead === 'the step (attested)' - && kept.stale[0].missingFromHead !== 'the step', - kept && kept.stale[0]) - // Confirm must re-derive the graft from diffs. Handing the first-pass quote in the prompt - // invites rubber-stamping an unread payload (claim, not evidence). - { - let confirmPrompt = null - const agent = async (prompt, o = {}) => { - if ((o.label || '').startsWith('audit:')) { - return { results: [{ file: '.github/workflows/ci.yml', verdict: 'STALE', evidence: 'main has a step HEAD lacks', missingFromHead: 'FIRST_PASS_SECRET_GRAFT', wouldBreak: 'the suite un-wires' }] } - } - confirmPrompt = prompt - return { file: '.github/workflows/ci.yml', refuted: true, reasoning: 'deliberate', missingFromHead: '' } - } - await fn({ repo: '/r', main: 'origin/main', files: ['.github/workflows/ci.yml'] }, - agent, async (t) => Promise.all(t.map((f) => f().catch(() => null))), async (i) => i, - () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) - check('confirm prompt does not offer the first-pass graft payload for rubber-stamping', - typeof confirmPrompt === 'string' - && !/FIRST_PASS_SECRET_GRAFT/.test(confirmPrompt) - && !/PAYLOAD OFFERED/.test(confirmPrompt) - && /EVIDENCE OFFERED/.test(confirmPrompt), - confirmPrompt && confirmPrompt.slice(0, 400)) - } - // Oracle integrity: the pre-zd7 publish path (`missingFromHead: r.missingFromHead` from the - // audit spread) would leak the first-pass quote under a confirmed-stale verdict. Mutating the - // control back to that mapping must go red against the attestation pin. - { - const leaked = { file: 'ci.yml', missingFromHead: 'FIRST_PASS_WRONG', confirmedMissing: null, refuted: false } - const oldPublish = [leaked].filter((c) => c.refuted === false) - .map((r) => ({ file: r.file, missingFromHead: r.missingFromHead })) - const newPublish = [leaked].filter((c) => - c.refuted === false - && typeof c.confirmedMissing === 'string' - && c.confirmedMissing.trim() !== '') - .map((r) => ({ file: r.file, missingFromHead: r.confirmedMissing })) - check('mutate→red: old confirmed-stale publish path leaks the first-pass graft', - oldPublish.length === 1 && oldPublish[0].missingFromHead === 'FIRST_PASS_WRONG' - && newPublish.length === 0, - { oldPublish, newPublish }) - } - const dropped = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: true, reasoning: 'deliberate', missingFromHead: '' })) - check('a live refutation still drops the suspicion', - !!dropped && dropped.stale.length === 0 && dropped.refuted.length === 1 && dropped.unconfirmed.length === 0, - dropped && { s: dropped.stale, r: dropped.refuted, u: dropped.unconfirmed }) - check('a fully-answered run still claims full coverage', - !!dropped && dropped.headline.some((h) => /full coverage/.test(h)), dropped && dropped.headline) - - // Partial result lists must not report full coverage: five verdicts for six files is incomplete. - const partialAudit = async () => { - const agent = async (prompt, o = {}) => { - if ((o.label || '').startsWith('audit:')) { - return { - results: [ - { file: 'a.yml', verdict: 'CLEAN', evidence: 'ok' }, - { file: 'b.yml', verdict: 'CLEAN', evidence: 'ok' }, - { file: 'c.yml', verdict: 'CLEAN', evidence: 'ok' }, - { file: 'd.yml', verdict: 'CLEAN', evidence: 'ok' }, - { file: 'e.yml', verdict: 'CLEAN', evidence: 'ok' }, - // f.yml omitted - ], - } - } - return null - } - return fn({ repo: '/r', main: 'origin/main', files: ['a.yml', 'b.yml', 'c.yml', 'd.yml', 'e.yml', 'f.yml'] }, - agent, async (t) => Promise.all(t.map((f) => f().catch(() => null))), async (i) => i, - () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) - } - const partial = await partialAudit() - check('a live audit that omits a requested file does not claim full coverage', - !!partial && !partial.headline.some((h) => /full coverage/.test(h)) - && Array.isArray(partial.missingAuditFiles) && partial.missingAuditFiles.includes('f.yml'), - partial && { headline: partial.headline, missing: partial.missingAuditFiles }) - - check('stale-take RULES pin diffs to args.repo via git -C', - /git -C \$\{REPO\} diff HEAD/.test(STA) && /git -C \$\{REPO\} diff \$\{MAIN\} HEAD/.test(STA)) -} - -// A verifier that re-ran ONE of five claimed commands satisfied `commandsRun.length > 0`, and with -// reproduced=true the lane converged while four suites were never independently run. Some of the -// evidence re-run is a sample, not a verification. -{ - const build = (cmds) => () => BUILD({ commands: cmds }) - const CMDS = ['cargo test -p a', 'cargo test -p b', 'npm run check:x'] - - const partial = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: [CMDS[0]], falseGreenRisk: 'none', oracleIntact: true }) }) - const r1 = await go(ARGS({ maxRounds: 1 }), partial) - check('a verifier that re-ran only some claimed commands does not converge', - !!r1 && r1.lanes[0].converged === false, r1 && r1.lanes[0].converged) - - const full = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: [...CMDS], falseGreenRisk: 'none', oracleIntact: true }) }) - const r2 = await go(ARGS({ maxRounds: 1 }), full) - check('a verifier that re-ran every claimed command still converges', - !!r2 && r2.lanes[0].converged === true, r2 && r2.lanes[0].converged) - - // Whitespace must not decide it, and repetition must not substitute for coverage. - const spaced = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: CMDS.map((c) => ` ${c.replace(/ /g, ' ')} `), falseGreenRisk: 'none', oracleIntact: true }) }) - const r3 = await go(ARGS({ maxRounds: 1 }), spaced) - check('command matching is not defeated by whitespace', !!r3 && r3.lanes[0].converged === true, r3 && r3.lanes[0].converged) - - const repeated = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: [CMDS[0], CMDS[0], CMDS[0]], falseGreenRisk: 'none', oracleIntact: true }) }) - const r4 = await go(ARGS({ maxRounds: 1 }), repeated) - check('re-running one command three times is not three commands', - !!r4 && r4.lanes[0].converged === false, r4 && r4.lanes[0].converged) - - // commandsRun: [""] must not converge — every entry has to be a non-empty string. - const blankOnly = mkAgent({ - build: build(['cargo test -p a']), - verify: () => VERIFY({ commandsRun: [''], falseGreenRisk: 'none', oracleIntact: true }), - }) - const r5 = await go(ARGS({ maxRounds: 1 }), blankOnly) - check('commandsRun of a single empty string does not converge', - !!r5 && r5.lanes[0].converged === false, r5 && r5.lanes[0].converged) - - const blankAmong = mkAgent({ - build: build(['cargo test -p a']), - verify: () => VERIFY({ commandsRun: ['cargo test -p a', ''], falseGreenRisk: 'none', oracleIntact: true }), - }) - const r6 = await go(ARGS({ maxRounds: 1 }), blankAmong) - check('a blank entry among commandsRun fails closed even if a real command is present', - !!r6 && r6.lanes[0].converged === false, r6 && r6.lanes[0].converged) - - // Omit/invalid claimed commands must not converge: empty coverage against the verifier's own - // commands is a vacuous pass, not independent verification of a done build. - const omitted = mkAgent({ - build: () => BUILD({ commands: [] }), - verify: () => VERIFY({ commandsRun: ['cargo test -p x'], falseGreenRisk: 'none', oracleIntact: true }), - }) - const r7 = await go(ARGS({ maxRounds: 1 }), omitted) - check('a done build that omits commands does not converge', - !!r7 && r7.lanes[0].converged === false, r7 && r7.lanes[0].converged) - - const blanksOnly = mkAgent({ - build: () => BUILD({ commands: ['', ' '] }), - verify: () => VERIFY({ commandsRun: ['cargo test -p x'], falseGreenRisk: 'none', oracleIntact: true }), - }) - const r8 = await go(ARGS({ maxRounds: 1 }), blanksOnly) - check('a done build whose commands are only blanks does not converge', - !!r8 && r8.lanes[0].converged === false, r8 && r8.lanes[0].converged) - - const missingField = mkAgent({ - build: () => { - const b = BUILD() - delete b.commands - return b - }, - verify: () => VERIFY({ commandsRun: ['cargo test -p x'], falseGreenRisk: 'none', oracleIntact: true }), - }) - const r9 = await go(ARGS({ maxRounds: 1 }), missingField) - check('a done build that omits the commands field does not converge', - !!r9 && r9.lanes[0].converged === false, r9 && r9.lanes[0].converged) -} - -// scout deferred a bead whose paths were ALL unusable and merely LOGGED the partial case, emitting a -// lane authorised for the work but forbidden from part of it — a failure that arrives after dispatch. -{ - const SCOUT = fs.readFileSync(path.join(HERE, 'scout.js'), 'utf8') - check('scout defers a bead when only SOME of its paths are unusable', - /are unusable as owned roots, so any lane would be authorised/.test(SCOUT)) - // The partial branch must CONTINUE, not fall through to placeable.push. - const partial = SCOUT.match(/if \(rejected\) \{[\s\S]*?\n \}/) - check('the partial-rejection branch stops the bead being placed', - !!partial && /continue/.test(partial[0]), partial && partial[0].slice(0, 120)) - - // Unverified dependency edges must be dropped, not kept via the stored fallback. - check('scout drops edges with no verification verdict (fail closed)', - /if \(!v\) \{ unverifiedEdges\.push\(e\); continue \}/.test(SCOUT) - && /UNVERIFIED EDGES ARE NOT KEPT/.test(SCOUT)) - check('scout no longer advertises fanoutArgs as feed-straight-into lane-fanout', - /fanoutPlan:/.test(SCOUT) && /status: 'incomplete'/.test(SCOUT) && !/Feed straight into lane-fanout/.test(SCOUT)) - check('scout fanoutPlan is explicitly incomplete for lane-fanout', - /missing tip and per-lane wt\/brief\/accept/.test(SCOUT)) - check('scout measures depth by downstream dependents (reverse edges)', - /corrected\.filter\(\(e\) => e\.to === id\)/.test(SCOUT)) - check('scout counts rejected paths before deduplicating roots', - /const normalised = \(item\.paths/.test(SCOUT) && /rejected = normalised\.filter/.test(SCOUT)) - check('scout rejects absolute paths outside REPO with a segment boundary', - /r\.startsWith\(`\$\{repo\}\/`\)/.test(SCOUT) && /else return null/.test(SCOUT)) -} - -// backlog-audit must not treat a partial crate census as complete coverage. -{ - const AUDIT = fs.readFileSync(path.join(HERE, 'backlog-audit.js'), 'utf8') - check('backlog-audit does not import Node fs for the crate census', !/import\(['"]node:fs['"]\)/.test(AUDIT)) - check('backlog-audit measures crates via a dedicated crate-disk-census agent', - /label: 'crate-disk-census'/.test(AUDIT) && /find backend\/crates -name Cargo\.toml/.test(AUDIT)) - check('backlog-audit does not treat Collect.cargoTomlPaths as the disk oracle', - /required: \['openIssueNumbers', 'openIssueCount', 'issues', 'beads', 'crates'\]/.test(AUDIT) - && !/required: \['openIssueNumbers', 'openIssueCount', 'issues', 'beads', 'crates', 'cargoTomlPaths'\]/.test(AUDIT) - && /diskCensus\.cargoTomlPaths/.test(AUDIT)) - const omitSrc = AUDIT.match(/function cratesOmittedFromCensus\([\s\S]*?\n\}/) - check('backlog-audit exposes cratesOmittedFromCensus to the preflight', !!omitSrc) - if (omitSrc) { - const cratesOmittedFromCensus = new Function(`${omitSrc[0]}; return cratesOmittedFromCensus`)() - check('a census that omits an on-disk crate is incomplete', - cratesOmittedFromCensus(['identity', 'brand-new'], ['identity']).join(',') === 'brand-new') - check('a census parent prefix still covers nested crates', - cratesOmittedFromCensus(['identity/domain', 'identity/rest'], ['identity']).length === 0) - } - const deriveSrc = AUDIT.match(/function crateNamesFromCargoTomlPaths\([\s\S]*?\n\}/) - check('backlog-audit exposes crateNamesFromCargoTomlPaths to the preflight', !!deriveSrc) - if (deriveSrc) { - const crateNamesFromCargoTomlPaths = new Function(`${deriveSrc[0]}; return crateNamesFromCargoTomlPaths`)() - check('cargoTomlPaths strip to crate names under backend/crates', - crateNamesFromCargoTomlPaths(['backend/crates/identity/Cargo.toml', './backend/crates/policy/Cargo.toml']).join(',') === 'identity,policy') - } -} - -// Contract-drift: string literals must not invent HTTP methods; OpenAPI path keys may contain ':'. -{ - const driftPath = path.join(HERE, '..', '..', 'scripts', 'check-platform-contract-drift.mjs') - const driftSrc = fs.readFileSync(driftPath, 'utf8') - check('contract-drift masks string literals before method discovery', - /function maskStringLiterals/.test(driftSrc) && /maskStringLiterals\(methodExpression\)/.test(driftSrc)) - check('contract-drift OpenAPI path keys allow colons inside the path', - driftSrc.includes('trimmedRight.match(/^ {2}(\\/.+):$/)') - || driftSrc.includes('trimmedRight.match(/^ {2}(/.+):$/)') - || /\\\/\.+\):\$/.test(driftSrc)) - check('contract-drift refuses repo-wide same-name fallback for undeclared PATH consts', - /refusing repo-wide same-name fallback/.test(driftSrc)) - check('contract-drift discovers route sources after stripping comments/literals', - /stripRustCommentsAndLiterals\(readFileSync\(file, "utf8"\)\)\.includes\(\s*"\.route\("\s*\)/.test(driftSrc) - || /stripRustCommentsAndLiterals\(readFileSync\(file, "utf8"\)\)\.includes\("\.route\("\)/.test(driftSrc)) - - const maskSrc = driftSrc.match(/function maskStringLiterals\([\s\S]*?\n\}/) - const stripSrc = driftSrc.match(/function stripRustCommentsAndLiterals\([\s\S]*?\n\}/) - if (stripSrc) { - const stripRustCommentsAndLiterals = new Function(`${stripSrc[0]}; return stripRustCommentsAndLiterals`)() - const docOnly = '//! example\n/// `.route("/api/x", get(h))` in docs only\nfn unused() {}\n' - const after = stripRustCommentsAndLiterals(docOnly) - check('doc-comment .route( does not survive strip discovery', - !after.includes('.route('), after) - const real = 'fn router() { axum::Router::new().route("/api/x", get(h)) }\n' - check('real .route( survives strip discovery', - stripRustCommentsAndLiterals(real).includes('.route(')) - } - if (maskSrc) { - const maskStringLiterals = new Function(`${maskSrc[0]}; return maskStringLiterals`)() - const methodConstructor = /\b(get|put|post|delete|options|head|patch|trace)\s*\(/g - const expr = 'get(handler).layer(/* "documentation says get() here" */)' - // Simulate a string that would false-positive without masking: - const withProse = 'get(handler_with_doc("documentation says get() here"))' - const rawHits = [...withProse.matchAll(methodConstructor)].map((m) => m[1]) - const maskedHits = [...maskStringLiterals(withProse).matchAll(methodConstructor)].map((m) => m[1]) - check('prose get() inside a string is a raw false-positive before masking', - rawHits.includes('get') && rawHits.length >= 2, rawHits) - check('masking string literals drops the prose get() false-positive', - maskedHits.length === 1 && maskedHits[0] === 'get', maskedHits) - - const openApiSrc = driftSrc.match(/function openApiApiOperations\([\s\S]*?\n\}/) - check('openApiApiOperations is extractable', !!openApiSrc) - if (openApiSrc) { - const helpers = driftSrc.match(/function operationKey\([\s\S]*?\n\}\n\nfunction normalizePathParameters\([\s\S]*?\n\}/) - const openApiApiOperations = new Function( - `const httpMethodSet = new Set(['get','put','post','delete','options','head','patch','trace']);\n` - + `${helpers ? helpers[0] : 'function operationKey(m,p){return m.toUpperCase()+\" \"+p} function normalizePathParameters(p){return p}'};\n` - + `${openApiSrc[0]}; return openApiApiOperations`, - )() - const ops = openApiApiOperations([ - 'paths:', - ' /api/jobs:run:', - ' post:', - ' summary: run', - ' /api/plain:', - ' get:', - ].join('\n')) - check('OpenAPI path keys with a colon are accepted', - ops.has('POST /api/jobs:run') && ops.has('GET /api/plain'), [...ops]) - } - } -} - -console.log(failures ? `\n${failures} FAILURE(S) — do not dispatch` : '\nALL PASS — safe to dispatch') -process.exit(failures ? 1 : 0) diff --git a/.claude/workflows/program-tick.js b/.claude/workflows/program-tick.js deleted file mode 100644 index 936d8da49..000000000 --- a/.claude/workflows/program-tick.js +++ /dev/null @@ -1,435 +0,0 @@ -export const meta = { - name: 'program-tick', - description: 'Survey the program before doing any work: collect live state mechanically, classify it deterministically in-script, and use agents only where judgement is genuinely required — then dispose of finished PRs and hand the chosen lanes to lane-fanout', - whenToUse: 'At the start of any working session or phase, INSTEAD of hand-picking lanes. Answers "what should be worked on right now, and what is already sitting somewhere unfinished" before a single implementer is spawned.', - phases: [ - { title: 'Collect', detail: 'one mechanical agent: raw git/gh/bd output, verbatim, no interpretation' }, - { title: 'Judge', detail: 'agents only for what cannot be computed: is the work still needed, is the brief real' }, - { title: 'Disposition', detail: 'act on finished PRs by their CURRENT state; never poll' }, - ], -} - -// --------------------------------------------------------------------------- -// DESIGN NOTE — why the split is where it is. -// -// Workflow scripts have no filesystem and no shell: only agent(), parallel(), phase(), log(). So -// running `git`/`gh`/`bd` MUST go through an agent. What must NOT go through an agent is the -// reasoning over that output. Set differences, graph reachability, path-collision detection and -// lane selection are total functions of the collected facts — computing them in an LLM adds a -// hallucination surface to arithmetic that cannot be wrong in JS. -// -// agent -> collection (forced: no fs access) and judgement (genuinely interpretive) -// script -> every classification that is a function of the collected facts -// -// args = { -// candidateWt, candidateTip, base, authority?, maxLanes?=4, fanout?=false, workspace? -// -// maxLanes is a HARD CAP, not advice to the judge: a selection larger than it refuses the whole -// fan-out rather than dispatching past it or silently truncating. -// -// workspace defaults to the directory candidateWt itself lives in. Lane worktrees are RESOLVED -// against the worktree inventory collected below, never constructed from a literal — see the -// fanout chain at the bottom of this file. -// } -// --------------------------------------------------------------------------- - -let ARGS = args -if (typeof ARGS === 'string') { - try { ARGS = JSON.parse(ARGS) } catch (e) { throw new Error(`program-tick: args is not valid JSON: ${e.message}`) } -} -ARGS = ARGS || {} - -// An option this workflow does not read must abort rather than be silently dropped. This guard was -// written for lane-fanout, repeated in backlog-audit, and never applied here — and a rule living in -// two of three sibling files is the signal that it belongs to the shape, not to the file. In a -// sibling runner the same defect (an option accepted, ignored, and the run looking entirely normal) -// cost six lanes. `fanout` and `workspace` are read at the bottom of this file; both are listed. -const KNOWN_ARGS = ['candidateWt', 'candidateTip', 'base', 'authority', 'maxLanes', 'fanout', 'workspace'] -{ - const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) - if (unknown.length) { - throw new Error(`program-tick: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) - } -} - -const CAND_WT = ARGS.candidateWt -const CAND_TIP = ARGS.candidateTip -const BASE = ARGS.base -const MAX_LANES = ARGS.maxLanes || 4 -const AUTHORITY = ARGS.authority || '' - -for (const [k, v] of [['candidateWt', CAND_WT], ['candidateTip', CAND_TIP], ['base', BASE]]) { - if (!v) throw new Error(`program-tick: args.${k} is required`) -} - -const READ_ONLY = ` -=== READ-ONLY. NON-NEGOTIABLE. === -Run ONLY: git status/log/diff/rev-parse/ls-files/worktree list, gh pr list/view/checks, bd list/show/ready/blocked/dep. -NEVER: stash, reset, checkout , rebase, merge, clean, push, worktree add|remove, bd close/update, -gh pr merge/close/edit. Do not edit a single file. You are reading, not deciding and not acting. -DO NOT POLL: read each PR's current check conclusion ONCE. Never sleep, never wait for a run. -` - -// --- COLLECT: raw facts only. No judgement, no classification, no opinion. ------------------- -const RAW_SCHEMA = { - type: 'object', - required: ['candidateFiles', 'worktrees', 'prs', 'beads'], - properties: { - candidateFiles: { type: 'array', items: { type: 'string' }, description: `verbatim output of: git -C ${CAND_WT} diff --name-only ${BASE}..${CAND_TIP}` }, - worktrees: { - type: 'array', - items: { - type: 'object', - required: ['path', 'head', 'dirtyCount', 'prunable', 'filesVsBase', 'filesVsBaseCount', 'commitsAheadOfCandidate'], - properties: { - path: { type: 'string' }, - head: { type: 'string' }, - branch: { type: 'string' }, - dirtyCount: { type: 'number', description: 'lines of git status --porcelain' }, - prunable: { type: 'boolean', description: 'worktree list --porcelain marked it prunable' }, - filesVsBase: { type: 'array', items: { type: 'string' }, description: `git -C diff --name-only ${BASE} — empty array if none or unreadable` }, - // Without the count, an empty filesVsBase cannot be told apart from a capped one, and - // "we did not look" reads exactly like "there is nothing there". - filesVsBaseCount: { type: 'number', description: 'the @@N value: how many files differ from base IN TOTAL, reported even when filesVsBase was capped or unreadable; -1 if the command failed' }, - commitsAheadOfCandidate: { type: 'number', description: `git -C rev-list --count ${CAND_TIP}..HEAD, or -1 if unreadable` }, - }, - }, - }, - prs: { - type: 'array', - items: { - type: 'object', - required: ['number', 'title', 'checkConclusion', 'reviewDecision', 'mergeable', 'headSha'], - properties: { - number: { type: 'number' }, - title: { type: 'string' }, - checkConclusion: { type: 'string', description: 'SUCCESS / FAILURE / PENDING / NONE — the rollup, read once' }, - reviewDecision: { type: 'string' }, - mergeable: { type: 'string' }, - mergeStateStatus: { type: 'string' }, - headSha: { type: 'string' }, - failingChecks: { type: 'array', items: { type: 'string' } }, - isDraft: { type: 'boolean' }, - }, - }, - }, - beads: { - type: 'array', - items: { - type: 'object', - required: ['id', 'title', 'status', 'blockedByOpen'], - properties: { - id: { type: 'string' }, - title: { type: 'string' }, - status: { type: 'string' }, - priority: { type: 'number' }, - blockedByOpen: { type: 'array', items: { type: 'string' }, description: 'ids of OPEN blockers only, from bd show; empty array if none' }, - blocksCount: { type: 'number', description: 'how many issues this one blocks — 0 if none' }, - }, - }, - }, - }, -} - -phase('Collect') - -const raw = await agent(`Collect raw program state. You are a COLLECTOR: report exactly what the commands -print. Do not classify, do not interpret, do not decide anything, do not summarise. Another stage -does all of that; your only failure mode that matters is reporting something you did not observe. - - cd ${CAND_WT} - -1. CANDIDATE FILE SET - git --no-pager diff --name-only ${BASE}..${CAND_TIP} - -2. WORKTREES — there may be a HUNDRED of them. Do NOT run four separate commands per worktree: - that is hundreds of shell round-trips and it is the slowest thing in this survey by far. Run ONE - batched loop and parse its output: - - git worktree list --porcelain | awk '/^worktree /{print $2}' | while read -r w; do - printf '@@WT\\t%s\\t%s\\t%s\\t%s\\n' \\ - "$w" \\ - "$(git -C "$w" rev-parse --short HEAD 2>/dev/null || echo MISSING)" \\ - "$(git -C "$w" status --porcelain 2>/dev/null | wc -l | tr -d ' ')" \\ - "$(git -C "$w" rev-list --count ${CAND_TIP}..HEAD 2>/dev/null || echo -1)" - n=$(git -C "$w" --no-pager diff --name-only ${BASE} 2>/dev/null | wc -l | tr -d ' ') - printf '@@N\\t%s\\n' "$n" - [ "$n" -le 60 ] && git -C "$w" --no-pager diff --name-only ${BASE} 2>/dev/null | sed 's/^/@@F\\t/' - done - - One pass, one round-trip per worktree instead of four. Measured on this repository: 89 worktrees - in ~5 seconds, against many minutes for the per-worktree form. - - The 60-file cap is deliberate. A worktree differing from base by thousands of files is an old - branch, not a lane: its file list is worthless to the classification and would swamp your - output (unfiltered, this repository emits 25,000+ lines). Report its @@N count with an empty - filesVsBase and let the count speak. A LANE worktree is small by construction. - - ALWAYS report @@N as filesVsBaseCount, capped or not. An empty filesVsBase with no count cannot - be told apart from a worktree that genuinely holds nothing, and the classification below would - then offer a capped worktree for removal. - - A worktree whose HEAD prints MISSING has no gitdir: set prunable true, filesVsBase [], - filesVsBaseCount -1 and commitsAheadOfCandidate -1. Do NOT skip it and do NOT guess its contents. - -3. PULL REQUESTS - gh pr list --state open --json number,title,mergeable,reviewDecision,isDraft,headRefOid - For each, ONCE: - gh pr view --json statusCheckRollup,mergeStateStatus - Reduce statusCheckRollup to one word — SUCCESS / FAILURE / PENDING / NONE — and list the names - of any failing checks. If there are zero open PRs, return an empty array. That is a complete - answer, not a failure. - -4. BEADS - bd list --status=open --json (fall back to plain output if --json is unsupported) - bd blocked - For each open bead record its id, title, status, priority, the ids of its OPEN blockers only, - and how many issues it blocks. - -Report numbers you actually saw. If a command fails, report the failure in that record rather than -inventing a plausible value. -${READ_ONLY}`, { label: 'collect', phase: 'Collect', schema: RAW_SCHEMA }) - -// --- CLASSIFY: pure functions of the collected facts. No agent involved. ---------------------- -const candSet = new Set(raw.candidateFiles || []) -const wts = raw.worktrees || [] - -const classifyWorktree = (w) => { - if (w.prunable) return 'prunable' - const files = w.filesVsBase || [] - const ahead = typeof w.commitsAheadOfCandidate === 'number' ? w.commitsAheadOfCandidate : -1 - // UNREADABLE IS NOT UNUSED, and it used to be sorted as `empty` — i.e. offered for removal. Two - // ways the inventory fails to describe a worktree, and both land here: `git rev-list` failed and - // the collector reported -1, and the deliberate 60-file cap, which reports the COUNT and an empty - // file list. In both cases the evidence that the worktree holds nothing is exactly what is - // missing, and removal is the one irreversible action in this plan. - const count = typeof w.filesVsBaseCount === 'number' ? w.filesVsBaseCount : files.length - if (ahead < 0 || count > files.length) return 'unreadable' - const unmerged = files.filter((f) => !candSet.has(f)) - // At risk if it carries commits the candidate lacks, or contributes files the candidate lacks, - // or has uncommitted edits. Anything else contributes nothing that is not already captured. - if (ahead > 0 || unmerged.length > 0) return 'atRisk' - if ((w.dirtyCount || 0) > 0) return 'dirty' - if (files.length === 0) return 'empty' - return 'integrated' -} - -const buckets = { integrated: [], atRisk: [], prunable: [], dirty: [], empty: [], unreadable: [] } -for (const w of wts) buckets[classifyWorktree(w)].push(w) - -// Duplicate work: the same non-candidate file contributed by more than one worktree. -const contributors = new Map() -for (const w of wts) { - for (const f of (w.filesVsBase || [])) { - if (candSet.has(f)) continue - if (!contributors.has(f)) contributors.set(f, []) - contributors.get(f).push(w.path) - } -} -const duplicated = [...contributors.entries()].filter(([, ps]) => ps.length > 1) - .map(([file, paths]) => ({ file, paths })) - -// Bead readiness is a graph fact, not a judgement call. -const beads = raw.beads || [] -const openIds = new Set(beads.filter((b) => b.status === 'open' || b.status === 'in_progress').map((b) => b.id)) -const unblocked = beads.filter((b) => b.status === 'open' && !(b.blockedByOpen || []).some((d) => openIds.has(d))) -const blocked = beads.filter((b) => b.status === 'open' && (b.blockedByOpen || []).some((d) => openIds.has(d))) -// Rank by what unblocks the most, then by priority. -const ranked = [...unblocked].sort((a, b) => (b.blocksCount || 0) - (a.blocksCount || 0) || (a.priority ?? 9) - (b.priority ?? 9)) - -// PR disposition is a decision table over the rollup, not an opinion. -const prAction = (p) => { - if (p.isDraft) return 'report-only' - if (p.checkConclusion === 'PENDING') return 'in-flight' - if (p.checkConclusion === 'FAILURE') return 'fix-then-merge' - if (p.mergeStateStatus === 'BEHIND') return 'rebase-then-merge' - if (p.checkConclusion === 'SUCCESS' && p.reviewDecision !== 'APPROVED') return 'needs-review' - if (p.checkConclusion === 'SUCCESS' && p.mergeable === 'MERGEABLE') return 'merge' - return 'report-only' -} -const prPlan = (raw.prs || []).map((p) => ({ - pr: p.number, title: p.title, action: prAction(p), - checkConclusion: p.checkConclusion, reviewDecision: p.reviewDecision, - failingChecks: p.failingChecks || [], -})) - -log(`collected: ${wts.length} worktrees, ${(raw.prs || []).length} open PRs, ${beads.length} open beads`) -log(`worktrees -> integrated ${buckets.integrated.length}, atRisk ${buckets.atRisk.length}, prunable ${buckets.prunable.length}, dirty ${buckets.dirty.length}, empty ${buckets.empty.length}, unreadable ${buckets.unreadable.length}`) -log(`beads -> ${unblocked.length} unblocked, ${blocked.length} blocked${duplicated.length ? `; ${duplicated.length} file(s) contributed by more than one worktree` : ''}`) - -// --- JUDGE: only what cannot be computed. ----------------------------------------------------- -const JUDGE_SCHEMA = { - type: 'object', - required: ['startNow', 'holdBack', 'alreadyDone', 'coverageRisks'], - properties: { - startNow: { - type: 'array', - items: { - type: 'object', - required: ['key', 'bead', 'owned', 'brief', 'accept', 'briefConfidence'], - properties: { - key: { type: 'string' }, bead: { type: 'string' }, owned: { type: 'string' }, - brief: { type: 'string' }, accept: { type: 'string' }, - briefConfidence: { type: 'string', enum: ['grounded', 'thin'] }, - structurallyNeedsLeasedFile: { type: 'string', description: 'a leased path the deliverable cannot avoid (workspace manifest for a new crate, CI wiring for a new test binary), or "none"' }, - }, - }, - }, - holdBack: { type: 'array', items: { type: 'object', required: ['bead', 'why'], properties: { bead: { type: 'string' }, why: { type: 'string' } } } }, - alreadyDone: { type: 'array', items: { type: 'object', required: ['bead', 'proof'], properties: { bead: { type: 'string' }, proof: { type: 'string' } } } }, - coverageRisks: { type: 'array', items: { type: 'string' } }, - }, -} - -phase('Judge') - -const judged = await agent(`Decide what to actually work on. The mechanical classification is DONE and is not -yours to redo — trust the numbers below and spend your effort only on what arithmetic cannot answer. - -ALREADY COMPUTED (do not recompute): - unblocked beads, ranked by how much they unblock: -${ranked.map((b) => ` ${b.id} [P${b.priority ?? '?'}] blocks:${b.blocksCount ?? 0} — ${b.title}`).join('\n') || ' (none)'} - blocked beads: -${blocked.map((b) => ` ${b.id} — waiting on ${(b.blockedByOpen || []).join(', ')}`).join('\n') || ' (none)'} - worktrees: integrated ${buckets.integrated.length}, atRisk ${buckets.atRisk.length}, prunable ${buckets.prunable.length}, dirty ${buckets.dirty.length}, unreadable ${buckets.unreadable.length} -${buckets.atRisk.length ? ` AT RISK (hold work nowhere else — never propose removing these):\n${buckets.atRisk.map((w) => ` ${w.path} @ ${w.head} (+${w.commitsAheadOfCandidate} commits)`).join('\n')}` : ''} -${duplicated.length ? ` DUPLICATED across worktrees:\n${duplicated.slice(0, 10).map((d) => ` ${d.file} <- ${d.paths.join(', ')}`).join('\n')}` : ''} - - cd ${CAND_WT} -${AUTHORITY ? ` Authority for scope and non-goals: ${AUTHORITY}` : ''} - -YOUR JOB — three judgements, each needing evidence a computation cannot supply: - -1. IS THE WORK STILL NEEDED? For each unblocked bead, CHECK THE CODE before calling it runnable: - grep for the artefact it would create, read the gate it would satisfy, run the check it would - fix. A bead tracking work that already landed is a recurring failure here — list it under - alreadyDone WITH THE PROOF, not under startNow. - -2. WHAT SHOULD ACTUALLY START, at most ${MAX_LANES} — a hard cap: returning more refuses the whole - fan-out and nothing starts. Choose for PATH DISJOINTNESS FIRST: two lanes sharing a writable - path must never both start however ready they look, and the fan-out refuses them anyway. - Prefer lanes that unblock the most. For each, write a brief citing CONCRETE files, line numbers - and the governing authority clause — a brief that restates the bead title is useless, mark it briefConfidence - "thin". Name any leased file the deliverable structurally cannot avoid (a new crate needs the - workspace manifest; a new test binary needs CI wiring) under structurallyNeedsLeasedFile, and - instruct the lane to REPORT it rather than stall or fake it. Omitting that is how a lane gets - authorised to do something while forbidden the only way to do it. - -3. COVERAGE RISKS. Anything landed or about to land WITHOUT a test that can fail, or a claimed - invariant with no oracle. This program has repeatedly shipped guards that could not detect their - own violation: a non-exhaustive matches!, a route check reading a hand-maintained list, a $ref - validator that truncated its input, a preflight test that never called preflight. Hunt that shape. - -Be honest when little is runnable. A thin frontier stated plainly beats lanes invented to look busy. -${READ_ONLY}`, { label: 'judge', phase: 'Judge', schema: JUDGE_SCHEMA }) - -const thin = (judged.startNow || []).filter((l) => l.briefConfidence === 'thin').map((l) => l.key) -if (thin.length) log(`briefs too thin to run unreviewed: ${thin.join(', ')}`) -if ((judged.alreadyDone || []).length) log(`beads tracking work that already landed: ${judged.alreadyDone.length}`) - -// --- DISPOSITION: act on PRs by current state. ------------------------------------------------ -phase('Disposition') - -const actionable = prPlan.filter((p) => ['merge', 'fix-then-merge', 'rebase-then-merge'].includes(p.action)) - -// EVERY DISPOSITION RUNS IN THE SAME CANDIDATE WORKTREE, AND fix-then-merge IS WORK. Two of -// them through parallel() is two writers in one root: exactly the collision lane-fanout refuses at -// dispatch for lanes, arriving here by a different door because PR dispositions are not lanes. -// program-tick is the CALLER that builds this set, so refusing downstream would be late — the -// agents are already chosen. Serialised, not partitioned by action: any of these agents may touch -// the tree, and a rule that has to guess which ones is a rule with a next spelling. Depth costs -// wall-clock and a tick disposes of a handful of PRs, which is the cheap side of the trade. -const disposePr = (p) => agent(`Dispose of PR #${p.pr} ("${p.title}") in ${CAND_WT}. - -DECIDED ACTION: ${p.action} -Current rollup: ${p.checkConclusion}; review: ${p.reviewDecision}${p.failingChecks.length ? `; failing: ${p.failingChecks.join(', ')}` : ''} - - - merge: verify ONCE that every required context is green, review is satisfied, and the head is - still the reviewed head. Then squash merge. If ANY required context is not green, or the head - moved since review, STOP and report — do not merge. - - fix-then-merge: this is WORK, not a watchlist item. Diagnose the failing checks and fix the - CAUSE. Do not re-run hoping for a different answer. Do not disable, skip or weaken the failing - check. If the fix lies outside this PR's scope, report exactly what is needed and stop. - - rebase-then-merge: you may NOT rebase or force-push. Report precisely what the owner must do. - -*** You may not relax branch protection, bypass a required check, skip a test, or merge anything -whose signed authority train is not intact. If the right action needs any of that, report it. *** -Do not poll: act on the state above, once.`, - { label: `pr:${p.pr}`, phase: 'Disposition', schema: { type: 'object', required: ['pr', 'done', 'outcome'], properties: { pr: { type: 'number' }, done: { type: 'boolean' }, outcome: { type: 'string' }, blockedBy: { type: 'string' } } } }) - -const prResults = [] -for (const p of actionable) prResults.push(await disposePr(p)) - -if (!actionable.length) log(`no PR needed action this tick (${prPlan.length} open)`) - -const plan = { - summary: `${ranked.length} unblocked, ${(judged.startNow || []).length} to start, ${buckets.atRisk.length} worktrees at risk, ${prPlan.length} open PRs`, - startNow: judged.startNow || [], - holdBack: judged.holdBack || [], - alreadyDone: judged.alreadyDone || [], - coverageRisks: judged.coverageRisks || [], - worktrees: { - counts: Object.fromEntries(Object.entries(buckets).map(([k, v]) => [k, v.length])), - // NEVER the worktree this tick is operating IN. It classifies as integrated by construction — - // its diff against base IS the candidate file set — so it sat at the head of a list titled - // "safe to remove", one copy-paste away from deleting the checkout doing the work. - safeToRemove: buckets.integrated.concat(buckets.empty) - .map((w) => w.path).filter((p) => String(p).replace(/\/+$/, '') !== CAND_WT.replace(/\/+$/, '')), - mustNotRemove: buckets.atRisk.map((w) => ({ path: w.path, head: w.head, aheadBy: w.commitsAheadOfCandidate })), - // Not removable and not at risk either: we simply cannot see what they hold. Reported so the - // gap is visible rather than resolved by a silent default in either direction. - unreadable: buckets.unreadable.map((w) => w.path), - prunableRegistrations: buckets.prunable.map((w) => w.path), - duplicated, - }, - prPlan, - prResults, -} - -// A lane worktree must be one this tick OBSERVED, never one it composed. This used to send every -// selected lane to a literal absolute path under one developer's home directory: a path that exists -// on exactly one machine, that nothing in this workflow creates, and that on CI, on Linux, or in any -// other checkout pointed every implementer at nothing at all. (The preflight now forbids the -// spelling as well as the instance.) Workflow scripts have no filesystem, so the -// only evidence a path exists is the inventory the collector just read — resolve against that, and -// refuse to spawn anything if a lane has no single unambiguous worktree. -const WORKSPACE = (ARGS.workspace || CAND_WT.replace(/\/+$/, '').replace(/\/[^/]*$/, '')).replace(/\/+$/, '') -const resolveLaneWt = (key) => { - const hits = wts.filter((w) => !w.prunable && (w.path === `${WORKSPACE}/${key}` || w.path.endsWith(`-${key}`))) - return { hits: hits.map((w) => w.path) } -} - -// Chaining is opt-in and refuses to spawn implementers off a brief nobody has read. -if (ARGS.fanout && (judged.startNow || []).length && !thin.length) { - const resolved = judged.startNow.map((l) => ({ lane: l, ...resolveLaneWt(l.key) })) - const unusable = resolved.filter((r) => r.hits.length !== 1).map((r) => ({ - key: r.lane.key, - why: r.hits.length ? `ambiguous: ${r.hits.join(', ')}` : `no worktree under ${WORKSPACE} matches lane "${r.lane.key}"`, - })) - // maxLanes lived ONLY in the judge's prompt, and a prompt is a request. The judge is free to - // return more, and everything it returned was then fanned out — the cap was read, printed, and - // enforced by nobody. Enforced HERE because here is the last point before implementers exist. - const overCap = judged.startNow.length > MAX_LANES - ? [{ key: '(all)', why: `${judged.startNow.length} lanes selected but maxLanes=${MAX_LANES}. Raise maxLanes deliberately, or narrow the selection — do not dispatch past a cap the caller set.` }] - : [] - const blocked = [...overCap, ...unusable] - if (blocked.length) { - // Refuse the WHOLE fanout, not the offending lanes only: chaining the remaining subset - // would silently drop selected work, which is the same defect in a different spelling. - plan.fanoutBlocked = blocked - log(`FANOUT REFUSED — no implementer dispatched. ${plan.fanoutBlocked.map((b) => `${b.key}: ${b.why}`).join(' | ')}`) - if (unusable.length) log(`create the missing worktree(s) from ${CAND_TIP} under ${WORKSPACE}, or pass args.workspace, then re-run with fanout`) - } else { - log(`chaining into lane-fanout with ${resolved.length} lane(s): ${resolved.map((r) => r.hits[0]).join(', ')}`) - plan.fanoutResult = await workflow('lane-fanout', { - tip: CAND_TIP, - lanes: resolved.map(({ lane: l, hits }) => ({ - key: l.key, bead: l.bead, owned: l.owned, brief: l.brief, accept: l.accept, wt: hits[0], - })), - }) - } -} else if (ARGS.fanout && thin.length) { - log('fanout requested but some briefs are thin — returning the plan for enrichment rather than spawning implementers') -} - -return plan diff --git a/.claude/workflows/review-gate.js b/.claude/workflows/review-gate.js deleted file mode 100644 index 2d098d99c..000000000 --- a/.claude/workflows/review-gate.js +++ /dev/null @@ -1,137 +0,0 @@ -export const meta = { - name: 'review-gate', - description: 'Per-story quality gate: fan out correctness + RLS-as-console_rt security + a codex cross-model review of a diff (and web a11y/perf when relevant), then synthesize one GO/NO-GO with ranked must-fix findings. Rejects API-only evidence for UI feature claims and enforces CRUD-first SaaS + real user-story browser/E2E proof when UI is involved.', - phases: [ - { title: 'Review', detail: 'parallel lanes: correctness · security/RLS · codex cross-model · (web a11y/perf)' }, - { title: 'Synthesize', detail: 'GO/NO-GO verdict + ranked must-fix' }, - ], -} - -// NOTE: this is a Workflow SCRIPT, not a standalone Node module. The runtime injects -// agent()/parallel()/phase()/log()/args and runs the body in an async context, so top-level `await` -// and the trailing `return` are the DOCUMENTED form — `node --check` will (wrongly) flag the return as -// "Illegal return statement". Do NOT wrap the body in a function to satisfy node; that breaks the runtime. -// args: { commit?, base?, head?, kind?: "backend"|"web"|"mixed"|"design", context?: string } -// Single commit: pass `commit`. Multi-commit story: pass `base` + `head` (reviews base..head). -// NOTE: the runtime may deliver `args` as a JSON STRING rather than an object — parse defensively, -// else `args.base`/`args.head` are undefined and the gate silently falls back to HEAD~1..HEAD. -const A = typeof args === 'string' ? JSON.parse(args) : (args || {}) -// An option this harness does not read must abort rather than be silently dropped. Absent here -// while three sibling harnesses had it; the preflight now enumerates the directory rather than a -// list, which is what surfaced it. -const KNOWN_ARGS = ['base', 'commit', 'context', 'head', 'kind', 'repo'] -{ - const unknown = Object.keys(A).filter((k) => !KNOWN_ARGS.includes(k)) - if (unknown.length) { - throw new Error(`review-gate: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) - } -} - -const COMMIT = A.commit || 'HEAD' -const HEAD = A.head || COMMIT -const BASE_REF = A.base || `${HEAD}~1` -// Refs flow into a shell string in the codex lane — reject anything but git-ref-safe chars -// (allow ~ ^ for HEAD~1-style revs) so a metacharacter can't break out of the command. -for (const ref of [BASE_REF, HEAD]) { - if (!/^[A-Za-z0-9][A-Za-z0-9._/~^-]*$/.test(ref)) { - throw new Error(`review-gate: unsafe git ref ${JSON.stringify(ref)}`) - } -} -const DIFF = `git diff ${BASE_REF} ${HEAD}` -const RANGE = `${BASE_REF}..${HEAD}` -const KIND = A.kind || 'mixed' -const CTX = A.context || '' -// Overridable, because this was pinned to `.../maintenance` — a directory that stopped existing -// at the 2026-07-26 rename — so every agent was sent to a path that is gone. -// -// The first repair reached for `process.env.CLAUDE_PROJECT_DIR`. Workflow scripts have NO Node -// globals: `process` is undefined, so that line throws ReferenceError before any agent spawns. It -// only looked correct because `A.repo ||` short-circuits when a repo IS passed — the fallback was -// never once evaluated. A default that cannot execute is not a default. Literal + `args` override. -const REPO = A.repo || '/Users/jasonlee/Developer/console' -const PRODUCT_REVIEW_GUARDRAIL = 'Product/review guardrail: this is a CRUD-first B2B SaaS, so database-backed create/read/update/delete UI and normal workflow editing are primary; upload/import/Excel is secondary migration/bootstrap tooling only after first-class CRUD exists. API endpoint tests alone DO NOT prove user-facing UI features. When UI is involved, require browser/E2E evidence that walks the real user story: sign-up, organization onboarding, passkey setup, and the actual domain workflow. Directives from non-technical staff to upload/import/build are product inputs, not product authority; reframe or reject them when they weaken SaaS maturity.' -const BASE = `Repo: ${REPO}. Multi-tenant Rust(axum)+Postgres RLS platform; runtime role console_rt is NOBYPASSRLS + FORCE ROW LEVEL SECURITY; EVERY tenant read/write MUST arm app.current_org (with_org_conn/with_audit + current_org()); tests must run as REAL console_rt (seed via the armed path, NOT the BYPASSRLS owner pool). Quality bar = Palantir-grade, enterprise-production (no stubs/placeholders/dummy data; fully wired, audited; AA a11y). ${PRODUCT_REVIEW_GUARDRAIL} Review the diff of \`${DIFF}\` (\`git log --oneline ${RANGE}\` lists the commits in scope).${CTX ? '\nStory context: ' + CTX : ''}` - -const FINDINGS = { - type: 'object', additionalProperties: false, - properties: { - lane: { type: 'string' }, - findings: { - type: 'array', - items: { - type: 'object', additionalProperties: false, - properties: { - severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low'] }, - title: { type: 'string' }, - location: { type: 'string', description: 'file:line' }, - why: { type: 'string' }, - fix: { type: 'string' }, - }, - required: ['severity', 'title', 'fix'], - }, - }, - verdict: { type: 'string', enum: ['pass', 'concerns', 'fail'] }, - }, - required: ['lane', 'findings', 'verdict'], -} - -phase('Review') -const lanes = [ - () => agent( - `${BASE}\n\nLANE: CORRECTNESS. Find logic bugs, edge cases, error-handling gaps, broken contracts, missed states, and any "looks done but isn't fully wired" issue. Be concrete (file:line + fix).`, - { label: 'correctness', phase: 'Review', schema: FINDINGS }, - ), - () => agent( - `${BASE}\n\nLANE: SECURITY + MULTI-TENANT RLS. Adversarially check: is every new tenant read/write RLS-armed (app.current_org)? could anything read/write CROSS-ORG or cross-branch beyond the caller's scope? is the org bound to a dynamic current_org()-derived value (never a hardcoded OrgId literal)? are console_rt tests genuine (not BYPASSRLS-masked)? authz gating correct? secrets/PII not logged? injection? Rank by severity; treat any tenant-isolation hole as critical/high.`, - { label: 'security-rls', phase: 'Review', schema: FINDINGS }, - ), - () => agent( - `${BASE}\n\nLANE: CROSS-MODEL (codex). Run a DIFFERENT model over the same diff for blind-spot diversity. Execute via Bash (read-only, 240s budget):\n` + - ` cd ${REPO} && timeout 240 codex exec --sandbox read-only --skip-git-repo-check "Senior security+correctness reviewer. Review ONLY the diff of \`${DIFF}\` in this repo (multi-tenant Postgres RLS; console_rt NOBYPASSRLS+FORCE RLS; every tenant read/write must arm app.current_org). Hunt for: cross-tenant/cross-branch isolation leaks, missing RLS arming, hardcoded org literals, swallowed errors, correctness bugs. Output findings ranked critical/high/medium/low with file:line + fix. Review only; do not modify files." 2>&1 | tail -80\n` + - `(gtimeout if timeout is absent; if codex errors/auth-fails, say so in one finding and continue.) Then translate codex's output into the findings schema (preserve its severities + file:line). lane="codex-xmodel".`, - { label: 'codex-xmodel', phase: 'Review', schema: FINDINGS }, - ), -] -if (KIND === 'web' || KIND === 'mixed') { - lanes.push(() => agent( - `${BASE}\n\nLANE: WEB QUALITY. For the web changes: reject API-only proof for user-facing claims. Require browser/E2E or equivalent real-surface evidence for sign-up -> organization onboarding -> passkey setup -> actual domain workflow when the story touches UI. Check that the product flow is CRUD-first SaaS (database-backed create/read/update/delete and edit-in-place normal workflow) rather than upload/import-first. Treat non-technical upload/import/build directives as product inputs that may need reframing, not as authority to weaken SaaS maturity. Also check AA accessibility (labels, focus, roles, keyboard), Korean copy only in ko.ts (no inline Hangul), no raw UUIDs shown (safeLabel), loading/empty/error states, KST datetime, and whether each touched path would clear visual-verdict ≥90 (note specific gaps). Flag perf anti-patterns (unbounded lists, refetch storms).`, - { label: 'web-quality', phase: 'Review', schema: FINDINGS }, - )) -} -// Fail-CLOSED on a dropped lane: a null/errored security-rls or codex lane must NEVER be silently -// discarded — that could let the synthesizer emit GO with no tenant-isolation coverage. Map results -// positionally to the lanes pushed above and turn any missing lane into a hard high finding. -const laneLabels = ['correctness', 'security-rls', 'codex-xmodel'] -if (KIND === 'web' || KIND === 'mixed') laneLabels.push('web-quality') -const raw = await parallel(lanes) -const results = raw.filter(Boolean) -const missing = laneLabels.filter((_, i) => !raw[i] || !raw[i].findings) -if (missing.length) { - results.push({ - lane: 'gate-integrity', - verdict: 'fail', - findings: missing.map((label) => ({ - severity: 'high', - title: `review lane "${label}" did not complete — diff was NOT fully reviewed`, - fix: `Re-run the gate. A missing lane (especially security-rls / codex-xmodel) means tenant-isolation/cross-model coverage is absent; treat as NO-GO until every lane completes.`, - })), - }) -} -const all = results.flatMap((r) => (r.findings || []).map((f) => ({ ...f, lane: r.lane }))) -const crit = all.filter((f) => f.severity === 'critical').length -const high = all.filter((f) => f.severity === 'high').length -log(`${results.length} lanes; findings: ${crit} critical, ${high} high, ${all.length} total`) - -phase('Synthesize') -const verdict = await agent( - `You are the gate keeper. Below are per-lane review findings (correctness, security/RLS, codex cross-model${KIND !== 'backend' ? ', web-quality' : ''}) for \`${DIFF}\` (${RANGE}).\n\n` + - JSON.stringify(results) + - `\n\nDedupe across lanes (same issue found by multiple = higher confidence). Then issue a GO/NO-GO:\n` + - `- NO-GO if any CRITICAL, or any tenant-isolation/security HIGH, or a correctness HIGH that breaks the feature.\n` + - `- NO-GO if a UI/user-facing feature is supported only by API endpoint tests, handler tests, or unit tests without real user-story browser/E2E proof covering sign-up, organization onboarding, passkey setup, and the actual domain workflow.\n` + - `- NO-GO if the change treats upload/import/Excel as the primary product path where CRUD-first SaaS UI/workflows should exist, or accepts non-technical upload/import/build directives as product authority instead of product input to reframe.\n` + - `- GO-WITH-FIXES if only medium/low.\n` + - `Output: (1) the verdict (GO / GO-WITH-FIXES / NO-GO); (2) the MUST-FIX-BEFORE-CHECKPOINT list (critical+high, deduped, each with file:line + the fix); (3) the should-fix (medium) + nice (low) lists; (4) which findings the codex cross-model lane caught that the same-model lanes missed (the value of cross-model). Concise + decisive — this gates the ultragoal checkpoint.`, - { label: 'verdict', phase: 'Synthesize', effort: 'high' }, -) -return { range: RANGE, kind: KIND, critical: crit, high, total: all.length, verdict } diff --git a/.claude/workflows/scout.js b/.claude/workflows/scout.js deleted file mode 100644 index 84ce9b7f0..000000000 --- a/.claude/workflows/scout.js +++ /dev/null @@ -1,499 +0,0 @@ -export const meta = { - name: 'scout', - description: 'Read-only: census the backlog, verify its dependency edges by re-deriving them, compute the critical path in-script, and emit an incomplete fan-out plan (beads + owned roots) whose lanes provably cannot collide', - whenToUse: 'Before a fan-out, when you need to know WHAT to parallelise and in WHICH order. Produces bead/root proposals for lane-fanout; callers must still supply tip/wt/brief/accept. Never edits code, never opens a PR.', - phases: [ - { title: 'Census', detail: 'one agent gathers raw tracker + repo state' }, - { title: 'Verify', detail: 'fan out: is each ready item real, and is each dependency edge real?' }, - { title: 'Plan', detail: 'critical path computed in-script, lanes proved disjoint' }, - ], -} - -// --------------------------------------------------------------------------- -// args = { repo, maxLanes?=4, integrationBranch?, focus? } -// -// WHY THIS IS NOT PART OF lane-fanout: that harness answers "is this change correct". -// This one answers "what should we work on, in what order, and what can run at once". -// Different subject, different output, and -- decisively -- this one must be able to -// run when there is NO candidate tip and NO worktree, which lane-fanout cannot. -// -// WHY THE ORDERING IS COMPUTED HERE AND NOT BY AN AGENT: a critical path is a -// deterministic function of a graph. Asking a model to "figure out the order" makes -// the answer unreproducible and unauditable, and this programme has already paid for -// a dependency graph that was silently REVERSED -- `bd dep add A B` and -// `bd dep A --blocks B` are inverse forms, the wrong one inverts the whole graph, and -// `bd dep cycles` stays green either way. So: agents report OBSERVATIONS, the script -// computes CONCLUSIONS. -// --------------------------------------------------------------------------- - -let ARGS = args -if (typeof ARGS === 'string') { - try { ARGS = JSON.parse(ARGS) } catch (e) { - throw new Error(`scout: args arrived as a string that is not valid JSON: ${e.message}`) - } -} -ARGS = ARGS || {} - -const KNOWN_ARGS = ['repo', 'maxLanes', 'integrationBranch', 'focus', 'batch'] -{ - const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) - if (unknown.length) { - throw new Error(`scout: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) - } -} - -const REPO = ARGS.repo -const MAX_LANES = ARGS.maxLanes || 4 -const FOCUS = ARGS.focus || '' -// FAN-OUT MUST NOT SCALE WITH BACKLOG SIZE. The first run dispatched one agent per edge and one per -// ready bead: 57 + 74 = 131 agents against a 92-bead tracker, and 117 of them died when the account -// limit was reached mid-run -- so the run cost a full quota and returned a plan with zero lanes. -// Width is also nearly free in wall-clock terms while DEPTH is what costs (12 agents at depth 6 took -// 74 minutes here; 36 at the same depth took 63), so a swarm buys nothing and risks everything. -// Work is batched instead: agent count is bounded by ceil(n / BATCH) and the depth is unchanged. -const BATCH = ARGS.batch === undefined ? 8 : ARGS.batch -if (!Number.isInteger(BATCH) || BATCH < 1) { - throw new Error(`scout: batch must be a positive integer; got ${JSON.stringify(ARGS.batch)}`) -} -const chunk = (xs, n) => xs.reduce((a, x, i) => (i % n ? a[a.length - 1].push(x) : a.push([x]), a), []) -if (!REPO) throw new Error('scout: args.repo is required') -if (!Number.isInteger(MAX_LANES) || MAX_LANES < 1) { - throw new Error(`scout: maxLanes must be a positive integer; got ${JSON.stringify(ARGS.maxLanes)}`) -} - -const RULES = [ - '=== READ-ONLY. ABSOLUTELY. ===', - 'You may not edit a file, create or close a bead, comment on an issue, commit, push, or open a PR.', - 'You are deciding what OTHER lanes will do. A scout that changes the ground it is surveying makes', - 'every measurement after it wrong.', - '', - '=== REPORT OBSERVATIONS, NOT CONCLUSIONS ===', - 'Do not rank, order, or decide what is on the critical path. That is computed from what you report.', - 'Your job is to make each observation TRUE; the ordering is arithmetic over the set of them.', - '', - '=== THE FAILURES THIS PHASE EXISTS TO CATCH ===', - '1. A DEPENDENCY EDGE POINTING THE WRONG WAY. `bd dep add A B` means "A depends on B";', - ' `bd dep A --blocks B` is the INVERSE. Using the wrong one reverses the graph and', - ' `bd dep cycles` still reports clean. Never trust the stored direction: re-derive it from the', - ' WORK. Read both beads and answer in plain language which one cannot start until the other is', - ' done, then say whether the stored edge agrees. A disagreement is a finding, not a footnote.', - '2. A BEAD THAT IS ALREADY DONE. The tracker lags the tree. Before calling anything ready, check', - ' whether the code it asks for already exists on the default branch.', - '3. PARENT-CHILD READ AS BLOCKING. `bd blocked` counts parent-child edges as blocking, which makes', - ' epics look like hard dependencies and hides work that is actually startable. Say which kind', - ' each edge is.', - '4. A BEAD WHOSE OWNED ROOT DOES NOT CONTAIN WHAT IT NEEDS. Authorising a goal while forbidding the', - ' file it structurally needs has cost this programme four rounds. Name every path the work must', - ' touch, including the tests that encode the OLD behaviour a change would have to update, and', - ' including generated peripherals (the documentation manifest pins doc blob OIDs, so any docs/**', - ' edit drags it in).', -].join('\n') - -// --- Census ---------------------------------------------------------------- -phase('Census') - -const CENSUS_SCHEMA = { - type: 'object', - required: ['readyBeads', 'blockedBeads', 'edges', 'openPrCount', 'integrationBranches'], - properties: { - readyBeads: { - type: 'array', - description: 'beads with no unmet dependency, whatever the tracker says about status', - items: { - type: 'object', - required: ['id', 'title', 'priority', 'paths'], - properties: { - id: { type: 'string' }, - title: { type: 'string' }, - priority: { type: 'number', description: '0 = highest' }, - paths: { type: 'array', items: { type: 'string' }, description: 'every path the work must touch — the owned root is derived from this, so an omission blocks a lane later' }, - alreadyDone: { type: 'boolean', description: 'TRUE if the tree already satisfies it — with evidence' }, - evidence: { type: 'string' }, - }, - }, - }, - blockedBeads: { - type: 'array', - items: { - type: 'object', - required: ['id', 'title', 'blockedBy'], - properties: { - id: { type: 'string' }, - title: { type: 'string' }, - blockedBy: { type: 'array', items: { type: 'string' } }, - }, - }, - }, - edges: { - type: 'array', - description: 'every dependency edge, as STORED. Direction is verified later, not here.', - items: { - type: 'object', - required: ['from', 'to', 'kind'], - properties: { - from: { type: 'string', description: 'the dependent — cannot start until `to` is done' }, - to: { type: 'string' }, - kind: { type: 'string', enum: ['blocks', 'parent-child', 'related', 'unknown'] }, - }, - }, - }, - openPrCount: { type: 'number' }, - integrationBranches: { type: 'array', items: { type: 'string' } }, - trackerDrift: { type: 'string', description: 'where beads and GitHub issues disagree' }, - }, -} - -const census = await agent( - `Census the backlog and the repository state. REPO: ${REPO} -${FOCUS ? `FOCUS: ${FOCUS}\n` : ''} -${RULES} - -Gather, and report exactly what you observed: - -1. BEADS. Every open bead: id, title, priority, and the dependency edges it participates in. - Use \`bd list\`, \`bd show\`, \`bd dep\` and \`bd blocked\`. Report edges AS STORED — do not correct - them here, a later phase re-derives direction independently and comparing the two is the point. - For each edge say which KIND it is; parent-child and blocks are counted the same by \`bd blocked\` - and they are not the same thing. - -2. FOR EACH BEAD THAT LOOKS READY, the PATHS the work must touch. Be exhaustive and concrete: - source files, the tests that encode the behaviour being changed, migrations, generated artifacts. - This list becomes a lane's owned root, and a lane whose root omits a file it structurally needs - cannot finish. Over-report rather than under-report, but do not list a whole crate when one - module is meant. - -3. ALREADY DONE? For each ready bead, check whether the default branch already satisfies it. The - tracker lags the tree here. Say so with file:line evidence, and do NOT close anything. - -4. REPO STATE. Count open PRs. List branches whose name suggests they are integration branches. - Report how many worktrees exist. This measures whether work is piling up unlanded. - -Report only what you ran and saw.`, - { schema: CENSUS_SCHEMA, label: 'census' }, -) - -if (!census) throw new Error('scout: the census agent died — nothing can be planned from a null census') - -const ready = (census.readyBeads || []).filter((b) => b && b.id) -const edges = (census.edges || []).filter((e) => e && e.from && e.to) -if (!ready.length) { - log('scout: the census found NO ready beads. That is a legitimate state (everything blocked or done),') - log('but it is also what a census that failed to read the tracker looks like. Check its coverage note.') -} -log(`census: ${ready.length} ready, ${(census.blockedBeads || []).length} blocked, ${edges.length} edges, ${census.openPrCount} open PRs`) - -// --- Verify ---------------------------------------------------------------- -// Two independent questions, fanned out together because neither needs the other's answer. -phase('Verify') - -const EDGE_VERDICT = { - type: 'object', - required: ['verdicts'], - properties: { verdicts: { type: 'array', items: { type: 'object', - required: ['from', 'to', 'storedDirectionIsCorrect', 'reasoning'], - properties: { - from: { type: 'string' }, - to: { type: 'string' }, - storedDirectionIsCorrect: { type: 'boolean' }, - reasoning: { type: 'string', description: 'which work cannot start until which other work is done, in plain language, derived from reading BOTH beads' }, - isRealDependency: { type: 'boolean', description: 'FALSE if they merely touch nearby code — that is a scheduling hint, not a dependency' }, - } } } }, -} - -const READY_VERDICT = { - type: 'object', - required: ['verdicts'], - properties: { verdicts: { type: 'array', items: { type: 'object', - required: ['id', 'isReady', 'paths', 'reasoning'], - properties: { - id: { type: 'string' }, - isReady: { type: 'boolean' }, - alreadyDone: { type: 'boolean' }, - paths: { type: 'array', items: { type: 'string' } }, - sizeHint: { type: 'string', enum: ['small', 'medium', 'large', 'unknown'] }, - reasoning: { type: 'string' }, - } } } }, -} - -const [edgeVerdicts, readyVerdicts] = await Promise.all([ - parallel(chunk(edges, BATCH).map((batch) => () => - agent( - `Verify ${batch.length} dependency edge(s) by re-deriving each from the work itself. REPO: ${REPO} - -STORED EDGES: -${batch.map((e) => ` ${e.from} depends on ${e.to} (kind: ${e.kind})`).join('\n')} - -Return one verdict per edge, in the same order. Do not merge or skip any. - -${RULES} - -Read BOTH beads and the code each concerns. Then answer, in plain language, which one genuinely -cannot start until the other is finished — deriving it from the work, NOT from the stored edge. -Only then compare your answer to the stored direction. - -This exists because the two \`bd dep\` forms are inverses, the wrong one reverses the graph, and -\`bd dep cycles\` stays green either way. A reversed edge schedules the whole plan backwards. - -Also decide whether this is a real dependency at all. Two beads touching nearby code is a -scheduling hint — it belongs in the same lane — not a dependency.`, - { schema: EDGE_VERDICT, label: `edges:${batch[0].from}+${batch.length - 1}`, phase: 'Verify' }, - ))), - parallel(chunk(ready, BATCH).map((batch) => () => - agent( - `Verify ${batch.length} backlog item(s) are genuinely ready, and enumerate everything each must touch. -REPO: ${REPO} - -BEADS: -${batch.map((b) => ` ${b.id} — ${b.title}\n census said it touches: ${(b.paths || []).join(', ') || '(nothing listed — itself suspicious)'}`).join('\n')} - -Return one verdict per bead, in the same order. Do not merge or skip any. - -${RULES} - -1. Is it ALREADY SATISFIED by the default branch? Check before anything else, with file:line - evidence. The tracker lags the tree. -2. Does it have an unmet dependency the census missed? -3. Enumerate EVERY path the work must touch. Include: the source, the tests that encode the - behaviour being changed (a behaviour change forces the tests asserting the old behaviour), any - migration, and any generated peripheral. If the work touches docs/**, the documentation manifest - pins doc blob OIDs and must be regenerated, so it belongs in the list. - An owned root that omits a file the work structurally needs cannot be finished by the lane that - gets it, and that failure costs a full round. -4. Size it: small / medium / large.`, - { schema: READY_VERDICT, label: `ready:${batch[0].id}+${batch.length - 1}`, phase: 'Verify' }, - ))), -]) - -// Each agent now returns { verdicts: [...] } for a batch, so flatten. A batch that DIED contributes -// nothing rather than a null that later code would read as a verdict. -const liveEdges = (edgeVerdicts || []).filter(Boolean).flatMap((r) => r.verdicts || []) -const liveReady = (readyVerdicts || []).filter(Boolean).flatMap((r) => r.verdicts || []) - -const deadEdges = edges.length - liveEdges.length -const deadReady = ready.length - liveReady.length -if (deadEdges || deadReady) { - log(`!! ${deadEdges} edge check(s) and ${deadReady} readiness check(s) DIED. Their subjects are UNVERIFIED`) - log(' and are excluded from the plan below rather than silently assumed good.') -} - -const reversed = liveEdges.filter((v) => v.storedDirectionIsCorrect === false) -const notReal = liveEdges.filter((v) => v.isRealDependency === false) -if (reversed.length) { - log(`!! ${reversed.length} STORED EDGE(S) POINT THE WRONG WAY — the tracker's graph is inverted for:`) - for (const r of reversed) log(` ${r.from} -> ${r.to}: ${r.reasoning.slice(0, 160)}`) - log(' The plan below uses the RE-DERIVED direction. Fix the tracker separately.') -} - -// --- Plan ------------------------------------------------------------------ -// Arithmetic, not judgement. -phase('Plan') - -// Corrected graph: flip what verification says is backwards, drop what is not a real dependency. -// UNVERIFIED EDGES ARE NOT KEPT. A dead edge-verification batch used to fall through to -// `else corrected.push(stored)`, so the plan ordered work from precisely the edges nobody -// re-derived — including ones that point the wrong way. Fail closed: no verdict, no edge. -const corrected = [] -const unverifiedEdges = [] -for (const e of edges) { - const v = liveEdges.find((x) => x.from === e.from && x.to === e.to) - if (!v) { unverifiedEdges.push(e); continue } - if (v.isRealDependency === false) continue - if (v.storedDirectionIsCorrect === false) corrected.push({ from: e.to, to: e.from }) - else corrected.push({ from: e.from, to: e.to }) -} -if (unverifiedEdges.length) { - log(`!! ${unverifiedEdges.length} stored edge(s) have no verification verdict — dropped from the plan (fail closed)`) - for (const e of unverifiedEdges.slice(0, 12)) log(` unverified: ${e.from} -> ${e.to}`) -} - -const startable = liveReady - .filter((v) => v.isReady === true && v.alreadyDone !== true) - .map((v) => { - const b = ready.find((r) => r.id === v.id) || {} - return { id: v.id, title: b.title || v.id, priority: b.priority ?? 2, paths: v.paths || b.paths || [], sizeHint: v.sizeHint || 'unknown' } - }) - -const doneAlready = liveReady.filter((v) => v.alreadyDone === true).map((v) => v.id) -if (doneAlready.length) log(`already satisfied by the tree, do NOT dispatch: ${doneAlready.join(', ')}`) - -// Longest-path depth over the corrected graph. Depth is what actually costs wall-clock: this -// programme measured 12 agents at depth 6 taking 74 minutes and 36 agents at the same depth taking -// 63 -- latency tracks DEPTH, and is nearly flat in WIDTH. So the critical path is the schedule. -// Edges are dependent `from` → prerequisite `to`. A ready bead has no outgoing deps, so measuring -// prerequisites always yields depth 0. Measure what starting the bead UNLOCKS: reverse edges. -function depthOf(id, seen = new Set()) { - if (seen.has(id)) return 0 // a cycle cannot lengthen the path; it is reported separately - seen.add(id) - const downstream = corrected.filter((e) => e.to === id).map((e) => e.from) - return downstream.length ? 1 + Math.max(...downstream.map((d) => depthOf(d, new Set(seen)))) : 0 -} - -const withDepth = startable.map((b) => ({ ...b, depth: depthOf(b.id) })) -const maxDepth = withDepth.reduce((m, b) => Math.max(m, b.depth), 0) -const criticalPath = withDepth.filter((b) => b.depth === maxDepth).map((b) => b.id) - -// Two lanes may never share a worktree, and may never own overlapping roots: they cannot corrupt -// each other's files, but they collide at LAND time, after every reviewer has passed. -function overlaps(a, b) { - return a.some((x) => b.some((y) => x.startsWith(y) || y.startsWith(x))) -} - -const ordered = [...withDepth].sort((a, b) => b.depth - a.depth || a.priority - b.priority) -const deferred = [] -// PATHS ARE AN AGENT'S PROSE UNTIL THIS FUNCTION SAYS OTHERWISE. -// The header of this file says agents report observations and the script computes conclusions. That -// was applied to the ORDERING and not to the PATHS, and a measured run showed all three ways it fails: -// - absolute paths: 64 of one lane's 64 roots came back as /Users///backend/... -// - prose in the array: one entry was "`*?]/.test(r)) return null // prose, globs, placeholders - // Absolute paths must sit under REPO with a path-segment boundary. A prefix test alone accepts - // `/workspace/console-lane/...` when REPO is `/workspace/console` (sibling worktree leak). - if (r.startsWith('/')) { - const repo = String(REPO).replace(/\/+$/, '') - if (r === repo || r.startsWith(`${repo}/`)) r = r.slice(repo.length) - else return null - } - r = r.replace(/^\/+/, '') - // A trailing file becomes its directory before segment collapse, so both spellings of a - // directory and a file inside it reduce to the same root. Extensionless files (Dockerfile, - // BUCK, Makefile, LICENSE, …) must NOT be mistaken for directories: inventing - // `backend/Dockerfile/` authorises a nonexistent subtree. Preserve the exact file path. - let fileRoot = false - if (r && !r.endsWith('/')) { - const base = r.split('/').pop() || '' - if (/\.[A-Za-z0-9]+$/.test(base)) r = r.slice(0, -base.length) - else if (base) fileRoot = true - } - // Canonicalise `./backend/crates/foo/` and `backend/crates/foo/` to the same root. Without - // this, overlaps treats them as disjoint and the packer can put one directory in two lanes. - const parts = r.split('/').filter((seg) => seg && seg !== '.') - if (parts.includes('..')) return null - r = parts.join('/') - if (!r) return null - if (!fileRoot && !r.endsWith('/')) r += '/' - if (r === '/') return null - if (r.split('/').filter(Boolean).length < MIN_ROOT_SEGMENTS) return null // "backend/" is the repo - return r -} - -const placeable = [] -for (const item of ordered) { - // Count failed normalisations BEFORE dedupe. Two valid files under one directory share a root; - // subtracting after Set would misclassify them as rejected and defer ordinary multi-file work. - const normalised = (item.paths || []).map(normaliseRoot) - const rejected = normalised.filter((r) => !r).length - const roots = [...new Set(normalised.filter(Boolean))] - if (!roots.length) { - deferred.push({ ...item, why: (item.paths || []).length - ? `every reported path was unusable as an owned root (${(item.paths || []).length} rejected: absolute, prose, or repo-wide)` - : 'no paths reported — an owned root cannot be derived' }) - continue - } - // A PARTIAL OWNED ROOT IS WORSE THAN NO LANE. The guard above defers a bead whose paths are ALL - // unusable; this one was merely logging the partial case and emitting the lane anyway, so a lane - // could be authorised for work while forbidden from touching a file that work structurally needs. - // That failure arrives AFTER dispatch -- the lane builds, then blocks or fails review -- and it has - // cost this programme four rounds in exactly that shape. Same defect as the all-unusable case, one - // spelling over: rejecting SOME is not a smaller version of rejecting ALL, it is the same problem - // with the evidence hidden in a log line. - if (rejected) { - const bad = (item.paths || []).filter((raw) => !normaliseRoot(raw)) - deferred.push({ ...item, why: `${rejected} of ${(item.paths || []).length} reported path(s) are unusable as owned roots, so any lane would be authorised for the work while forbidden from part of it: ${bad.join(', ')}` }) - log(`${item.id}: DEFERRED — ${rejected} unusable path(s): ${bad.join(', ')}`) - continue - } - placeable.push({ ...item, roots }) -} - -// GROUP BY TERRITORY BEFORE CREATING LANES, transitively. -// The first version grew a lane's roots as it absorbed a bead, and a grown lane can overlap a lane -// created EARLIER -- so a run that had already done all its verification threw at the final guard and -// discarded 15 agents of work. It also merged a bead into only the FIRST lane it clashed with, while -// leaving the second clash unaddressed. Both are the same mistake: deciding membership one item at a -// time against a set that is still moving. -// Union-find settles the whole partition first, so two lanes CANNOT share territory by construction -// and the guard below becomes an assertion that can never fire rather than a way to lose a run. -const parent = placeable.map((_, i) => i) -const find = (i) => (parent[i] === i ? i : (parent[i] = find(parent[i]))) -for (let i = 0; i < placeable.length; i++) { - for (let j = i + 1; j < placeable.length; j++) { - if (overlaps(placeable[i].roots, placeable[j].roots)) parent[find(i)] = find(j) - } -} -const groups = new Map() -placeable.forEach((item, i) => { - const root = find(i) - if (!groups.has(root)) groups.set(root, []) - groups.get(root).push(item) -}) - -// Deepest group first: depth is what costs wall-clock, so the longest chain must start earliest. -const sorted = [...groups.values()].sort((a, b) => - Math.max(...b.map((x) => x.depth)) - Math.max(...a.map((x) => x.depth)) || - Math.min(...a.map((x) => x.priority)) - Math.min(...b.map((x) => x.priority))) - -const lanes = [] -for (const group of sorted) { - if (lanes.length >= MAX_LANES) { - for (const item of group) deferred.push({ ...item, why: 'maxLanes reached' }) - continue - } - const beads = group.map((x) => x.id) - const roots = [...new Set(group.flatMap((x) => x.roots))] - lanes.push({ key: beads[0].replace(/[^a-z0-9]/gi, '').slice(-6) || `l${lanes.length}`, beads, roots, depth: Math.max(...group.map((x) => x.depth)) }) -} - -// The guard that makes the output trustworthy: never emit a plan whose lanes would be refused. -for (let i = 0; i < lanes.length; i++) { - for (let j = i + 1; j < lanes.length; j++) { - if (overlaps(lanes[i].roots, lanes[j].roots)) { - throw new Error(`scout: emitted lanes ${lanes[i].key} and ${lanes[j].key} own overlapping roots — this plan would be refused at dispatch, which means the packing above is wrong`) - } - } -} - -log(`plan: ${lanes.length} lane(s), critical path depth ${maxDepth}, ${deferred.length} deferred`) -for (const l of lanes) log(` ${l.key}: ${l.beads.join(' ')} @ ${l.roots.join(' ')}`) - -// ONE integration branch, ONE PR. N lanes each opening a PR is how a queue of unreviewable -// branches accumulates; this programme reached twelve-plus worktrees above main with zero open PRs, -// then the opposite. The lane fan-out lands every converged lane onto ONE branch, and that branch -// is what becomes a pull request -- once. -const integration = ARGS.integrationBranch || 'integration/scouted' - -return { - headline: [ - `${startable.length} startable, ${lanes.length} lanes proposed, critical path depth ${maxDepth}`, - reversed.length ? `${reversed.length} STORED DEPENDENCY EDGE(S) ARE REVERSED — tracker needs fixing` : 'every verified edge points the right way', - notReal.length ? `${notReal.length} stored edge(s) are not real dependencies` : null, - doneAlready.length ? `${doneAlready.length} bead(s) already satisfied by the tree` : null, - deadEdges || deadReady ? `${deadEdges + deadReady} check(s) died — those subjects are UNVERIFIED` : null, - ].filter(Boolean), - criticalPath, - reversedEdges: reversed, - alreadyDone: doneAlready, - deferred, - unverifiedEdges, - // INCOMPLETE for lane-fanout. A prior revision advertised this as `fanoutArgs` that could be - // "fed straight into lane-fanout", but every nonempty plan aborted: lane-fanout requires tip and - // per-lane wt/brief/accept, which a read-only scout must not invent. Briefs especially: writing - // them here would decide the implementation without reading the code. Callers must supply the - // missing fields before dispatch. - fanoutPlan: { - status: 'incomplete', - reason: 'missing tip and per-lane wt/brief/accept — scout only proposes beads and owned roots', - integrationBranch: integration, - land: true, - lanes: lanes.map((l) => ({ key: l.key, bead: l.beads.join(' '), owned: l.roots.join(' ') })), - }, -} diff --git a/.claude/workflows/slice.js b/.claude/workflows/slice.js deleted file mode 100644 index 4785877b2..000000000 --- a/.claude/workflows/slice.js +++ /dev/null @@ -1,528 +0,0 @@ -export const meta = { - name: 'slice', - description: 'Build ONE vertical slice through the full pipeline: explore, design, RED tests first, implement, cover, doubt, simplify, security, CI integration, then two adversarial reviewers who see the DIFF ONLY. Reusable for any slice — parameterised by task, exploration areas, and owned paths.', - whenToUse: 'Any bounded implementation slice where correctness matters more than speed: a new crate, a gate, a domain type, a migration. Not for trivial edits (a 2-line change does not need ten phases).', - phases: [ - { title: 'Explore', detail: 'parallel readers — read the code, do not guess' }, - { title: 'Design', detail: 'competing designs, then judged and synthesised' }, - { title: 'Red', detail: 'failing tests FIRST, each observed failing for the right reason' }, - { title: 'Implement', detail: 'make them green without rewriting them' }, - { title: 'Cover', detail: 'the rest of the tests, measured not estimated' }, - { title: 'Doubt', detail: 'hunt what is wrong and repair it' }, - { title: 'Simplify', detail: 'smaller without weaker — never delete a check' }, - { title: 'Security', detail: 'attack it as a hostile tenant' }, - { title: 'Integrate', detail: 'prove every new test actually executes in CI' }, - { title: 'Prove', detail: 'two adversarial reviewers — diff only, no author reasoning' }, - ], -} - -// Workflow SCRIPT, not a Node module: the runtime injects agent()/parallel()/phase()/log()/args -// and runs the body in an async context. Top-level await and the trailing return are the -// documented form — `node --check` will wrongly flag the return. Do not wrap in a function. -// -// args: { -// task: string — what to build. Required. -// context?: string — domain facts the agents cannot infer. -// explore?: [{label, prompt}] — exploration areas. Defaults to one generalist reader. -// owns?: string — paths the implementer owns. A COHERENT SLICE, not a file list (see below). -// crate?: string — crate boundary for the work queue. Bun grouped ~16k errors BY CRATE, -// never by file, explicitly to prevent task fragmentation. -// designs?: number — competing designs before the judge. Default 2. -// lane?: "1".."5" — REQUIRED for anything that builds. Routes implement/prove into -// ~/Developer/console-lanes/lane-N, which has its own backend/target. -// Omitting it builds in the main checkout and contends on the build lock. -// repo?: string -// } -// NO NODE GLOBALS. The script body runs in a bare sandbox: `process` is not defined, so a -// `process.env.HOME` fallback is not a fallback — it is an immediate ReferenceError that kills the -// run before agent 1 starts. (`Date.now`/`Math.random` are likewise banned, as they would break -// resume.) Paths are therefore literals or `args`. Verified by the failure this line replaced. -const HOME = '/Users/jasonlee' -const A = typeof args === 'string' ? JSON.parse(args) : (args || {}) -// The unknown-option guard runs BEFORE the required-field check on purpose: with the order -// reversed, a caller who typos an option gets "`task` is required" and goes looking for the wrong -// thing. Report the typo you can see, not the consequence of it. -const KNOWN_ARGS = ['base', 'context', 'crate', 'designs', 'explore', 'lane', 'owns', 'repo', 'task'] -{ - const unknown = Object.keys(A).filter((k) => !KNOWN_ARGS.includes(k)) - if (unknown.length) { - throw new Error(`slice: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) - } -} -if (!A.task) throw new Error('slice: `task` is required') -const REPO = A.repo || `${HOME}/Developer/console` - -// MEASURED 2026-07-28: a workflow that ran its implementer with cwd = the MAIN checkout turned a -// single-crate `cargo check` into 47 MINUTES — the log reads `Blocking waiting for file lock on -// build directory`, because the implementer and the caller contended on one `backend/target`. CI -// for the same change is 20 minutes. The bottleneck was never CI; it was a shared target dir. -// -// Anything that BUILDS therefore runs in a lane worktree, which has its own `target/`. Read-only -// exploration may use the main checkout because reads take no build lock. -const LANE = A.lane ? `${HOME}/Developer/console-lanes/lane-${A.lane}` : null -const BUILD_CWD = LANE || REPO -if (!LANE) log('WARNING: no `lane` arg — implement/prove will build in the MAIN checkout and may contend on the build lock. Pass lane: "1".."5".') -// What a reviewer diffs against. The implementer now COMMITS, so `git diff` alone would show an -// empty tree and a reviewer would report "no changes" as a pass. -const BASE_REF = A.base || 'origin/main' -const N_DESIGNS = Math.min(Math.max(A.designs ?? 2, 1), 3) -const CRATE = A.crate || '' -const OWNS = A.owns || '' - -// ── Discipline shared by every agent ──────────────────────────────────────── -// Each line below was earned by a measured failure, not adopted on principle. -const DISCIPLINE = ` -## Non-negotiable discipline - -- **Verify by EXECUTION.** Run it; quote real output. Cite \`file:line\` of CODE, never a header - comment — a plan premise died because a migration's header described the problem it had already - fixed. Reasoning that feels airtight has been wrong repeatedly here. -- **A probe must be proven RED on a known-bad input before its GREEN is trusted.** Six verification - probes were defective in one session; in one case the models were right and the *grader* was wrong. - A probe with no demonstrated failure mode is not evidence. -- **Reproduce the original failure, not the artifact you touched.** A repair once fixed 1 of 3 - commands and reported green because it tested only its own file. -- **\`git stash\` and \`git reset\` are BANNED.** Commit or abandon. Atomic, per-file commits. - (Bun had to amend their workflow to forbid these after agents used them to escape trouble.) -- **Write findings to disk as you go.** Agents have gone idle without ever returning a report; do not - rely on your final message surviving. -- **\`grep\` is unreliable in this shell** (exits 1 on files it matches). Use \`awk\` for anything - load-bearing. -- **Escalate rather than settle.** If the correct fix lies outside your slice, STOP and report it. - Do not implement the second-best fix — an implementer once knowingly shipped a worse design because - a briefing file-list omitted the file the real fix needed. The briefing was the defect. -` - -const CTX = ` -Repo: ${REPO} -${A.context ? A.context + '\n' : ''}${CRATE ? `Crate boundary: \`${CRATE}\`. Group work BY CRATE, never by file — \`cargo check -p ${CRATE}\` is the work queue.\n` : ''}${OWNS ? `Slice you own: ${OWNS}\n` : ''} -## Task -${A.task} -${DISCIPLINE}` - -// Appended to EVERY phase, exploration included. -// -// The first version excluded exploration on the reasoning that "reads take no build lock". That -// premise was refuted by observation within the hour: an explorer asked to confirm a call signature -// ran `cargo test -p console-ontology-rest --test publish_auto_create_action_as_runtime_role` -// against the MAIN checkout's Cargo.toml and held `backend/target/debug/.cargo-lock` for over seven -// minutes, blocking `npm run verify` behind `Blocking waiting for file lock on build directory`. -// Verifying a signature by executing it is exactly the discipline demanded elsewhere in this file, -// so the fix is to give exploration a lane too — not to tell it to stop running things. -const WORKDIR = ` -## WORKING DIRECTORY — READ THIS BEFORE RUNNING ANYTHING - -**Your inherited cwd is NOT your lane.** You start in whatever directory the calling session -happened to be in when you were spawned, and that value drifts constantly as the caller works. -Assume it is wrong. - -**Run this FIRST — before \`git status\`, before \`pwd\`, before any orientation command at all:** -\`\`\`bash -cd ${BUILD_CWD} -source ${REPO}/scripts/console/lane-env.sh # RUSTC_WRAPPER=sccache, 50G ceiling -\`\`\` - -This is not a formality. An implementer once oriented itself with \`git status\` in its inherited -cwd, found a DIFFERENT lane's branch and commits there, concluded that its own brief was stale, -and asked whether it should ignore the instruction and work in the wrong tree. It was right about -what it saw and wrong about what it meant. Had it proceeded, its work would have landed in another -agent's open pull-request branch and been swept into that agent's next commit. - -If what you find after \`cd\` contradicts your brief, that is a real conflict worth escalating. If -you find a contradiction BEFORE \`cd\`, you are simply in the wrong directory. -sccache's cache is user-global (\`~/Library/Caches/Mozilla.sccache\`), so lanes — and other repos on -this machine — reuse each other's compiled artifacts. Without it every lane recompiles the whole -dependency graph from cold: measured \`Cache hits: 0%\` across 4,084 commands, because nothing had -ever sourced this. Confirm with \`sccache --show-stats\` after your build; hits should climb. - -${LANE - ? `This is an isolated lane worktree with its own \`backend/target\`, so your build cannot contend with -another agent's. Building in the main checkout instead is what turned a single-crate \`cargo check\` -into 47 minutes.` - : `NOTE: no lane was assigned, so this IS the main checkout and you may contend on the build lock -with concurrent agents. Report the contention if a build stalls rather than waiting it out.`} -` - -phase('Explore') - -const EXPLORE_SCHEMA = { - type: 'object', additionalProperties: false, - required: ['area', 'findings', 'exact_api', 'gotchas'], - properties: { - area: { type: 'string' }, - findings: { type: 'string', description: 'What EXISTS, with file:line. Concrete, not summary.' }, - exact_api: { type: 'string', description: 'Exact signatures/paths to call, copied verbatim from source.' }, - gotchas: { type: 'array', items: { type: 'string' }, description: 'What will silently break a naive implementation.' }, - }, -} - -const areas = A.explore?.length ? A.explore : [{ - label: 'survey', - prompt: 'Survey what already exists for this task. The most valuable finding is that some of it is already built — assume nothing is greenfield until you have checked.', -}] - -const facts = (await parallel(areas.map((a) => () => - agent(`${CTX}${WORKDIR}\n## YOUR EXPLORATION AREA\n${a.prompt}\n\nRead the code. Return exact APIs a caller must use — an implementer will build directly from your answer, so a vague finding becomes their wrong guess.`, - { label: `explore:${a.label}`, phase: 'Explore', schema: EXPLORE_SCHEMA }) -))).filter(Boolean) - -log(`${facts.length}/${areas.length} exploration lanes returned`) - -phase('Design') - -const DESIGN_SCHEMA = { - type: 'object', additionalProperties: false, - required: ['approach', 'file_layout', 'verification', 'risks'], - properties: { - approach: { type: 'string' }, - file_layout: { type: 'string', description: 'Exact paths and what each contains.' }, - verification: { type: 'string', description: 'How this is PROVEN to work, including what would make it fail.' }, - risks: { type: 'array', items: { type: 'string' } }, - }, -} - -// Competing designs, not one. Bun could skip this because a .zig reference removed -// design entirely; where no reference exists, one design is one unexamined guess. -const BIASES = [ - 'BIAS: minimal surface. Fewest files, least abstraction, no framework. An interface with one implementation is a defect here.', - 'BIAS: durability. Optimise for surviving change underneath you. What breaks when the substrate moves, and what makes this immutable?', - 'BIAS: falsifiability. Optimise for the failure being LOUD and specific. How does this fail for the right reason, and how would a silent pass be detected?', -] - -const designs = (await parallel( - Array.from({ length: N_DESIGNS }, (_, i) => () => - agent(`${CTX}\n\nVerified exploration findings:\n${JSON.stringify(facts, null, 2)}\n\n## Design it\n${BIASES[i % BIASES.length]}`, - { label: `design:${i + 1}`, phase: 'Design', schema: DESIGN_SCHEMA })) -)).filter(Boolean) - -const spec = designs.length === 1 ? JSON.stringify(designs[0], null, 2) : await agent( - `${CTX}\n\nCompeting designs:\n${JSON.stringify(designs, null, 2)}\n\n## JUDGE and SYNTHESISE - -Pick the better spine and graft the best ideas from the others. State plainly which you chose and -what you took from the losers — a synthesis that silently drops a rival's best idea is a worse -outcome than either design alone. - -Judge in this order: -1. **Is it verifiable, and does it fail LOUDLY?** A design that cannot demonstrate its own failure is - disqualified regardless of elegance. -2. **Is it simple?** Fewest files, least abstraction. -3. **Does it survive the substrate changing?** - -Return a FINAL SPECIFICATION concrete enough to implement with no further design decisions: exact -paths, exact APIs from the exploration findings, and the exact proof of correctness.`, - { label: 'judge', phase: 'Design' }) - -// ── The build pipeline ────────────────────────────────────────────────────── -// Red tests BEFORE implementation, then defect-hunting, simplification, security and CI wiring as -// SEPARATE passes before the final adversarial verification. Each stage is its own agent because a -// single agent asked to implement AND simplify AND security-review its own work grades its own -// homework — the same reason the reviewers never see the implementer's narrative. -// -// Order is deliberate and is not arbitrary taste: defects are fixed before simplification (you -// cannot safely simplify code that is wrong), simplification precedes security review (so the -// review reads what actually ships, not a draft), and CI wiring precedes verification (so the -// verifier can confirm the gate really executes rather than that it merely exists). -const COMMIT_RULE = ` -**COMMIT as you go** on the branch already checked out in your lane. Atomic commits, one coherent -step each. Stage the paths you own BY NAME — never \`git add -A\` or \`git commit -a\`, which is how -one agent's work ends up inside another agent's commit. \`git stash\` and \`git reset\` stay banned. - -An earlier version of this workflow told implementers to leave work UNCOMMITTED because "the caller -owns landing". That contradicted the stash/reset ban, an implementer followed the nearer rule, and a -concurrent \`git reset --hard\` in the same tree destroyed a finished, passing deliverable. The -caller owns landing; it does not own keeping your work alive, and neither does the filesystem.` - -phase('Red') - -// TDD, and the reason it is a separate phase with its own gate: a test written after the code tends -// to assert what the code does. Written first, it asserts what the code SHOULD do. The gate is that -// the tests must be OBSERVED failing — a red test nobody watched fail is just an unproven claim. -const red = await agent(`${CTX}${WORKDIR} - -Invoke the \`test-driven-development\` skill and follow it. - -## SPECIFICATION — already judged. Do NOT implement it yet. -${spec} - -Exploration findings you may rely on: -${JSON.stringify(facts, null, 2)} - -## Your job: write the FAILING tests, and nothing else -Write the tests that will prove this specification correct, BEFORE any implementation exists. Then -RUN them and paste the real failure output. - -Hard rules: -1. **Do not write implementation code.** If a test cannot even compile without a function that does - not exist yet, add the smallest possible signature that returns \`unimplemented!()\` or its - equivalent — never a working body. That stub is the thing the next phase replaces. -2. **Every test must be OBSERVED failing, and for the RIGHT REASON.** Quote the actual output. A - test that fails because a helper is missing, a fixture is wrong, or the file does not compile is - NOT a red test — it is a broken test that happens to be red. Distinguish these explicitly. -3. **A test that passes before the implementation exists is a defect in the test.** Say so and fix - it. This is the single most valuable thing this phase produces. -4. Assert BEHAVIOUR, not implementation shape. A test that pins internal structure blocks the - simplification phase for no safety gain. -5. Include the negative and refusal cases now, not later — those are the ones that get quietly - dropped when written after the fact. -${COMMIT_RULE} - -Report: each test, the failure you observed, and whether that failure is the right one.`, - { label: 'red', phase: 'Red' }) - -phase('Implement') - -const impl = await agent(`${CTX}${WORKDIR} - -## SPECIFICATION — already judged. Implement it; do not redesign. -${spec} - -## THE RED TESTS ARE ALREADY WRITTEN AND OBSERVED FAILING -${red} - -## Your job -Make those tests pass. Do not rewrite them to fit your implementation — if a test is genuinely -wrong, say so explicitly and explain why rather than quietly editing it green. Changing a test to -match the code you wrote inverts the entire point of writing it first. - -Implement it, and ${COMMIT_RULE.trim().slice(2)} - -This instruction used to read "leave changes UNCOMMITTED, the caller owns landing", which -contradicted the \`git stash\`/\`git reset\` ban three paragraphs above it. An implementer followed -the nearer rule, and a concurrent agent's \`git reset --hard\` in the same tree destroyed the -finished deliverable — a working mechanism, already passing, gone. Two rules pointing opposite ways -is a defect in the process, not in the agent that picked one. - -Never \`git commit -a\` or \`git add -A\`: stage the paths you own by name. A blanket add is how one -agent's work ends up inside another agent's commit. -${OWNS ? `You own: ${OWNS}. Touching anything else is a scope violation; escalate instead.\n` : ''} -Requirements: -1. It must COMPILE${CRATE ? ` — run \`cargo check -p ${CRATE}\` and iterate until clean` : ''}. -2. It must be PROVEN, by execution, with real output quoted. -3. If you add a Rust crate, a valid \`Cargo.toml\` lands in the SAME change — an unmatched workspace - glob breaks the build for every lane. Never hand-edit a generated BUCK file. -4. **Report what you could NOT verify, plainly.** An unproven claim stated as fact is worse than an - admitted gap, because it survives review.`, - { label: 'implement', phase: 'Implement' }) - -phase('Cover') - -const cover = await agent(`${CTX}${WORKDIR} - -The specification is implemented and the red tests are green. Read the diff yourself: -\`git diff ${BASE_REF}...HEAD\`. - -## Your job: close the coverage gap the red tests did not reach -The red phase wrote the tests that prove the SPEC. That is necessarily narrower than the code that -now exists. Find what ships untested and test it. - -1. **Measure, do not estimate.** Use whatever coverage tooling this repo has; if none is wired, walk - every branch of the new code by hand and say that is what you did. "Looks well covered" is not a - measurement. -2. Prioritise by consequence, not by line count: error paths, refusal paths, boundary values, and - anything touching authorization, tenancy or money. An untested happy path is a risk; an untested - refusal path is a vulnerability. -3. **Every test you add must be proven RED first** — break the code, watch it fail, restore with a - \`cp\` backup (never \`git checkout\`, which would discard uncommitted work). A test added at this - stage is at the highest risk of asserting what the code does rather than what it should do. -4. State the residual gap plainly. Some things genuinely cannot be tested at this layer; naming them - is worth more than a fabricated test that pretends otherwise. -${COMMIT_RULE}`, - { label: 'cover', phase: 'Cover' }) - -phase('Doubt') - -// Defect-hunting BEFORE simplification: simplifying wrong code produces elegant wrong code, and the -// elegance makes the wrongness harder to see. -const doubt = await agent(`${CTX}${WORKDIR} - -Invoke the \`doubt-driven-development\` skill, and draw on \`code-review-and-quality\` and -\`debugging-and-error-recovery\` as needed. Follow them. - -An implementation was just committed on this lane's branch. Read it yourself: -\`git diff ${BASE_REF}...HEAD\`. You are NOT given the implementer's account — their reasoning would -prime you to accept it. - -## Your job: find what is WRONG, then fix it -You may edit, unlike the final reviewers. This is the repair pass. - -1. **Doubt every claim the code makes about itself.** Comments, docstrings and commit messages are - the least reliable artefacts in this repository — it has recorded THREE separate incidents of a - comment describing a problem it had already fixed, one of them yesterday. Where a comment and the - code disagree, the code is the fact and the comment is the defect. -2. **Hunt the failure modes the tests do not express.** This suite has TWICE been unable to - distinguish a correct implementation from a wrong one: a resolver that produced a byte-identical - graph while writing false history, and an as-of read silently becoming a head read. Both were - caught by reading the diff and asking what ELSE would produce this green. -3. Check concurrency, partial failure, and rollback: what happens if this is interrupted midway? -4. Fix what you find, with a test that would have caught it. Report anything you judge out of scope - as an explicit escalation rather than leaving it unsaid. -${COMMIT_RULE}`, - { label: 'doubt', phase: 'Doubt' }) - -phase('Simplify') - -const simplify = await agent(`${CTX}${WORKDIR} - -Invoke the \`code-simplification\` skill and the \`ponytail\` skill. Follow them. - -Read the diff yourself: \`git diff ${BASE_REF}...HEAD\`. - -## Your job: make it smaller without making it weaker -The tests are green and the known defects are fixed. Now remove what does not earn its place. - -1. **Behaviour must not change.** Run the full test set before and after; both must be identical. - If a simplification requires a test to change, it is not a simplification — stop and report it. -2. Delete speculative generality: an abstraction with one implementation, a parameter every caller - passes the same value for, a config knob nothing configures, a branch nothing reaches. -3. Prefer the stdlib and what this repo already has over anything new. Reuse beats invention. -4. **Do NOT simplify away:** input validation at trust boundaries, error handling that prevents data - loss, authorization checks, tenancy scoping, or any assertion. Removing a check is not - simplification, it is scope reduction wearing a disguise — and this repo's reviewers explicitly - check for deleted assertions. -5. If the diff is already tight, say so and change nothing. A pass that invents work to look busy is - worse than a pass that reports the code is clean. -${COMMIT_RULE}`, - { label: 'simplify', phase: 'Simplify' }) - -phase('Security') - -// After simplification deliberately: the review must read what actually ships. -const security = await agent(`${CTX}${WORKDIR} - -Invoke the \`security-and-hardening\` skill (and \`claude-security\` if relevant). Follow them. - -Read the diff yourself: \`git diff ${BASE_REF}...HEAD\`. You may edit to fix what you find. - -## Your job: attack this change -This is a multi-tenant system with row-level security, policy-based authorization and an audit -chain. Assume an attacker holds valid credentials for ONE tenant and wants another tenant's data. - -1. **Tenancy first.** Is every new query armed with the org scope? Is any read reachable that - bypasses RLS? A superuser or BYPASSRLS read in a test makes the isolation assertion vacuous — - check that the tests assert as the genuine non-superuser runtime role. -2. **Authorization, not authentication.** Who can call this? Is the check on the right side of the - trust boundary? Does a deny-by-default path stay deny-by-default under every input, including - empty, null and absent? -3. **Injection and trust boundaries.** Any string interpolated into SQL, any client-supplied - identifier used as a key, any input reaching a \`SECURITY DEFINER\` function. -4. **Failure modes.** Does an error leak internal state? Does a partial failure leave a half-applied - write? Does a raw database error escape as a 500 where a mapped 4xx belongs? -5. Fix what you find. For anything you cannot fix in scope, ESCALATE it explicitly with its concrete - attack path — a named vulnerability is worth more than a silent one. -${COMMIT_RULE}`, - { label: 'security', phase: 'Security' }) - -phase('Integrate') - -// CI wiring as its own phase because "a test that cannot execute in CI is not a deliverable" has -// been violated repeatedly here — most recently a Buck target that existed, was correct, and that no -// workflow ever referenced, so the only proof of a core mechanism had never once run. -const integrate = await agent(`${CTX}${WORKDIR} - -Invoke the \`ci-cd-and-automation\` skill and follow it. - -Read the diff yourself: \`git diff ${BASE_REF}...HEAD\`. - -## Your job: make every new test actually EXECUTE in CI -A test that cannot run in CI is not a deliverable. This repository has shipped the failure twice: a -Buck target that existed and was correct but that no workflow referenced — so the only committed -proof of a core mechanism had never executed — and a required check whose display name promised -something its steps did not run. - -1. Trace each new test from the file to the workflow step that runs it. Name the chain explicitly: - generator entry, build target, wrapper, workflow step. A missing link anywhere means it never runs. -2. Generated files are GENERATED. Never hand-edit one; change the generator and regenerate. -3. **Prove it, do not assume it.** Run the local equivalent of the CI job. If a job cannot run - locally, say exactly which link you could not verify and why. -4. If a new gate should become a required check, say so and explain the sequencing — a required - context that has never reported blocks every merge, so it must report green at least once first. -${COMMIT_RULE}`, - { label: 'integrate', phase: 'Integrate' }) - -phase('Prove') - -const VERDICT_SCHEMA = { - type: 'object', additionalProperties: false, - required: ['verdict', 'proven_failable', 'weakening', 'residual'], - properties: { - verdict: { type: 'string', enum: ['PASS', 'FAIL'] }, - proven_failable: { type: 'string', description: 'Concrete inputs you RAN that make it fail, with real output. Could not make it fail = FAIL.' }, - weakening: { type: 'string', description: 'Was any assertion deleted, loosened, skipped or made conditional? Diff it yourself.' }, - residual: { type: 'array', items: { type: 'string' }, description: 'Still vacuous or unverified after this change.' }, - }, -} - -// Bun's reviewers received the DIFF ONLY and never the implementer's reasoning — the author's -// narrative primes a reviewer to accept. Passing the implementer's report here (as I did in an -// earlier workflow) quietly defeats the mechanism, so these agents are told to read the diff -// themselves and are given no summary of it. -const REVIEW_BASE = `${CTX}${WORKDIR} - -Invoke the \`verification-before-completion\` skill and follow it. - -**READ-ONLY. No edits, no git mutations.** Every earlier phase could edit; you cannot. You are the -last thing between this change and the branch, and your job is to disbelieve it. - -An implementation was just COMMITTED on this lane's branch. **Read the diff yourself** — -\`git diff ${BASE_REF}...HEAD\`, plus \`git log --oneline ${BASE_REF}..HEAD\` and -\`git status --short\` to confirm nothing was left dangling. You are deliberately NOT given the -implementer's account of what they did: their reasoning would prime you to accept it, and Bun's -reviewers saw the diff alone for exactly this reason. - -**If you build or test, do it in THIS lane and nowhere else.** Never run a build in another agent's -lane — two writers in one worktree share a \`target/\` and a build lock, and the results are not -merely slow but WRONG. A verification run made while another agent was building in the same tree -reported a test binary as 0-passed/3-failed; the identical command on an uncontended tree reported -3-passed/0-failed minutes later. A contended run is not evidence, in either direction. - -Assume the change is WRONG and look hard before conceding anything. Bun's reviewers also rejected -solutions that needed a paragraph of justification to defend — a workaround that must be explained -is a defect.` - -const verdicts = (await parallel([ - () => agent(`${REVIEW_BASE} - -## YOUR LENS: vacuity and correctness -1. Can you make it FAIL? Construct concrete failing inputs and RUN them. If you cannot make it fail, - that is a FAIL verdict — a check that cannot fail is not a check. -2. Would a trivial STUB satisfy this? Try to write one. Success here is the most valuable finding - available. -3. Was any existing assertion deleted, loosened or made conditional?`, - { label: 'prove:vacuity', phase: 'Prove', schema: VERDICT_SCHEMA }), - - () => agent(`${REVIEW_BASE} - -## YOUR LENS: discipline and honesty — deliberately different from the other reviewer -1. Does it actually compile and run? Run it. Do not accept that it does. -2. Are the project's testing constraints honoured — non-superuser runtime role, disposable Postgres, - \`--test-threads=1\`, no hand-edited generated files? -3. **Is anything claimed that was not verified?** Compare what the code proves against what any - comment or docstring asserts. Overstatement that survives review becomes tomorrow's false premise. -4. Did it stay inside its slice? Check \`git status\` for files outside ${OWNS || 'the declared scope'}.`, - { label: 'prove:discipline', phase: 'Prove', schema: VERDICT_SCHEMA }), -])).filter(Boolean) - -const passed = verdicts.length === 2 && verdicts.every((v) => v.verdict === 'PASS') - -// "Edit the process, not the outputs." When a lane produces bad work, Bun fixed the PROMPT and -// reran rather than hand-patching the diff. Emit that rerun brief instead of a bare failure, so the -// next attempt corrects the instruction rather than the symptom. -const rerun = passed ? null : { - guidance: 'Do NOT hand-patch the diff. Fix the brief and rerun this workflow — edit the process, not the outputs.', - failures: verdicts.filter((v) => v.verdict !== 'PASS').flatMap((v) => [v.proven_failable, v.weakening]).filter(Boolean), - suggested_context_additions: verdicts.flatMap((v) => v.residual || []), -} - -return { - specification: spec, - red, - implementation: impl, - cover, - doubt, - simplify, - security, - integrate, - verdicts, - passed, - rerun, -} diff --git a/.claude/workflows/stale-take-audit.js b/.claude/workflows/stale-take-audit.js deleted file mode 100644 index 090aad7d9..000000000 --- a/.claude/workflows/stale-take-audit.js +++ /dev/null @@ -1,243 +0,0 @@ -export const meta = { - name: 'stale-take-audit', - description: 'Find every file where a hand-rebuilt branch silently took the OLD side of a file the base branch had advanced — before CI finds them one push at a time', - whenToUse: 'After reconstructing a branch by copying a tree rather than merging, or after any hand-written exclusion list decided which side of a file to keep. Run it BEFORE the first push: it found in one pass what cost five CI round-trips to discover one file at a time.', - phases: [ - { title: 'Audit', detail: 'batched: does main have content this branch dropped?' }, - { title: 'Confirm', detail: 'adversarially re-check only the files claimed stale' }, - ], -} - -// The defect class, stated once so every agent judges the same thing: -// -// PR #618 was rebuilt by taking a superseded branch's TREE for the files it owned, with a -// hand-written exclusion list for files main owned. That list was a LIST -- each entry reasoned -// about individually -- so it was complete only for the cases already thought of. Five CI pushes -// have each surfaced one more file where main had advanced and the copy silently reverted it: -// ci.yml (un-wired a test suite main runs), and the ci-preflight lock and its counters. -// -// A revert that reintroduces old content is INVISIBLE in the diff-to-main -- it looks like an -// ordinary change. Only comparing against what main HAS reveals it. - -// args can arrive as a JSON STRING. Every other harness here guards for it and this one did not, -// so the first run died at line 16 having spawned zero agents. -let ARGS = args -if (typeof ARGS === 'string') { - try { ARGS = JSON.parse(ARGS) } catch (e) { - throw new Error(`stale-take-audit: args arrived as a string that is not valid JSON: ${e.message}`) - } -} -ARGS = ARGS || {} - -// Unknown options are rejected BEFORE required fields, so a typo is reported as the typo rather -// than as the missing field it happens to look like. -const KNOWN_ARGS = ['repo', 'main', 'files', 'batch'] -{ - const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) - if (unknown.length) { - throw new Error(`stale-take-audit: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) - } -} - -const FILES = ARGS.files -const REPO = ARGS.repo -const MAIN = ARGS.main -if (!Array.isArray(FILES) || !FILES.length) throw new Error('stale-take-audit: args.files must be a non-empty array') -if (!REPO || !MAIN) throw new Error('stale-take-audit: args.repo and args.main are required') - -const BATCH = ARGS.batch || 6 -const chunk = (xs, n) => xs.reduce((a, x, i) => (i % n ? a[a.length - 1].push(x) : a.push([x]), a), []) - -const RULES = ` -REPO: ${REPO} BASE BRANCH: ${MAIN} (read-only: do not edit, commit, or push anything) - -For each file, run BOTH directions IN ${REPO} (never the inherited cwd) and read them: - git -C ${REPO} diff HEAD ${MAIN} -- # '+' lines = what MAIN has that HEAD LACKS <-- the danger - git -C ${REPO} diff ${MAIN} HEAD -- # '+' lines = what HEAD adds - -You are looking for ONE thing: content present in ${MAIN} and ABSENT from HEAD, where the absence is -a REVERSION rather than a deliberate removal. Signals that it is a reversion: - - main's version is strictly larger and HEAD's matches an older shape - - a locked list, ratchet, counter, registry or wiring entry that main added and HEAD does not have - - a CI step, npm script, or test-suite registration that exists in main and not in HEAD - - a comment in main referencing a commit or PR that HEAD's version predates - -NOT a reversion, and must NOT be reported: - - content HEAD deliberately removes as its stated deliverable (this branch removes employees DML - from backend/app/src/hr.rs on purpose -- that is the whole point of the change) - - main's prose being merely reworded - - generated files whose content is derived (they are regenerated separately) - -For every file you judge STALE you must quote the exact missing lines. A file you cannot decide is -UNCERTAIN, not stale -- a false 'stale' costs a wrong revert, which is worse than another CI round. -` - -const SCHEMA = { - type: 'object', - required: ['results'], - properties: { - results: { - type: 'array', - items: { - type: 'object', - required: ['file', 'verdict', 'evidence'], - properties: { - file: { type: 'string' }, - verdict: { type: 'string', enum: ['STALE', 'CLEAN', 'UNCERTAIN'] }, - evidence: { type: 'string', description: 'the exact lines main has and HEAD lacks, or why it is clean' }, - missingFromHead: { type: 'string', description: 'verbatim content to graft back, if STALE' }, - wouldBreak: { type: 'string', description: 'which gate or behaviour breaks if this stays reverted' }, - }, - }, - }, - }, -} - -phase('Audit') -const audited = await parallel(chunk(FILES, BATCH).map((batch, i) => () => - agent( - `Audit ${batch.length} file(s) for silently-reverted content. -${RULES} - -FILES: -${batch.map((f) => ` ${f}`).join('\n')} - -Return one result per file, in order. Do not skip any.`, - // Cheap tier: every STALE claim from this pass is adversarially re-checked below, and a - // CLEAN verdict that is wrong shows up as the next CI failure rather than as a bad merge. - { schema: SCHEMA, label: `audit:${i}`, phase: 'Audit', model: 'sonnet' }, - ))) - -const all = (audited || []).filter(Boolean).flatMap((r) => r.results || []) -const dead = chunk(FILES, BATCH).length - (audited || []).filter(Boolean).length -if (dead) log(`!! ${dead} audit batch(es) died — those files are UNAUDITED, not clean`) - -// Batch objects returning is not coverage. A live batch that omits or duplicates a requested -// path used to leave `dead === 0` and report "full coverage" while classifying the omitted file -// as audited. Every requested path must appear exactly once before coverage is claimed. -const resultCounts = new Map() -for (const r of all) { - if (!r || typeof r.file !== 'string') continue - resultCounts.set(r.file, (resultCounts.get(r.file) || 0) + 1) -} -const missingAuditFiles = FILES.filter((f) => !resultCounts.has(f)) -const duplicatedAuditFiles = FILES.filter((f) => (resultCounts.get(f) || 0) > 1) -const coverageIncomplete = dead > 0 || missingAuditFiles.length > 0 || duplicatedAuditFiles.length > 0 -if (missingAuditFiles.length) { - log(`!! ${missingAuditFiles.length} requested file(s) have no audit result — UNAUDITED, not clean`) - for (const f of missingAuditFiles) log(` missing: ${f}`) -} -if (duplicatedAuditFiles.length) { - log(`!! ${duplicatedAuditFiles.length} requested file(s) have duplicate audit results — coverage ambiguous`) - for (const f of duplicatedAuditFiles) log(` duplicated: ${f}`) -} - -const suspect = all.filter((r) => r.verdict === 'STALE') -log(`audit: ${all.length} file(s) read, ${suspect.length} claimed STALE, ${all.filter((r) => r.verdict === 'UNCERTAIN').length} uncertain`) - -if (!suspect.length) { - return { - headline: [ - `No stale takes found across ${all.length} files.`, - coverageIncomplete ? `coverage INCOMPLETE — deadBatches=${dead}, missing=${missingAuditFiles.length}, duplicated=${duplicatedAuditFiles.length}` : 'full coverage', - ], - stale: [], - uncertain: all.filter((r) => r.verdict === 'UNCERTAIN'), - missingAuditFiles, - duplicatedAuditFiles, - } -} - -// Only the accusations get a second opinion; confirming a CLEAN verdict costs more than it is worth. -phase('Confirm') -const confirmed = await parallel(suspect.map((s) => () => - agent( - `Try to REFUTE this claim. Default to refuted=true when uncertain. - -CLAIM: ${MAIN} contains content that HEAD dropped by reversion, in ${s.file}. -EVIDENCE OFFERED: ${s.evidence} - -${RULES} - -Refute it if: the content is absent from HEAD deliberately (it is the branch's stated deliverable), -or main's version is not actually newer, or the lines quoted do not exist as claimed. Run the diffs -yourself rather than trusting the quote. - -Do NOT be handed a graft candidate from the first pass — a payload nobody re-read is a claim, not -evidence. If you uphold STALE (refuted=false), you MUST return missingFromHead as the exact graft -payload YOU reconstruct after re-reading the diffs. An upheld STALE without that payload is -unresolved, not confirmed. A mismatched or missing confirmation payload must not be substituted -from the first agent's text.`, - { - schema: { - type: 'object', - required: ['file', 'refuted', 'reasoning', 'missingFromHead'], - properties: { - file: { type: 'string' }, - refuted: { type: 'boolean' }, - reasoning: { type: 'string' }, - missingFromHead: { - type: 'string', - description: 'When refuted=false: the exact content to graft, attested by THIS pass. When refuted=true: empty string.', - }, - }, - }, - label: `confirm:${s.file.split('/').pop()}`, phase: 'Confirm', - }, - // `v && v.refuted` yields NULL for a dead agent, and null is neither `=== false` nor truthy -- so - // the suspicion fell out of BOTH lists below and vanished without trace while the headline still - // said "full coverage". `typeof === 'boolean'` is used rather than a null check because the schema - // is a request to the model, not an enforcement: an agent that returns an object without `refuted` - // must land in the same unresolved bucket as one that never returned at all. - ).then((v) => ({ - ...s, - refuted: v ? v.refuted : null, - refutation: v ? v.reasoning : null, - confirmedMissing: v && typeof v.missingFromHead === 'string' ? v.missingFromHead : null, - })))) - -const answered = (confirmed || []).filter((c) => c && typeof c.refuted === 'boolean') -// Upholding STALE without attesting the graft payload must not publish the first-pass quote. -const real = answered.filter((c) => - c.refuted === false - && typeof c.confirmedMissing === 'string' - && c.confirmedMissing.trim() !== '') -// A suspicion whose REFUTATION never ran is still a suspicion. This file's own rule two paragraphs -// up says a false 'stale' costs a wrong revert while a missed one costs another CI round -- but the -// missed one here is the worse half: the reversion this workflow exists to catch is a file whose -// un-wiring means the check that would have caught it does not run. -const unconfirmed = suspect.filter((s) => { - const a = answered.find((c) => c.file === s.file) - if (!a) return true - if (a.refuted === false && !(typeof a.confirmedMissing === 'string' && a.confirmedMissing.trim())) return true - return false -}) -if (unconfirmed.length) { - log(`!! ${unconfirmed.length} confirmation(s) never returned — those files are UNRESOLVED, not clean`) - for (const u of unconfirmed) log(` ${u.file}`) -} - -return { - headline: [ - `${all.length} files audited, ${suspect.length} suspected, ${real.length} CONFIRMED stale after adversarial re-check`, - unconfirmed.length ? `${unconfirmed.length} suspected file(s) UNRESOLVED — their confirmation never returned` : null, - coverageIncomplete || unconfirmed.length - ? `coverage INCOMPLETE — deadBatches=${dead}, missing=${missingAuditFiles.length}, duplicated=${duplicatedAuditFiles.length}, unresolved=${unconfirmed.length}` - : 'full coverage', - ].filter(Boolean), - // Publish ONLY the confirmer-attested graft. Never r.missingFromHead from the audit spread — - // that is an unconfirmed claim even when the STALE verdict itself was upheld. - stale: real.map((r) => ({ file: r.file, missingFromHead: r.confirmedMissing, wouldBreak: r.wouldBreak, evidence: r.evidence })), - // Unresolved accusations keep the first-pass claim under a non-graft key so an operator sees - // what was alleged without a ready-to-apply missingFromHead payload. - unconfirmed: unconfirmed.map((u) => ({ - file: u.file, - evidence: u.evidence, - claimedMissingFromHead: u.missingFromHead, - wouldBreak: u.wouldBreak, - })), - refuted: answered.filter((c) => c.refuted).map((c) => ({ file: c.file, why: c.refutation })), - uncertain: all.filter((r) => r.verdict === 'UNCERTAIN').map((r) => ({ file: r.file, evidence: r.evidence })), - missingAuditFiles, - duplicatedAuditFiles, -} diff --git a/.codex/config.toml b/.codex/config.toml deleted file mode 100644 index 146af7eb8..000000000 --- a/.codex/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[features] -hooks = true diff --git a/.codex/hooks.json b/.codex/hooks.json deleted file mode 100644 index 13c72299e..000000000 --- a/.codex/hooks.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "hooks": { - "PostCompact": [ - { - "hooks": [ - { - "command": "bd codex-hook PostCompact", - "statusMessage": "Scheduling Beads context refresh", - "type": "command" - } - ], - "matcher": "manual|auto" - } - ], - "PreCompact": [ - { - "hooks": [ - { - "command": "bd codex-hook PreCompact", - "statusMessage": "Checking Beads context", - "type": "command" - } - ], - "matcher": "manual|auto" - } - ], - "SessionStart": [ - { - "hooks": [ - { - "command": "bd codex-hook SessionStart", - "statusMessage": "Loading Beads context", - "type": "command" - } - ], - "matcher": "startup|resume|clear" - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "command": "bd codex-hook UserPromptSubmit", - "statusMessage": "Refreshing Beads context", - "type": "command" - } - ] - } - ] - } -} diff --git a/.cursor/PROCESS-SYNTHESIS.md b/.cursor/PROCESS-SYNTHESIS.md deleted file mode 100644 index 040ca8c1d..000000000 --- a/.cursor/PROCESS-SYNTHESIS.md +++ /dev/null @@ -1,79 +0,0 @@ -# Console / Oyatie / Grok → Cursor process synthesis - -Date: 2026-08-09 -Sources: Console study, Oyatie study, mm-* study, Cursor hooks map, existing plan `cursor_agentic_parity_8fa3c09c` (chat evidence; not in-repo paths). - -## Diagnosis - -Multi-round PR fix loops in this program were **process failures**, usually one of: - -1. **Incomplete inventory** — fixed the last critic's finding, not all already-visible threads (`process.known-blockers-skipped`). -2. **Wrong convergence** — treated unproven bot opinions as merge bars after every push (`process.bot-thread-treadmill`). -3. **Wrong transport** — `mm-role` CLI critics (auth workaround) instead of native Task (`transport.mm-role-default`). -4. **Missing restack / forge preflight** — tip PRs conflicted or `gh` auth died mid-merge. - -It is less often "the critic failed to catch everything at once." Catching A while B was already on the PR, then opening a round for B, then a round for new unproven C, **is** the bug. - -See `.cursor/failure-classes-2026-08-10.md` for the measured table and controls. -## What to keep (portable process) - -| Pattern | Origin | Cursor artifact | -|---------|--------|-----------------| -| Proof-gated convergence (`major` blocks only if proven) | lane-fanout `isBlocking` | rules + critic agent + receipt validator | -| Standing lenses non-overridable | lane-fanout `STANDING_LENSES` | `.cursor/rules/console-standing-lenses.mdc` | -| Schema-required fields | BUILD_SCHEMA | `scripts/cursor/validate-lane-receipt.mjs` | -| Third spelling → mechanism | BASE_LOCK | `console-base-lock.mdc` | -| One writer / path-disjoint | oyatie parallelism + lane-fanout | rules + git-lock hook | -| Trial before scale | oyatie `deliver.js` / console-complete | base-lock + sessionStart | -| Failure class → edit process | oyatie/console `process-upgrade` | edit rules/hooks, not chat memory | -| Soft-red silence forbidden | work-manager / lane-board | board + push admission | -| Tip-serial mutex | work-graph | planner discipline | -| Role split (plan ≠ implement ≠ critic) | mm model-routing | `.cursor/agents/*` + orchestrator rule | - -## What to drop (transport accidents) - -- `.grok/bin/mm-role` / `claude -p` / `codex exec` as default critic path → **hook-denied** unless `CURSOR_ALLOW_MM_ROLE=1` -- Rhai workflow nesting as Cursor runtime -- `MM_ROLE_OK` harvest lines -- Credential sync for shelling out of Grok - -## Installed Cursor ratchet (this change) - -``` -.cursor/ - hooks.json - hooks/ # hard gates - rules/ # alwaysApply BASE_LOCK + standing lenses - agents/ # lane-implementer, lane-critic - receipts/ # schema-valid JSON - PROCESS-SYNTHESIS.md -scripts/cursor/validate-lane-receipt.mjs -``` - -Hard vs soft: -- **Hard:** hooks deny destructive git, mm-role, `--workflow-only`; push/PR admission; stop follow-up without valid receipt -- **Soft:** alwaysApply rules + agent prompts -- **Medium:** receipt JSON must validate before "done" - -## Operator notes - -- Escape hatches: `CURSOR_ALLOW_MM_ROLE=1`, `CURSOR_ALLOW_GIT_DANGEROUS=1`, `SKIP_LOCAL_ADMISSION=1` -- Land process files on a **chore/cursor-swarm-ratchet** PR from clean `main` once product trains quiesce — do not squash process into P4 product PRs -- Extend existing `cursor_agentic_parity` plan: add BASE_LOCK content (done here), keep shared `tools/hooks/` migration as follow-up - -## Orchestrator observations (2026-08-10 wave-1) - -Measured while running Beads-to-Zero; controls landed in agents/rules/scripts the same day. - -1. **Local ≠ landed.** Admission commits that are not on `origin/admission/*` do not exist for CI or reviewers. Run `scripts/cursor/check-admit-sync.sh` in the admit worktree after every rebuild; unpublished tip = soft-red, not babysit-idle. -2. **Lanes are local-first by design.** Individual `lane/*` branches usually are not pushed; evidence lives in worktrees + receipts. Custody/prune must not delete a worktree whose leaf is unadmitted. -3. **Signing is setup, not a nice-to-have.** Smoke `git commit -S` before product. Admission re-sign is a safety net, not the plan (`process.lane-unsigned-product`). -4. **Fmt can lie about gates.** If CI fails fmt first, gate tests did not run — require local gate pins on the tip you push (`process.ci-fmt-masks-gates`). -5. **Class findings need sweep beads.** Prototype-chain / trait-default / unsigned release candidates are classes; filing only the instance recreates the treadmill (`i91`, `h3e`, `9ry`). -6. **Hook fix ⇒ doc fix same turn.** `process.doc-vs-hook-drift` burned a round; BASE_LOCK/agents must move with the hook. -7. **Receipts ride the lane.** Hub copies go stale within one fix round (`process.receipt-location-drift`). -8. **Train scope freeze is real.** Do not fold train-2 leaves (ann/soe/mbl/…) into an open wave-1 PR mid-convergence — new admit train after merge. -9. **Release-please vs authority train.** Bot candidates cannot carry trusted SSH signatures (`process.release-candidate-unsigned` / bead `9ry`) — pick a mechanism before the next version bump. -10. **Babysit-only is banned.** When waiting on CI/admit push, keep ≥1 productive lane/critic/audit in flight. -11. **Worktrees under the hub.** Cursor External-File Protection treats sibling checkouts (`../console-lane-*`) as outside the workspace when the hub folder is open → constant allow-edit prompts. Provision only via `scripts/cursor/provision-lane-worktree.sh` / `/.worktrees/` (`process.worktree-outside-workspace`). Do not rely on multi-root `.code-workspace`. -12. **Do not pkill git across worktrees.** Measured agent transcripts used `pkill -f 'git -C …/console'`, `killall -9 git`, and `rm index.lock` while other lanes held the hub lock (`process.git-pkill-lock-race`). Ops: wait/timeout; stale lock only via `scripts/cursor/safe-stale-git-lock.sh`; never broad-kill git to unblock one agent. diff --git a/.cursor/agents/lane-critic.md b/.cursor/agents/lane-critic.md deleted file mode 100644 index 9dc935723..000000000 --- a/.cursor/agents/lane-critic.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: lane-critic -description: Console lane/PR critic — standing lenses, provenByExecution convergence + tie-break, anti-bot-treadmill. Use before merge or after implementer claims done. ---- - -You are a Console **lane critic** (STANDING_LENSES + oyatie dual-critic semantics). - -## Transport -- Cursor Task only. No mm-role. -- Prefer a different model family than the implementer when the parent can choose. - -## Standing lenses (all required) -1. **ORACLE INTEGRITY** -2. **PERIPHERAL DRIFT** -3. **ENFORCEMENT PLACEMENT** -4. **FALSE GREEN** — empty commandsRun, `--workflow-only`, Buck `include_str!` without RESOURCE_CONFIG, CI that never builds the target, unverified edges kept - -## Convergence (anti-treadmill) -- `blocker` → BLOCK. -- `major` blocks **only if** `provenByExecution: true`. -- **Tie-break:** provenByExecution wins over a conflicting APPROVE. -- If the implementer already did a full inventory fix commit: do **not** BLOCK solely on new unproven bot/Codex opinions — file as minor / ownerLease deferral. -- Same class appearing again → demand mechanism replacement, not another patch. - -## Fix observation packet (when reviewing a fix — no observation = no fix) -Demand all three; fixer never self-certifies: -1. **Visibility** — before (failing symptom: CI URL / test / thread + tip SHA), diff (explicit paths + blast radius: what was NOT touched), after (commands + output on the **new** tip, not narration). -2. **No-regression** — touched-surface greens still pass on the fix tip; if the bug class had no pin, a regression test that would have failed before is REQUIRED; deleted/weakened asserts or "documented as intentional" to silence review = BLOCK. -3. **Genuine addition** — causally addresses the stated failure (not rename/comment/allowlist-broaden); net stricter fail-closed. Prior tip's green is not evidence for this tip. - -## Output -Write the critic receipt **in the lane worktree** (same rule as implementers — `process.receipt-location-drift`): -`/.cursor/receipts/-critic.json` -Validate via hub absolute path until ratchet lands: -`node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs --schema critic ` - -Review **diff vs declared tip SHA** only — ignore implementer narrative. - -## Class elevation (do not leave orphans) -When you prove a defect that is clearly a **class** (prototype-chain lookups, trait-default bypass, unsigned bot candidates, unpinned CI wiring), say so in `followUps` and name the sweep bead the parent should open (or confirm it already exists). One-file patches without the class bead recreate the treadmill. - -## Peripherals lens — OpenAPI / published contracts -If the leaf changes an enum, status set, or wire shape: verify **every** OpenAPI occurrence (request, response, **list filters**, descriptions/"all N" copy). Response-schema-only updates with stale list filters = proven peripheral drift → BLOCK (`process.openapi-enum-peripheral-drift`). - -## Push ban (train integrity — NEVER push to the PR branch) - -You review; you do NOT push. **Never push commits to a PR branch** — not fixes, not evidence edits, not even re-signed trains. You MAY still write your own critic receipt into the lane worktree per your output contract (`.cursor/receipts/-critic.json`) — the ban covers git mutations and branch modification only. The signed C+T train belongs to the conductor/owner alone: any push from a reviewer (signed or not) replaces the pinned-authority head, fails `authenticate-console-authority`, and restarts the entire required-check suite. File review threads instead; the conductor folds accepted findings into the next re-signed train. This rule is itself reviewed like any lane change. diff --git a/.cursor/agents/lane-implementer.md b/.cursor/agents/lane-implementer.md deleted file mode 100644 index 43ff3b765..000000000 --- a/.cursor/agents/lane-implementer.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: lane-implementer -description: Console lane implementer — owned-root only, RED baseline first, inventory-all-known-blockers then ONE commit, schema-valid receipt. Use for P4/P5/backend lane execution. ---- - -You are a Console **lane implementer** (Cursor-native port of oyatie/console EXECUTOR + lane-fanout BUILD). - -## Contract -- Read `.cursor/rules/console-base-lock.mdc` and `.cursor/failure-classes-2026-08-10.md`. -- Edit **only** paths in the brief's `owned` allowlist. Anything else → stop and report in `followUps` (ownerLease). -- **One writer per worktree.** Unexpected porcelain → stop. -- **Inventory before first push** (prevents `process.known-blockers-skipped` / bot treadmills): - 1. Unresolved GitHub review threads on the PR - 2. Prior critic receipts / Sol-Opus findings - 3. Standing-lens false-green checks (empty commands, Buck externals, `--workflow-only`) - Fix every `blocker` and `major+proven` (and obvious P1s) in **one** commit. Do not push a partial fix and wait for the next bot round. -- After that one commit: unproven new bot opinions → reply/defer, **not** another impl round. -- Third spelling of the same class → replace the mechanism. - -## Method -1. **Signing smoke before product** — after worktree provision, prove `git commit -S` works (`%G? = G`). If signing fails: STOP → CAPTURE → ELEVATE (`process.lane-unsigned-product`). Prefer folding the smoke into the receipt/product commit rather than leaving a permanent "chore smoke" leaf when the brief wants one product commit. -2. RED baseline first (failing test or hostile probe). -3. Minimal mechanical fix for the **whole inventory**. -4. Owned peripherals in the same commit — if you change an enum/status/wire shape, inventory **all** OpenAPI sites (filters + response + prose counts) before committing. -5. Scoped verification; record exact commands (no empty strings). For gate/CI leaves: run `cargo fmt --all -- --check` (or scoped fmt) **and** the gate tests yourself — CI's Backend job fails at fmt first and can mask gate regressions (`process.ci-fmt-masks-gates`). -6. Write the receipt **inside your lane worktree**: `/.cursor/receipts/.json` (required schema fields). Hub-only receipts = `process.receipt-location-drift`. -7. Validate via hub absolute path until ratchet lands: `node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /.cursor/receipts/.json` — nonzero = not done. -8. If landing/merging: `bash scripts/cursor/preflight-forge.sh` first. -9. If the brief requires self-provisioning: create the worktree **under the hub** only — `bash scripts/cursor/provision-lane-worktree.sh ` or `git [-C ] worktree add /.worktrees/ -b lane/ origin/main` (hook-allowlisted). **Never** `../console-lane-*` siblings (outside Cursor workspace → External-File Protection / allow-edit prompts). Anything else → STOP → CAPTURE → ELEVATE. - -## Role separation (anti-mega-worker) -- You own **one lane**. You are not the wave: do not dispatch sibling lanes, do not open the train PR, do not admit other leaves — that is the orchestrator/coordinator's job. -- **No implement+watch loop:** push → write receipt → **exit** (or take a different disjoint lane if re-briefed). CI outcome belongs to babysit/orchestrator; red CI comes back as a fixer dispatch, not your private retry storm. -- If you were dispatched as a fan-out coordinator with N≥2 ready disjoint lanes, you MUST spawn ≥N parallel implementer Tasks (or PARK on record) — serializing them yourself is the failure mode. - -## Friction protocol (do not self-fix process) -On process/hub/envelope/CI-policy friction (path outside owned roots, hub collision, hook block, policy gap): **STOP → CAPTURE** (worktree, tip SHA, colliding paths, exact error) **→ ELEVATE** in `followUps`. No workaround commits, no scope expansion, no bypass. A different agent fixes it on the owning lane. - -## Forbidden -- `mm-role` / `claude -p` / `codex exec` unless brief sets CURSOR_ALLOW_MM_ROLE -- Any `*-fast` model slug for sub-dispatch (never pay extra inference for speed) -- `--workflow-only`, bare `cargo test`, oracle weakening, `gh pr update-branch`, bare `--force` -- Second fix round to invent receipt fields or chase unproven Codex P2s -- **`pkill`/`killall`/`pgrep|kill` of git** (or `rm` of `index.lock` / `gc.pid`) to unstick a hung shell — that races sibling writers on the hub `.git` (`process.git-pkill-lock-race`). Wait, timeout, or `bash scripts/cursor/safe-stale-git-lock.sh `; elevate. Do not set `CURSOR_ALLOW_GIT_DANGEROUS` to bypass this. diff --git a/.cursor/failure-classes-2026-08-10.md b/.cursor/failure-classes-2026-08-10.md deleted file mode 100644 index 51ec61c16..000000000 --- a/.cursor/failure-classes-2026-08-10.md +++ /dev/null @@ -1,36 +0,0 @@ -# Cursor session failure classes (2026-08-09/10) - -Measured on the #618/#619/#620 land attempt. Promote into hooks/rules after ≥1 repeat (oyatie: after 2; we promote now because each class already burned a full round). - -| ID | What happened | Root cause | Control (now) | -|----|---------------|------------|---------------| -| `transport.mm-role-default` | First PR train used `mm-role` → Sol BLOCK vs Opus APPROVE, zero merges | Treated Grok CLI transport as process | Hook deny + BASE_LOCK; native Task critics only | -| `process.known-blockers-skipped` | Push, then fix whatever the latest critic said | No mandatory inventory of threads+prior findings before first push | BASE_LOCK + implementer: inventory → one commit | -| `process.bot-thread-treadmill` | Every push attracted new Codex P1/P2; each became a new fix round | Unproven bot opinions treated as merge bars | Standing lenses: after one fix pass, only `blocker` or `major+provenByExecution` reopen the lane | -| `process.tip-land-without-restack` | #620 merge left #619 CONFLICTING; agent stopped (force-with-lease gated) | Serial tip PRs without restack in the brief | Hook allows `git rebase origin/main` + `--force-with-lease`; briefs must name restack | -| `process.hook-failclosed-unready` | `failClosed` + non-executable hooks locked all shells (exit 126) | Shipped hard gate before smoke | Hooks invoke via `bash …`; no failClosed until probe-ratchet.py green | -| `process.stop-nag-foreign-dirt` | Stop hook demanded receipt because #618 migrations were dirty | Subject ≠ owned root | stop-receipt-gate only watches `.cursor/**` + `scripts/cursor/**` | -| `ops.gh-auth-stale` | Merge agents dispatched while `gh` token invalid / rate-limited | No preflight on forge credentials | `scripts/cursor/preflight-forge.sh` before any `gh pr merge` / thread resolve fan-out | -| `process.critic-tiebreak-missing` | Sol proven BLOCK vs Opus APPROVE → paralysis | No tie-break rule | provenByExecution wins; APPROVE cannot stand over unfixed proven majors | -| `process.subagent-quota-death` | Rebase agent died mid-flight on usage limit | No parent fallback | Parent owns merge; on subagent error, parent continues or re-dispatches once — do not wait idle | -| `process.lane-setup-hook-deadlock` | mbl lane: brief mandates `git worktree add`, git-lock hook denies it; env escape unreachable from subagents; enforcement also inconsistent (`-C` form slipped past, bare form denied) | Hook denylist written for sprawl never got a lane-provisioning allowlist; regex missed `git -C … worktree` | Hook allowlists `git [-C ] worktree add /.worktrees/ -b lane/*\|admission/* origin/main` (+ `scripts/cursor/provision-lane-worktree.sh`); sibling `../console-lane-*` denied | -| `process.worktree-outside-workspace` | Agents editing `../console-lane-*` / admit siblings constantly hit Cursor "allow edit" / External-File Protection while hub `/…/console` is open | Git worktrees created as sibling directories outside the opened workspace root | New lanes only under `/.worktrees/` (gitignored); BASE_LOCK + ritual + hook path constraint; open hub folder (multi-root `.code-workspace` unreliable) | -| `process.squash-title-not-conventional` | Release 0.3.4 changelog (#621) lists only #618; #619/#620 invisible because squash titles weren't conventional-commit format; #622 as opened ("Wave 1: …") would repeat this | PR titles composed for humans, not for release-please's conventional-commit parser | Train PR titles MUST be `type(scope): …` before squash-merge (put it on the coordinator's brief); #622 retitled `fix(p4): …` 2026-08-10 | -| `process.release-candidate-unsigned` | #621 preflight: `verify-console-authority-train.mjs` rejects release-please bot candidate (not SSH-signed by trusted key); every release PR structurally un-mergeable | Authority train gate assumes human/agent-signed candidates; bot commits can never carry the trusted SSH signature | Bead `9ry` (P1): pick ONE mechanism — narrow verifier rule for bot+docs-only, re-sign flow, or signed-candidate release strategy | -| `process.receipt-location-drift` | ann/cm3/soe receipts landed in hub `.cursor/receipts/` while critics look in the lane; lanes branched from main also cannot self-validate (validator untracked) | Briefs said "receipt" without naming the lane worktree; validator not on origin/main yet | Ritual + BASE_LOCK: receipt MUST be `/.cursor/receipts/.json`; validate via hub absolute path until `chore-cursor-ratchet` lands | -| `process.doc-vs-hook-drift` | BASE_LOCK still forbade all `worktree add` after the hook allowlisted lane provisioning; also still named banned `composer-2.5-fast` | Hook fixed under friction protocol; rules not updated in the same turn | Same turn as any hook change: update BASE_LOCK / agents / ritual; cost policy = never `*-fast` | -| `process.admit-tip-unpublished` | Admit worktree rebuilt to `f53067b95` while PR #622 remote tip stayed `9c7ea19f6` — green local, dead remote | Coordinator treated "commit locally" as landed; no publish check | `scripts/cursor/check-admit-sync.sh` on admission/*; orchestrator must push or re-dispatch within one turn of a clean local tip | -| `process.lane-unsigned-product` | q06 lane tip unsigned (gpg unavailable); critic BLOCKed; disposed only because admission re-signed | Signing assumed ambient | Implementer: commit -S smoke before product; elevate on fail — do not write product unsigned | -| `process.ci-fmt-masks-gates` | #622 Backend job failed at `cargo fmt` first → clippy/tests/writer-ownership gate never ran at tip; fmt-only green would false-clear lexer holes | Job short-circuits on first step | Admit/local: fmt + gate pins before push; QA must note when Required green ≠ gate executed | -| `process.openapi-enum-peripheral-drift` | cm3: response schemas gained `time_change_consult` but list filter enums + "all four" copy stayed stale → critic BLOCK | Peripherals updated incompletely | Critic peripherals lens + implementer OpenAPI inventory when enums change | -| `process.git-pkill-lock-race` | Subagents "unstick" hung `git`/`gh` with `pkill -f 'git -C …/console'`, `killall -9 git`, `pgrep … \| kill -9`, or `rm …/index.lock` while sibling lanes still hold the hub lock → stampede, force-with-lease blocks, `bd` reads hang | Shared `.git` + no ownership check on kill/unlock; `CURSOR_ALLOW_GIT_DANGEROUS` was used as a bypass | Hook denies git-targeted pkill/killall/pgrep-kill and unlock-by-rm **before** `CURSOR_ALLOW_GIT_DANGEROUS`; wait/timeout; stale locks only via `scripts/cursor/safe-stale-git-lock.sh` (PID dead + age); ops: never pkill git across worktrees | - -## Anti-pattern that looked like "not catching issues" - -Catching issues across **multiple rounds** often meant: -1. Round 1 critic found class A (good), -2. Implementer fixed only A, not open review threads B/C already visible, -3. Round 2 bot filed D (new opinion) + restated B, -4. Process treated D as mandatory → treadmill. - -So the defect is usually **incomplete inventory + wrong convergence rule**, not "the critic missed something." diff --git a/.cursor/hooks.json b/.cursor/hooks.json deleted file mode 100644 index 011384fd8..000000000 --- a/.cursor/hooks.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "version": 1, - "hooks": { - "sessionStart": [ - { - "command": "bash .cursor/hooks/session-start.sh" - } - ], - "beforeShellExecution": [ - { - "command": "bash .cursor/hooks/git-lock-enforcer.sh" - }, - { - "command": "bash .cursor/hooks/no-mm-role.sh", - "matcher": "mm-role|claude -p|codex exec" - }, - { - "command": "bash .cursor/hooks/cargo-scope-enforcer.sh", - "matcher": "cargo\\s" - }, - { - "command": "bash .cursor/hooks/push-admission.sh", - "matcher": "git\\s+push|gh\\s+pr\\s+(create|merge)" - }, - { - "command": "bash .cursor/hooks/forge-destructive-guard.sh", - "matcher": "gh\\s+(api|repo|release|secret|auth)|git\\s+push" - } - ], - "stop": [ - { - "command": "bash .cursor/hooks/stop-receipt-gate.sh", - "loop_limit": 2 - } - ] - } -} diff --git a/.cursor/hooks/cargo-scope-enforcer.sh b/.cursor/hooks/cargo-scope-enforcer.sh deleted file mode 100755 index 8f79cd73b..000000000 --- a/.cursor/hooks/cargo-scope-enforcer.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -# Scope Cargo; ban --workflow-only false greens. -set -euo pipefail -INPUT="$(cat || true)" -CMD="$(python3 -c 'import json,sys -try: - d=json.load(sys.stdin) -except Exception: - d={} -print(d.get("command") or d.get("toolInput",{}).get("command") or d.get("tool_input",{}).get("command") or "") -' <<<"$INPUT" 2>/dev/null || true)" - -# Only care about cargo test / build invocations -if ! printf '%s' "$CMD" | grep -Eq '(^|[[:space:];|&])cargo[[:space:]]+(test|build|check|nextest)'; then - echo '{"permission":"allow"}' - exit 0 -fi - -if printf '%s' "$CMD" | grep -Eq -- '--workflow-only'; then - python3 -c 'import json; print(json.dumps({ - "permission":"deny", - "user_message":"BASE_LOCK: --workflow-only selects zero dark targets and exits 0 (false green). Use --only .", - "agent_message":"Denied --workflow-only. Use tools/ci/cargo_needs_postgres.sh --only --num-threads=1." - }))' - exit 0 -fi - -# Bare cargo test without -p / --manifest-path / --package is a common false-scope -if printf '%s' "$CMD" | grep -Eq '(^|[[:space:];|&])cargo[[:space:]]+test([[:space:]]|$)' \ - && ! printf '%s' "$CMD" | grep -Eq -- '(-p[[:space:]]|--package[[:space:]]|--manifest-path[[:space:]]|cargo_needs_postgres)'; then - python3 -c 'import json; print(json.dumps({ - "permission":"ask", - "user_message":"Bare cargo test detected. Prefer scoped: cargo test --locked --manifest-path backend/Cargo.toml -p ", - "agent_message":"Unscoped cargo test. Scope with -p and --manifest-path backend/Cargo.toml." - }))' - exit 0 -fi - -echo '{"permission":"allow"}' -exit 0 diff --git a/.cursor/hooks/forge-destructive-guard.sh b/.cursor/hooks/forge-destructive-guard.sh deleted file mode 100755 index 00a17ec94..000000000 --- a/.cursor/hooks/forge-destructive-guard.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -# Deny destructive forge (gh) surfaces and the git-push destructives that -# git-lock-enforcer does not already cover (-f, deletions, direct push to main). -# Everything else allows; push-admission.sh separately gates push/PR-create/merge. -set -euo pipefail -INPUT="$(cat || true)" -CMD="$(python3 -c 'import json,sys -try: - d=json.load(sys.stdin) -except Exception: - d={} -print(d.get("command") or d.get("toolInput",{}).get("command") or d.get("tool_input",{}).get("command") or "") -' <<<"$INPUT" 2>/dev/null || true)" - -deny() { - python3 -c 'import json,sys; print(json.dumps({"permission":"deny","user_message":sys.argv[1],"agent_message":sys.argv[1]}))' "$1" - exit 0 -} - -allow() { - echo '{"permission":"allow"}' - exit 0 -} - -# Escape hatch, same convention as git-lock-enforcer: session env var, or an -# explicit CURSOR_ALLOW_GIT_DANGEROUS=1 prefix on the command itself. -if [[ "${CURSOR_ALLOW_GIT_DANGEROUS:-}" == "1" ]] || printf '%s' "$CMD" | grep -q 'CURSOR_ALLOW_GIT_DANGEROUS=1'; then - allow -fi - -# --- gh destructive surfaces (no existing hook covers these) --- -if printf '%s' "$CMD" | grep -Eq 'gh[[:space:]]+repo[[:space:]]+(delete|rename)'; then - deny "forge-guard: gh repo delete/rename is irreversible and reserved for humans. If explicitly approved, prefix CURSOR_ALLOW_GIT_DANGEROUS=1." -fi -if printf '%s' "$CMD" | grep -Eq 'gh[[:space:]]+secret([[:space:]]|$)'; then - deny "forge-guard: gh secret touches repository credentials; agents must not read or write secrets. If explicitly approved, prefix CURSOR_ALLOW_GIT_DANGEROUS=1." -fi -if printf '%s' "$CMD" | grep -Eq 'gh[[:space:]]+auth[[:space:]]+(logout|refresh)'; then - deny "forge-guard: gh auth logout/refresh would invalidate the shared gh session other lanes depend on (ops.gh-auth-stale). Use gh auth status to inspect." -fi -if printf '%s' "$CMD" | grep -Eq 'gh[[:space:]]+release[[:space:]]+delete'; then - deny "forge-guard: gh release delete is destructive. If explicitly approved, prefix CURSOR_ALLOW_GIT_DANGEROUS=1." -fi -if printf '%s' "$CMD" | grep -Eq 'gh[[:space:]]+api' \ - && printf '%s' "$CMD" | grep -Eiq -- '(-X|--method)[[:space:]]*=?[[:space:]]*DELETE'; then - deny "forge-guard: gh api with method DELETE is destructive. Reads/POST/PUT/PATCH are allowed; if deletion is explicitly approved, prefix CURSOR_ALLOW_GIT_DANGEROUS=1." -fi - -# --- git push destructives NOT covered by git-lock-enforcer --- -# (git-lock-enforcer already denies long-form 'git push --force' without lease) -if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push'; then - if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push[^;|&]*[[:space:]]-f([[:space:]]|$)' \ - && ! printf '%s' "$CMD" | grep -Eq -- '--force-with-lease|--force-if-includes'; then - deny "forge-guard: git push -f (plain force) forbidden. Use --force-with-lease, and only after git rebase origin/main (BASE_LOCK restack rule)." - fi - if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push[^;|&]*[[:space:]](--delete|-d|--mirror)([[:space:]]|$)'; then - deny "forge-guard: git push --delete/-d/--mirror (remote deletion / mirror overwrite) forbidden. Branch cleanup belongs to the integration owner; prefix CURSOR_ALLOW_GIT_DANGEROUS=1 if explicitly approved." - fi - if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push[^;|&]*[[:space:]][+]?:[^[:space:]]'; then - deny "forge-guard: ':' deletion refspec removes a remote ref. Branch cleanup belongs to the integration owner; prefix CURSOR_ALLOW_GIT_DANGEROUS=1 if explicitly approved." - fi - if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push[^;|&]*[[:space:]]([^[:space:]]*:)?(refs/heads/)?main([[:space:]]|$)'; then - deny "forge-guard: direct push targeting main is forbidden (protected branch). Push a feature branch and open a PR (gh pr create); merges go through gh pr merge --squash after preflight." - fi -fi - -allow diff --git a/.cursor/hooks/git-lock-enforcer.sh b/.cursor/hooks/git-lock-enforcer.sh deleted file mode 100755 index 7e29ca313..000000000 --- a/.cursor/hooks/git-lock-enforcer.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env bash -# Deny destructive / multi-writer git that caused Bun-rewrite and console collisions. -# Also deny cross-agent pkill/killall of git and unlock-by-rm (process.git-pkill-lock-race). -set -euo pipefail -INPUT="$(cat || true)" -CMD="$(python3 -c 'import json,sys -try: - d=json.load(sys.stdin) -except Exception: - d={} -print(d.get("command") or d.get("toolInput",{}).get("command") or d.get("tool_input",{}).get("command") or "") -' <<<"$INPUT" 2>/dev/null || true)" - -deny() { - python3 -c 'import json,sys; print(json.dumps({"permission":"deny","user_message":sys.argv[1],"agent_message":sys.argv[1]}))' "$1" - exit 0 -} - -allow() { - echo '{"permission":"allow"}' - exit 0 -} - -# --------------------------------------------------------------------------- -# process.git-pkill-lock-race — apply BEFORE CURSOR_ALLOW_GIT_DANGEROUS. -# Agents were unsticking hung shells with `pkill -f git` / `killall git` / -# `rm …/index.lock`, racing sibling writers on the shared hub .git. -# Escape only: CURSOR_ALLOW_GIT_PKILL=1 (human operator), or the allowlisted -# safe-stale helper below. -# --------------------------------------------------------------------------- -_PKILL_MSG='BASE_LOCK: process.git-pkill-lock-race — do not pkill/killall/kill-by-pattern git (or git hooks) across worktrees. Wait/timeout/flock; escalate. Stale locks: bash scripts/cursor/safe-stale-git-lock.sh . Escape: CURSOR_ALLOW_GIT_PKILL=1 (operator only).' -_LOCK_RM_MSG='BASE_LOCK: process.git-pkill-lock-race — do not rm git *.lock / gc.pid. Use bash scripts/cursor/safe-stale-git-lock.sh (PID dead + age gate). Escape: CURSOR_ALLOW_GIT_PKILL=1 (operator only).' - -# Escape if env is set on the hook process OR assigned in the shell command -# (Cursor may not forward prefixed assignments into the hook environment). -if [[ "${CURSOR_ALLOW_GIT_PKILL:-}" != "1" ]] \ - && ! printf '%s' "$CMD" | grep -Eq '(^|[[:space:];|&])CURSOR_ALLOW_GIT_PKILL=1([[:space:];|&]|$)'; then - # Allow the safe helper itself when invoked as a command (not nested after pkill). - if printf '%s' "$CMD" | grep -Eq '(^|[[:space:];|&])(bash[[:space:]]+)?([^[:space:]]*/)?scripts/cursor/safe-stale-git-lock\.sh([[:space:]]|$)' \ - && ! printf '%s' "$CMD" | grep -Eq '(^|[[:space:];|&])(pkill|killall)([[:space:]]|$)'; then - : - else - # Collapse newlines so multi-line agent shells still match. - # Match pkill/killall/pgrep as COMMANDS (word boundaries), never as path substrings - # like lane-console-pkill-lock-ban/.../git-lock-enforcer.sh. - _FLAT="$(printf '%s' "$CMD" | tr '\n' ' ')" - _HAS_PKILL=0 - _HAS_KILLALL=0 - _HAS_PGREP=0 - _HAS_KILL_CMD=0 - printf '%s' "$_FLAT" | grep -Eq '(^|[[:space:];|&])pkill([[:space:]]|$)' && _HAS_PKILL=1 - printf '%s' "$_FLAT" | grep -Eq '(^|[[:space:];|&])killall([[:space:]]|$)' && _HAS_KILLALL=1 - printf '%s' "$_FLAT" | grep -Eq '(^|[[:space:];|&])pgrep([[:space:]]|$)' && _HAS_PGREP=1 - printf '%s' "$_FLAT" | grep -Eq '(^|[[:space:];|&])kill([[:space:]]|$)' && _HAS_KILL_CMD=1 - - _GIT_TARGET=0 - # Args / patterns that mean "aimed at git or shared console git state". - if printf '%s' "$_FLAT" | grep -Eqi \ - '(^|[[:space:]-/'\''\"])git([[:space:]'\''\"/]|$)|git-lock|hooks/git|Cellar/git|/opt/homebrew[^;&|]*bin/git|admission-[A-Za-z0-9_-]+|/\.worktrees/'; then - _GIT_TARGET=1 - fi - - if [[ "$_HAS_PKILL" -eq 1 || "$_HAS_KILLALL" -eq 1 ]]; then - if [[ "$_GIT_TARGET" -eq 1 ]]; then - deny "$_PKILL_MSG" - fi - # killall git / pkill git with bare name - if printf '%s' "$_FLAT" | grep -Eqi '(^|[[:space:];|&])(pkill|killall)[[:space:]]+(-[A-Za-z0-9]+[[:space:]]+)*[`'\''\"]?git[`'\''\"]?([[:space:]]|$)'; then - deny "$_PKILL_MSG" - fi - fi - - # pgrep … git piped/looped into kill — diagnostic pgrep alone is fine. - if [[ "$_HAS_PGREP" -eq 1 && "$_HAS_KILL_CMD" -eq 1 && "$_GIT_TARGET" -eq 1 ]]; then - deny "$_PKILL_MSG" - fi - # pgrep -x git is almost always a prelude to killing every git on the machine. - if printf '%s' "$_FLAT" | grep -Eq '(^|[[:space:];|&])pgrep[[:space:]]+(-[A-Za-z0-9]+[[:space:]]+)*-x[[:space:]]+git([[:space:]]|$)'; then - deny "$_PKILL_MSG" - fi - # lsof -t | kill - if printf '%s' "$_FLAT" | grep -Eqi 'lsof[[:space:]]+-t[^;&|]*git[^;&|]*(^|[[:space:];|&])kill([[:space:]]|$)'; then - deny "$_PKILL_MSG" - fi - - # Raw unlock-by-rm of git lock files (command-shaped rm). - if printf '%s' "$_FLAT" | grep -Eq \ - '(^|[[:space:];|&])rm[[:space:]]+[^;&|]*index\.lock|(^|[[:space:];|&])rm[[:space:]]+[^;&|]*HEAD\.lock|(^|[[:space:];|&])rm[[:space:]]+[^;&|]*gc\.pid|(^|[[:space:];|&])rm[[:space:]]+[^;&|]*MERGE_RR\.lock|(^|[[:space:];|&])rm[[:space:]]+[^;&|]*/\.git/[^;&|]*\.lock'; then - deny "$_LOCK_RM_MSG" - fi - fi -fi - -# Integration owner / explicit SKIP escapes (other destructive git) -if [[ "${CURSOR_ALLOW_GIT_DANGEROUS:-}" == "1" ]]; then - allow -fi - -# Patterns forbidden for lane workers (BASE_LOCK) -if printf '%s' "$CMD" | grep -Eq '(^|[[:space:]])git[[:space:]]+(stash|reset|clean)([[:space:]]|$)'; then - deny "BASE_LOCK: git stash/reset/clean forbidden in lane worktrees (fix forward)." -fi -if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+checkout[[:space:]]+(-b|[[:alnum:]_./-]+)'; then - # allow checkout -- - if ! printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+checkout[[:space:]]+--[[:space:]]'; then - deny "BASE_LOCK: git checkout forbidden; stay on assigned worktree branch." - fi -fi -# Lane workers must not rebase/merge. Integration restack onto origin/main is the -# approved path when main moves (never gh pr update-branch). Mid-restack -# continue/abort/skip must also pass or a conflict freezes the worktree. -if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+rebase[[:space:]]+origin/main([[:space:]]|$)'; then - : # allow integration restack -elif printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+rebase[[:space:]]+(--continue|--abort|--skip)([[:space:]]|$)'; then - : # allow finishing or abandoning an in-progress origin/main restack -elif printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+(rebase|merge)([[:space:]]|$)'; then - deny "BASE_LOCK: git rebase/merge forbidden for lane workers (integration owner serializes). Restack only: git rebase origin/main" -fi -# Force-with-lease only after an integration restack onto origin/main (PR refresh). -# Plain --force stays forbidden. -if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push[[:space:]]+.*--force([^-]|$)'; then - deny "BASE_LOCK: git push --force forbidden. Use --force-with-lease only after rebase onto origin/main." -fi -if printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+push[[:space:]]+.*--force-with-lease'; then - : # allow PR tip refresh after rebase origin/main -fi -# Lane/admission provisioning is the programme's approved parallelism mechanism: -# a NEW worktree under /.worktrees/ on a NEW lane/* or admission/* -# branch cut from origin/main. Sibling paths like ../console-lane-* are denied — -# they sit outside the Cursor workspace root and trigger External-File Protection -# ("allow edit") prompts on every agent write. -# Everything else (remove, arbitrary add, update-branch) stays denied — including -# the `git -C worktree` form the old pattern missed. -# Path must be `.worktrees/` or absolute/relative ending in `/.worktrees/`. -_WT_ADD_RE='git[[:space:]]+(-C[[:space:]]+[^[:space:]]+[[:space:]]+)?worktree[[:space:]]+add[[:space:]]+(\.worktrees/|[^[:space:]]*/\.worktrees/)[[:alnum:]_.-]+[[:space:]]+-b[[:space:]]+(lane|admission)/[[:alnum:]_./-]+[[:space:]]+origin/main([[:space:]]|$)' -if printf '%s' "$CMD" | grep -Eq "$_WT_ADD_RE"; then - : # allow in-repo .worktrees/ lane provisioning from origin/main -elif printf '%s' "$CMD" | grep -Eq 'git[[:space:]]+(-C[[:space:]]+[^[:space:]]+[[:space:]]+)?worktree[[:space:]]+(add|remove)|gh[[:space:]]+pr[[:space:]]+update-branch'; then - deny "BASE_LOCK: worktree add/remove outside in-repo provisioning (allowed: git [-C ] worktree add /.worktrees/ -b lane/|admission/ origin/main). Sibling ../console-lane-* paths are forbidden (Cursor External-File Protection). gh pr update-branch forbidden." -fi - -allow diff --git a/.cursor/hooks/no-mm-role.sh b/.cursor/hooks/no-mm-role.sh deleted file mode 100755 index a4dbbd4a7..000000000 --- a/.cursor/hooks/no-mm-role.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# mm-role / claude -p / codex exec are a Grok transport accident — deny by default in Cursor. -set -euo pipefail -INPUT="$(cat || true)" -CMD="$(python3 -c 'import json,sys -try: - d=json.load(sys.stdin) -except Exception: - d={} -print(d.get("command") or d.get("toolInput",{}).get("command") or d.get("tool_input",{}).get("command") or "") -' <<<"$INPUT" 2>/dev/null || true)" - -if [[ "${CURSOR_ALLOW_MM_ROLE:-}" == "1" ]]; then - echo '{"permission":"allow"}' - exit 0 -fi - -if printf '%s' "$CMD" | grep -Eq '(^|[/\s])mm-role([[:space:]]|$)|claude[[:space:]]+-p|codex[[:space:]]+exec'; then - python3 -c 'import json; print(json.dumps({ - "permission":"deny", - "user_message":"Use Cursor-native Task subagents (Grok 4.5 / Composer), not mm-role/claude -p/codex exec. Set CURSOR_ALLOW_MM_ROLE=1 only if you explicitly want CLI receipts.", - "agent_message":"Transport ban: mm-role is a Grok auth workaround. Prefer Task(subagent) with cursor-grok-4.5-high." - }))' - exit 0 -fi - -echo '{"permission":"allow"}' -exit 0 diff --git a/.cursor/hooks/push-admission.sh b/.cursor/hooks/push-admission.sh deleted file mode 100755 index 461bc7104..000000000 --- a/.cursor/hooks/push-admission.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -# Cursor adapter: local admit on push/PR create; forge preflight on gh pr merge. -set -euo pipefail -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -INPUT="$(cat || true)" - -CMD="$(python3 -c 'import json,sys -try: - d=json.load(sys.stdin) -except Exception: - d={} -print(d.get("command") or d.get("toolInput",{}).get("command") or d.get("tool_input",{}).get("command") or "") -' <<<"$INPUT" 2>/dev/null || true)" - -deny() { - python3 -c 'import json,sys; print(json.dumps({"permission":"deny","user_message":sys.argv[1],"agent_message":sys.argv[1]}))' "$1" - exit 0 -} - -# Merge / heavy forge ops require live gh auth (ops.gh-auth-stale) -if printf '%s' "$CMD" | grep -Eq 'gh[[:space:]]+pr[[:space:]]+merge'; then - if ! bash "$ROOT/scripts/cursor/preflight-forge.sh" >/tmp/console-preflight-forge.out 2>/tmp/console-preflight-forge.err; then - deny "ops.gh-auth-stale: $(head -c 800 /tmp/console-preflight-forge.err)" - fi -fi - -OUT="$(printf '%s' "$INPUT" | "$ROOT/scripts/hooks/pre-tool-push-admission.sh" || true)" - -if printf '%s' "$OUT" | grep -q '"decision"[[:space:]]*:[[:space:]]*"deny"'; then - REASON="$(printf '%s' "$OUT" | python3 -c 'import json,sys -try: - d=json.load(sys.stdin); print(d.get("reason","admission denied")) -except Exception: - print("admission denied") -' 2>/dev/null || echo "admission denied")" - deny "$REASON" -fi - -echo '{"permission":"allow"}' -exit 0 diff --git a/.cursor/hooks/session-start.sh b/.cursor/hooks/session-start.sh deleted file mode 100755 index 56d95b3a7..000000000 --- a/.cursor/hooks/session-start.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# Cursor sessionStart — inject process posture (portable from oyatie/console harnesses). -set -euo pipefail -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -cd "$ROOT" - -# Best-effort beads prime (fail open — not all sessions have bd). -if command -v bd >/dev/null 2>&1; then - bd prime --hook-json 2>/dev/null || true -fi - -python3 - <<'PY' -import json -msg = ( - "Console Cursor ratchet active (.cursor/rules + hooks). " - "Native Task only (no mm-role). " - "Lane worktrees: ONLY /.worktrees/ (scripts/cursor/provision-lane-worktree.sh) — never ../console-lane-* siblings (External-File Protection / allow-edit prompts). " - "Before first push: inventory ALL threads+prior findings → ONE fix commit. " - "After that, only blocker or major+provenByExecution reopens the lane — not new unproven bot P2s. " - "preflight-forge.sh before gh pr merge. " - "Receipt: node scripts/cursor/validate-lane-receipt.mjs .cursor/receipts/.json. " - "Failure classes: .cursor/failure-classes-2026-08-10.md" -) -print(json.dumps({"additional_context": msg})) -PY diff --git a/.cursor/hooks/stop-receipt-gate.sh b/.cursor/hooks/stop-receipt-gate.sh deleted file mode 100755 index 7c7434cfc..000000000 --- a/.cursor/hooks/stop-receipt-gate.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# stop hook: only ratchet Cursor-owned dirt (.cursor/**, scripts/cursor/**). -# Do NOT nag about unrelated lane dirt (e.g. #618 migrations on this worktree). -set -euo pipefail -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -cd "$ROOT" - -# Paths this ratchet owns. Foreign dirty files are ownerLease — another lane's problem. -OWNED_DIRTY="$(git status --porcelain 2>/dev/null | grep -E '(\.cursor/|scripts/cursor/)' || true)" -if [[ -z "$OWNED_DIRTY" ]]; then - echo '{}' - exit 0 -fi - -OK=0 -shopt -s nullglob -for f in .cursor/receipts/*.json; do - # Prefer a build receipt for this lane - case "$f" in - *-critic.json) continue ;; - esac - if node scripts/cursor/validate-lane-receipt.mjs "$f" >/dev/null 2>&1; then - OK=1 - break - fi -done - -if [[ "$OK" -eq 1 ]]; then - echo '{}' - exit 0 -fi - -python3 - <<'PY' -import json -print(json.dumps({ - "followup_message": ( - "Cursor-owned dirt (.cursor/** or scripts/cursor/**) without a valid build receipt. " - "Before claiming the ratchet done: (1) enumerate known blockers and fix in ONE commit, " - "(2) write .cursor/receipts/.json with enforcementPlacement/peripheralsUpdated " - "(and commands/headSha if status=done), " - "(3) node scripts/cursor/validate-lane-receipt.mjs .cursor/receipts/.json, " - "(4) native Grok critic once — major blocks only if provenByExecution. " - "Ignore unrelated worktree dirt (other PR lanes). No mm-role." - ) -})) -PY diff --git a/.cursor/permissions.json b/.cursor/permissions.json deleted file mode 100644 index 92d1040eb..000000000 --- a/.cursor/permissions.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "_comment": "Do NOT put terminalAllowlist/mcpAllowlist here if Auto-review must stay selectable (Cursor greys Auto-review when ~/.cursor/permissions.json has non-empty allowlists; project files may merge similarly). Put deterministic verbs in Settings → Agents → Approvals & Execution. Classifier steering: ~/.cursor/permissions.json autoRun. Hard denies: .cursor/hooks/git-lock-enforcer.sh, push-admission.sh, forge-destructive-guard.sh, no-mm-role.sh, cargo-scope-enforcer.sh. Suggested IDE allowlist: git, gh, bash, python3, node, npm run, npm test, cargo, buck2, sed, bd, rg, jq, kill.", - "autoRun": { - "allow_instructions": [ - "Console autonomous workflow may run without asking when scoped to this workspace including /.worktrees/* and tmp: git (non-destructive), gh pr/api/run, npm run admit/check/verify, cargo scoped tests, buck2, python3, node scripts, bd, rg/jq." - ], - "block_instructions": [ - "Block mm-role, .grok/bin/*, claude -p, and codex exec in Cursor — use native Cursor Task subagents (never *-fast model slugs) instead.", - "Block creating git worktrees outside /.worktrees/ (no ../console-lane-* siblings).", - "Block git push --force without --force-with-lease, remote branch deletion, and direct pushes to main." - ] - } -} diff --git a/.cursor/receipts/09c-critic.json b/.cursor/receipts/09c-critic.json deleted file mode 100644 index 5a8eac673..000000000 --- a/.cursor/receipts/09c-critic.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "verdict": "APPROVE", - "tipSha": "41b9eeda5a18b2ff856e5f8ef8d472d8091bc5a1", - "priorReviewedProductTip": "ad5599d885358dbf0978a9fddfb9a82cbff868cc", - "baseSha": "eca122ad0122457a77b85ca55fc1e8b2e4869e74", - "briefBaseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "lane": "console-09c", - "role": "critic", - "lensesApplied": [ - "ORACLE INTEGRITY", - "PERIPHERAL DRIFT", - "ENFORCEMENT PLACEMENT", - "FALSE GREEN", - "Cartesian doubt", - "Red Team", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "Operability / Day-2" - ], - "challenges": [ - "Chesterton: gated-route sweep labels aggregate Gated, but the hidden-row arm only asserts no title/id disclosure and 404 when {id} is named — aggregate URIs have neither, so count inflation would still green the sweep. Adapter PG suite (not the REST sweep) is the load-bearing anti-drift oracle; REST count pin remains a follow-up, not a product residual::lower miss.", - "Red Team: attributes->> key must never be interpolated; tip binds $2 after is_safe_ident and REST further requires a declared property — poisoned key Err proven in residual-filter PG suite.", - "Cartesian: brief tip ad5599d88 was restacked mid-review onto origin/main eca122ad0 → 41b9eeda; ontology Rust trees byte-identical across rekey; only openapi.yaml absorbed leave §60⑤ paths from the new parent." - ], - "findings": [ - { - "severity": "major", - "claim": "Implementer receipt .cursor/receipts/09c.json is uncommitted working-tree only; tip product tree is sound and signed (%G?=G) but the build receipt is not on the tip (and still cites prior tip ad5599d88).", - "failureScenario": "Admission or a later critic re-reads the tip tree and finds no durable implementer receipt, or a stale headSha after restack, forcing re-proof or false belief that verification was never recorded.", - "location": ".cursor/receipts/09c.json (git status ??) vs HEAD 41b9eeda5a18b2ff856e5f8ef8d472d8091bc5a1", - "provenByExecution": true, - "ownerLease": true - }, - { - "severity": "minor", - "claim": "every_gated_route_refuses_a_policy_hidden_instance does not pin aggregate bucket sums against the permitted residual; for /instances/aggregate the hidden arm only checks disclosure strings, which AggregateBucket {key,count} cannot contain.", - "failureScenario": "A future regression that counts policy-hidden rows into GROUP BY totals could keep the REST gated sweep green while violating ADR-0030 §7.5 subject scoping at the HTTP edge.", - "location": "backend/crates/ontology/rest/tests/object_policy_attach_as_runtime_role.rs INSTANCE_ROUTE_CLASSIFICATION aggregate entry + every_gated_route_refuses_a_policy_hidden_instance disclosure-only arm", - "provenByExecution": false, - "ownerLease": true - } - ], - "acVerification": { - "residualReuseNoMv": { - "result": "pass", - "evidence": "aggregate_instances calls residual::lower(LoweringTarget::Instance { attributes_column: \"r.attributes\" }, …, first_bind) with bind offset 2 (column group) or 3 (attribute key at $2); SQL is SELECT group_key, COUNT(*) … GROUP BY 1 — no MATERIALIZED VIEW" - }, - "groupByAllowlistSafeKey": { - "result": "pass", - "evidence": "AggregateGroupBy enum + is_safe_column_ref / is_safe_ident; attribute key bound never interpolated; REST resolve_aggregate_group_by allowlist + declared property check; poisoned key Err in PG suite" - }, - "restAndOpenapi": { - "result": "pass", - "evidence": "GET INSTANCES_AGGREGATE_PATH mounted before /{id}; ONTOLOGY_ROUTE_PATHS + Gated classification; openapi path aggregateOntologyInstances + OntologyInstanceAggregateBucket schema + group_by enum present on tip 41b9eeda (post-restack openapi also carries leave consult paths from parent)" - }, - "pgProofsAbc": { - "result": "pass", - "criticReRun": [ - "bash tools/ci/cargo_needs_postgres.sh --only ontology-adapter-postgres-instances-residual-filter-as-runtime-role-pg --num-threads=1 → 3 passed (incl. aggregate_instances_subject_counts_match_list_and_deny_all_as_runtime_role)", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-object-policy-attach-postgres --num-threads=1 → 27 passed (incl. every_gated_route_refuses + every_ontology_route_is_classified)" - ], - "note": "PG re-run against product bytes identical to tip 41b9eeda ontology Rust (diff ad5599d88..41b9eeda on adapter-postgres+rest = empty); leaf commit HEAD^..HEAD is the same five owned paths" - }, - "oracleIntegrity": { - "result": "pass", - "evidence": "No #[ignore], no deleted tests, no conforming asserts; additive aggregate test + classification entry" - }, - "ownedRoots": { - "result": "pass", - "filesOnLeaf": [ - "backend/crates/ontology/adapter-postgres/src/instances.rs", - "backend/crates/ontology/adapter-postgres/tests/instances_residual_filter_as_runtime_role.rs", - "backend/crates/ontology/rest/src/lib.rs", - "backend/crates/ontology/rest/tests/object_policy_attach_as_runtime_role.rs", - "backend/openapi/openapi.yaml" - ] - } - }, - "standingLenses": { - "oracleIntegrity": "PASS — residual-filter PG 3/3 and object-policy-attach 27/27 re-run on equivalent product Rust; no ignore/weaken", - "peripheralDrift": "PASS — OpenAPI path+schema+enum retained after restack; route constants + classification; ADR Evidence cell correctly left to admission/T", - "enforcementPlacement": "PASS — residual::lower under with_org_conn; empty policies → deny_all/WHERE FALSE; unsafe key fails before SQL; route on ONTOLOGY_ROUTE_PATHS so unclassified oracle sees subject", - "falseGreen": "PASS for product ACs — no --workflow-only; commands re-executed; note minor REST sweep count-blindness (adapter suite is load-bearing)" - }, - "followUps": [ - "Parent: refresh implementer 09c.json headSha to 41b9eeda and durable-ize before admit if process requires tip-bound receipts.", - "Optional bead: strengthen every_gated_route (or a dedicated HTTP test) so aggregate bucket sum equals list length for the same subject — class: process.aggregate-count-oracle-gap.", - "Admission/T flips ADR-0030 §7 row 5 Evidence; b4z absorbs openapi.yaml on rebase." - ], - "notes": "Brief tip ad5599d88 was restacked during critique onto origin/main eca122ad0; current HEAD 41b9eeda5a18b2ff856e5f8ef8d472d8091bc5a1 verified %G?=G. Ontology Rust byte-identical across rekey; leaf vs parent is five owned files (+444/-3). Product ACs 1–6 satisfied; APPROVE. Receipt-location major is ownerLease per brief convergence." -} diff --git a/.cursor/receipts/09c.json b/.cursor/receipts/09c.json deleted file mode 100644 index 8fb95dd9a..000000000 --- a/.cursor/receipts/09c.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "lane": "console-09c", - "role": "implementer", - "status": "done", - "headSha": "41b9eeda5a18b2ff856e5f8ef8d472d8091bc5a1", - "priorTip": "eca122ad0122457a77b85ca55fc1e8b2e4869e74", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-console-09c", - "branch": "lane/console-09c", - "baseSha": "eca122ad0122457a77b85ca55fc1e8b2e4869e74 (origin/main after #745 restack)", - "summary": "ADR-0030 \u00a77.5 / bead console-09c: ontology aggregate query path. PgInstanceStore::aggregate_instances reuses residual::lower (LoweringTarget::Instance, correct bind offset) and emits SELECT , COUNT(*) GROUP BY 1 without materialising rows. Group keys allowlisted (lifecycle_state, object_type_id, attributes->>bound key with is_safe_ident). REST GET /api/v1/ontology/instances/aggregate + OpenAPI path/schema; route classified Gated. No MATERIALIZED VIEW. Restacked cleanly onto eca122ad0 (#745).", - "filesChanged": [ - "backend/crates/ontology/adapter-postgres/src/instances.rs", - "backend/crates/ontology/adapter-postgres/tests/instances_residual_filter_as_runtime_role.rs", - "backend/crates/ontology/rest/src/lib.rs", - "backend/crates/ontology/rest/tests/object_policy_attach_as_runtime_role.rs", - "backend/openapi/openapi.yaml" - ], - "redBaseline": "Before API existed: cargo test -p console-ontology-adapter-postgres --test instances_residual_filter_as_runtime_role --no-run failed with E0432 (AggregateGroupBy) + E0599 (no method named aggregate_instances) \u2014 6 errors. After implementation: same suite 3/3 ok under cargo_needs_postgres.", - "verification": "SSH-signed leaf 41b9eeda5 (%G?=G) after clean rebase onto eca122ad0. Prior tip ad5599d88 rebased 1/1 with no conflicts. PG suites recorded on prior tip under cargo_needs_postgres; OpenAPI path+schema inventory OK; route classification gated.", - "commands": [ - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-adapter-postgres --test instances_residual_filter_as_runtime_role --no-run # RED: E0432/E0599 aggregate_instances missing", - "cargo fmt --manifest-path backend/Cargo.toml -p console-ontology-adapter-postgres -p console-ontology-rest -- --check", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-adapter-postgres-instances-residual-filter-as-runtime-role-pg --num-threads=1 # 3 passed", - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-rest --test object_policy_attach_as_runtime_role every_ontology_route_is_classified # ok", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-object-policy-attach-postgres --num-threads=1 # 27 passed incl. every_gated_route_refuses", - "git rebase origin/main # clean 1/1 onto eca122ad0 \u2192 41b9eeda5", - "git log -1 --format=%H %G? # 41b9eeda5a18b2ff856e5f8ef8d472d8091bc5a1 G", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console/.worktrees/lane-console-09c/.cursor/receipts/09c.json" - ], - "contractBreaches": "Additive published contract only: new GET /api/v1/ontology/instances/aggregate + OntologyInstanceAggregateBucket schema. Existing list/get instance paths unchanged. Soft-order note: hand-edited openapi.yaml before b4z fragment migration; b4z absorbs on rebase.", - "enforcementPlacement": "WHERE: aggregate SQL builds residual via residual::lower identically to visible_instances, then COUNT/GROUP BY in Postgres under with_org_conn (RLS armed). Subject EXISTS at call time (SubjectAttrs + ObjectPolicy[] from object_view_policies). Finest distinction: per-(subject, policy-set, group_by) buckets over current-head rows of one object_type_id. Examined-zero fails: empty policy set \u2192 ResidualFilter::deny_all \u2192 WHERE FALSE \u2192 zero counts (PG-proven); unsafe attribute key \u2192 validation Err before SQL; free-form group_by rejected at REST. REST route added to ONTOLOGY_ROUTE_PATHS + INSTANCE_ROUTE_CLASSIFICATION (Gated) so unclassified-route and gated-sweep oracles see the subject.", - "peripheralsUpdated": "OpenAPI path + OntologyInstanceAggregateBucket schema; ONTOLOGY_ROUTE_PATHS + router mount before /{id}; INSTANCE_ROUTE_CLASSIFICATION gated entry; residual-filter PG suite gains aggregate_instances_*_as_runtime_role. Did NOT edit docs/decisions ADR-0030 \u00a77 Evidence cell (admission/T). Authz crate untouched (local is_safe_column_ref / is_safe_ident reuse).", - "lensesApplied": [ - "Cartesian doubt", - "Essentialism / YAGNI", - "Systems Thinking", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "challenges": [ - "Chesterton/MV: a MATERIALIZED VIEW has one body and cannot carry per-(subject, policy-set) residual::lower \u2014 counts would leak or require one MV per subject; rejected; live GROUP BY over the same residual as list is the capability \u00a77.5 actually needs.", - "Red Team: free-form attributes->>'' would be an injection surface the list path is not \u2014 attribute keys must pass is_safe_ident and are bound ($2), never interpolated; poisoned key Err proven in PG suite.", - "Cartesian: deny-all proven by empty policy set yielding total count 0 (not the unfiltered universe); list anti-drift oracle equates aggregate sum to list_instances_filtered.len() for the same subject." - ], - "followUps": [ - "Admission/T must flip ADR-0030 \u00a77 row 5 Evidence cell \u2014 this leaf must NOT edit docs/decisions/**.", - "b4z will absorb hand-edited openapi.yaml aggregate route into generated fragments on rebase; do not fight whole-file rewrite mid-lane.", - "Defer Leptos/frontend aggregate consumer; expression indexes only when a query is measured slow.", - "Orchestrator admits this leaf FIRST before b4z." - ], - "admitSha": "77e82dcd3a1b60638e06ee283aacd9e6db8e2954", - "flowDelta": "admitted onto admission/wave4-20260811 @ 77e82dcd3 (tree-identical cherry-pick); critic APPROVE 41b9eeda5; awaiting b4z" -} diff --git a/.cursor/receipts/5yn-pod-prefix-20260811.json b/.cursor/receipts/5yn-pod-prefix-20260811.json deleted file mode 100644 index a97622adb..000000000 --- a/.cursor/receipts/5yn-pod-prefix-20260811.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "lane": "5yn-pod-prefix-20260811", - "bead": "console-5yn", - "role": "implementer", - "status": "done", - "headSha": "c5b4490c5cd96622cc437bf75fa00692e754b4b2", - "priorTip": "d9808dd16d0f6a1e6117ad11a811313beff733e0", - "smokeSignature": "G jason19931225@gmail.com (ED25519) — product commit %G?=G", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-5yn-pod-prefix-20260811", - "branch": "lane/5yn-pod-prefix-20260811", - "baseSha": "d9808dd16d0f6a1e6117ad11a811313beff733e0", - "ownedRoots": ["backend/crates/logistics"], - "summary": "POD agreement corpus now builds from the ENFORCED validator scheme (discovered by probing which leading characters of an accepted reference cannot vary), not from the published pattern literal. Hostile prefix drift `^evidence://` → `^evidence://x[...]` was GREEN before and is RED after. published_property() closes flow mappings with quote-aware brace depth so ECMA `{n,m}` inside a quoted pattern no longer truncates at the first `}`.", - "filesChanged": ["backend/crates/logistics/rest/src/lib.rs"], - "redBaseline": "Hostile probe (openapi verifyLogisticsPod pattern mutated to '^evidence://x[A-Za-z0-9._/-]+$', validator unchanged): BEFORE fix `published_schema_and_validator_agree_on_concrete_references` PASS (false green). AFTER fix FAIL — disagree on enforced floor candidate \"evidence://aaaaaaaa\" (published accepts=false, server accepts=true, enforced scheme \"evidence://\").", - "verification": "cargo test -p console-logistics-rest --manifest-path backend/Cargo.toml --lib → 2 passed (agreement + flow_mapping_end ECMA unit). cargo fmt -p console-logistics-rest --manifest-path backend/Cargo.toml -- --check clean. Hostile prefix probe RED; restored openapi. ECMA quantifier in live openapi: property reader no longer truncates; parse_pattern still fail-closes on unevaluable `{n,m}` shape (subset [class]+$) — intentional, not silent truncate.", - "commands": [ - "cargo test -p console-logistics-rest --manifest-path backend/Cargo.toml --lib published_schema_and_validator_agree_on_concrete_references # RED baseline hostile: PASS under published '^evidence://x[...]' (false green) before fix", - "cargo test -p console-logistics-rest --manifest-path backend/Cargo.toml --lib published_evidence_reference_contract # GREEN after fix: 2 passed", - "cargo test -p console-logistics-rest --manifest-path backend/Cargo.toml --lib published_schema_and_validator_agree_on_concrete_references # hostile '^evidence://x[...]' → FAIL (prefix drift caught)", - "cargo fmt -p console-logistics-rest --manifest-path backend/Cargo.toml -- --check", - "cargo test -p console-logistics-rest --manifest-path backend/Cargo.toml --lib # 2 passed", - "git commit -S # c5b4490c5; git log -1 --format='%G?' → G", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console/.worktrees/lane-5yn-pod-prefix-20260811/.cursor/receipts/5yn-pod-prefix-20260811.json" - ], - "contractBreaches": "none — only backend/crates/logistics/rest/src/lib.rs. Did not touch ci.yml, frontend, infra/ci/buckconfig, scripts/cas, backend/openapi/openapi.yaml (hostile probes reverted), or sibling 90h/qjb roots.", - "enforcementPlacement": "WHERE: unit tests in published_evidence_reference_contract run under `cargo test -p console-logistics-rest --lib`; subject is validate_evidence_reference + include_str! openapi.yaml (exists at compile time via Buck/cargo mapped srcs). FINEST DISTINCTION: per-candidate published.accepts vs enforced ok on a corpus keyed to the discovered enforced scheme (not the published literal). EXAMINED-ZERO FAILS: accepted_under / enforced_window / enforced_scheme panic when no accepted seed or no required prefix; agreement asserts accepted>0 && rejected>0; flow_mapping_end panics if unterminated.", - "peripheralsUpdated": "Module doc + corpus comments in rest/src/lib.rs updated (independent corpus / console-5yn; Unicode sweep points at console-ann). docs/program/ledger/2026-08-09-contract-refs-and-pod-evidence.md still names console-5yn as open — outside owned roots; elevate to parent for ledger close after land.", - "lenses": [ - "Cartesian doubt — proved false green by mutating only the published prefix while the validator stayed fixed", - "Essentialism / YAGNI — discover enforced scheme from one accepted seed; do not restate evidence:// or 19..=411", - "Chesterton's Fence — kept parse_pattern's [class]+$ subset fail-closed rather than inventing a third quantifier evaluator", - "Red Team — hostile longer published literal, ECMA brace truncation, empty-corpus vacuity panics", - "Blast-radius / cell-based — single file under logistics/rest; path-disjoint from 90h/qjb" - ], - "claimGraph": [ - "E: pre-fix hostile '^evidence://x[...]' → test PASS (PROBE_EXIT observed ok)", - "E: post-fix same hostile → assertion fail on evidence://aaaaaaaa / enforced scheme evidence://", - "E: post-fix clean openapi → 2 lib tests PASS; fmt --check clean; commit %G?=G", - "E: flow_mapping_end unit keeps {1,400} inside quoted pattern", - "I: corpus independence requires scheme discovery after bootstrap, not published_literal for candidate construction", - "U: whether a future published pattern using {n,m} should expand parse_pattern or stay fail-closed on subset (deferred; truncation bug closed)" - ], - "challenges": [ - "Chesterton: why was the corpus built from the published scheme originally? So length/class probes stayed aligned with both sides without restating the scheme — but that made the sides dependent and blind to stricter published prefixes. Discovery after a published bootstrap keeps the no-restatement property while restoring independence.", - "Contrarian: hardcode enforced scheme \"evidence://\" in the test? Rejected — that is a third spelling of the validator constant and would not track a real scheme change." - ], - "followUps": [ - "Parent: push/open leaf PR for lane/5yn-pod-prefix-20260811; close bead console-5yn + issue #691 after land.", - "Parent/docs owner: update docs/program/ledger/2026-08-09-contract-refs-and-pod-evidence.md stop-condition / open-bead prose (outside owned roots).", - "No CI babysit from this lane." - ], - "knownBlockersInventory": [ - "No open PR on this branch → no unresolved review threads", - "No prior 5yn critic receipt in worktree", - "Issue #691 / bead console-5yn: prefix-derived corpus + published_property first-} truncation — both addressed in this commit", - "Standing false-green checks: commands non-empty; no --workflow-only; no Buck include_str gap introduced (same openapi external)" - ] -} diff --git a/.cursor/receipts/6pl.json b/.cursor/receipts/6pl.json deleted file mode 100644 index 7179fb947..000000000 --- a/.cursor/receipts/6pl.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "laneId": "console-6pl", - "status": "done", - "headSha": "7b568df9db961fa8aa3f36917eaa13c6af2c3023", - "codeChanged": false, - "summary": "Tip 7b568df9 already satisfies L4-PORT-JP: PgJobPositionPort + migration 0215 + runtime-role suite + sole production DML + required_tables/REQUIRED_TABLES pins. No owned gap; no leaf commit.", - "filesChanged": [], - "redBaseline": "Bead body at older tip claimed zero tables/writers; re-measured on 7b568df9 — FALSE. Production DML grep (POSIX ERE, validated against known UPDATE employees writer) hits only canonical-adapter-postgres/src/job_position.rs; tests/ hold hostile probes only. Gate exit-0 alone is not evidence (criterion 2 limit).", - "verification": "job_position_port_as_runtime_role: 13/13 ok via cargo_needs_postgres.sh (canonical-enforce examined 20 tables including job_positions/job_position_revisions). Gate: second-writer plant, dual-writer ratchet, stale ratchet, topology roster, expected-writer ratchet all ok; cargo run writer-ownership exit 0 scanned 254 files. Sole production DML in src/job_position.rs.", - "contractBreaches": "none. Did not touch forbidden writer-ownership/, ops/postgres-reconcile-topology.sh, orgchange/, ontology/rest|canonical-domain, sibling person/company/org_unit/pay_run modules.", - "enforcementPlacement": "n/a - adds no enforcement", - "peripheralsUpdated": "n/a - nothing described this behaviour; tip peripherals (0215, topology required_tables, census REQUIRED_TABLES arity 20, gen_first_party package key, module docs) already present from #618.", - "commands": [ - "git rev-parse HEAD", - "git grep -n -iE \"(INSERT +INTO|UPDATE|DELETE +FROM) +[\\\"']?(job_positions|job_position_revisions)\" HEAD -- backend/", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-canonical-adapter-postgres-job-position-port-as-runtime-role-pg --num-threads=1", - "cargo run -q -p console-gate-writer-ownership --manifest-path backend/Cargo.toml", - "cargo test -p console-gate-writer-ownership --manifest-path backend/Cargo.toml --test gate_detects_violation gate_detects_second_writer_of_an_owned_table -- --exact --test-threads=1", - "cargo test -p console-gate-writer-ownership --manifest-path backend/Cargo.toml --test gate_detects_violation measured_tip_has_exactly_the_ratcheted_dual_writers -- --exact --test-threads=1", - "cargo test -p console-gate-writer-ownership --manifest-path backend/Cargo.toml --test gate_detects_violation stale_ratchet_entries_fail_the_gate -- --exact --test-threads=1", - "cargo test -p console-gate-writer-ownership --manifest-path backend/Cargo.toml --test gate_detects_violation topology_script_table_roster_matches_the_registry -- --exact --test-threads=1", - "cargo test -p console-gate-writer-ownership --manifest-path backend/Cargo.toml --test gate_detects_violation topology_script_names_the_roles_that_lose_dml -- --exact --test-threads=1" - ], - "closeReasonForParent": "Close console-6pl: JobPosition single-writer port proven on origin/main tip 7b568df9 (landed in #618). Criteria 1–7 evidenced by migration 0215 (job_positions FK→org_units + append-only revisions), sole production DML in PgJobPositionPort, RED-capable gate_detects_second_writer_of_an_owned_table, unchanged KNOWN_SECOND_WRITERS, untouched roster, required_tables+REQUIRED_TABLES include both names with executed census examining 20 tables, expected-writer ratchet byte-identical. Honest negative: pre-#618 tip had zero writers/tables outside contract+roster; static gate was green before and after; proof is runtime-role suite + census execution + sole-DML grep, not gate exit code. Criterion 8 Buck key already present. Parent closes bead; no leaf commit (filesChanged=[]).", - "followUps": [ - "Parent: bd close console-6pl with closeReasonForParent text after admit.", - "Unblocks console-xxp [L5-JOB].", - "ownerLease false: topology/census/writer-ownership already on tip; tai.1 must not be disturbed." - ] -} diff --git a/.cursor/receipts/7sx-critic.json b/.cursor/receipts/7sx-critic.json deleted file mode 100644 index 4051f37f9..000000000 --- a/.cursor/receipts/7sx-critic.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "verdict": "APPROVE", - "lane": "console-7sx", - "role": "critic", - "tipSha": "c8fdd7d21dbb9650b6d648485bb4d8a20bf3d444", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "candidateSha": "c8fdd7d21dbb9650b6d648485bb4d8a20bf3d444", - "prHeadSha": "14e5de6086bb7759b0415ba55cca2c3a59e685ac", - "bead": "console-7sx", - "lensesApplied": [ - "Cartesian doubt", - "Chesterton's Fence", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "ORACLE INTEGRITY", - "PERIPHERAL DRIFT", - "ENFORCEMENT PLACEMENT", - "FALSE GREEN" - ], - "challenges": [ - "Chesterton: identity-only SECURITY DEFINER grants still use bare pool — keep that exemption narrow; this tip correctly confines FORCE-RLS company_revisions/bindings/regions/branches to with_org_conn per member org rather than extending the grants exemption.", - "Cartesian: prior tip green under BYPASSRLS owner pool is not evidence for this tip — re-ran org_reference_surface under SET ROLE console_rt on c8fdd7d and observed Resolved/bound projections.", - "Red Team: multi-org actors still require per-member app.current_org arming; bare-pool deny-by-omission would again flood UNBOUND — the per-org with_org_conn loop is the load-bearing control." - ], - "findings": [ - { - "severity": "minor", - "claim": "CreateRegion/CreateBranch binding emit (emit_region_or_branch_binding → ensure_unambiguous_legacy_binding_in_tx via with_audits) still has no dedicated console_rt integration pin of its own; coverage is indirect via org_entities readback of seeded bindings.", - "failureScenario": "A future write-path GUC regression could land while the read-path console_rt oracle stays green until an apply-path pin exists.", - "location": "backend/crates/orgchange/adapter-postgres/src/lib.rs:emit_region_or_branch_binding (~1058); org_reference_surface.rs (readback-only runtime-role pin)", - "provenByExecution": false, - "ownerLease": true - } - ], - "priorBlockersDisposition": [ - { - "id": "force-rls-with-org-conn", - "priorSeverity": "blocker", - "disposition": "FIXED", - "evidence": "E: diff 552eafd→c8fdd7d replaces bare &self.pool company_revisions EXISTS + load_org_unit_references(pool) with with_org_conn(&self.pool, org_id, …) + company_has_revision(tx) + load_org_unit_references_tx(tx). E: with_org_conn calls set_current_org. E: console_rt itest asserts company_resolution_status Resolved." - }, - { - "id": "openapi-org-entity-schemas", - "priorSeverity": "blocker", - "disposition": "FIXED", - "evidence": "E: openapi.yaml OrgEntitySummary required includes companyId/companyResolutionStatus/orgUnits; CanonicalResolutionStatus enum [RESOLVED,UNBOUND,AMBIGUOUS]; OrgUnitReference present. E: CAP-ORG openapi-fragment.yaml mirrors. E: Rust serde SCREAMING_SNAKE_CASE + camelCase fields match. E: listOrgEntities has no stale resolution list filters (response-only surface)." - }, - { - "id": "console-rt-oracle", - "priorSeverity": "major", - "disposition": "FIXED", - "evidence": "E: org_entities_readback_company_and_bound_org_units seeds on owner pool then PgOrgChangeStore::new(runtime_role_pool) with SET ROLE console_rt. E: cargo_needs_postgres --only orgchange-adapter-postgres-org-reference-surface-pg → 4 passed including that test on tip tree matching c8fdd7d." - } - ], - "standingLenses": { - "oracleIntegrity": "PASS — console_rt readback asserts Resolved/bound/unbound gap; no #[ignore]/deleted controls in fix range; binding_seam_tests 2/2 green; domain lib 8/8 green.", - "peripheralDrift": "PASS — OpenAPI + CAP-ORG fragment carry OrgEntitySummary new fields + CanonicalResolutionStatus + OrgUnitReference; company_has_revision now called from org_entities (prior unused-helper minor closed); postgres-cargo-map entry retained.", - "enforcementPlacement": "PASS — runtime-role itest is the subject that fails under unarmed FORCE RLS; with_org_conn arms app.current_org before reading FORCE-RLS tables that exist (0215). Examined-zero would fail as empty/failed test binary, not silent green.", - "falseGreen": "PASS — this tip's green is under console_rt, not owner-only; no --workflow-only; commandsRun non-empty and re-executed on c8fdd7d product paths." - }, - "highRiskLenses": { - "redTeam": "PASS — per-member with_org_conn closes silent UNBOUND under console_rt for the org-entities reference surface.", - "operabilityDay2": "PASS — resolution_status under production role is now trustworthy for the pinned seed shape.", - "blastRadius": "Contained to org-entities read arming + OpenAPI/fragment schemas + org_reference_surface runtime-role pin; binding writes unchanged via with_audits.", - "zeroTrust": "PASS — FORCE RLS boundary honored in code path and measured under NOBYPASSRLS console_rt." - }, - "fixObservation": { - "visibility": { - "before": "Prior critic tip 552eafd: bare-pool org_entities → hostile console_rt probe company_resolution_status Unbound; OpenAPI OrgEntitySummary lacked companyId/orgUnits; oracle owner-pool-only.", - "diff": "c8fdd7d vs 552eafd: adapter-postgres org_entities with_org_conn; org_reference_surface runtime_role_pool; openapi.yaml + CAP-ORG fragment schemas; company_has_revision call-site. Untouched: foreign leave dirt / unrelated OpenAPI surfaces.", - "after": "On product tip c8fdd7d (worktree product paths match): org_reference_surface 4/4 green including console_rt readback Resolved." - }, - "noRegression": "binding_seam_tests 2/2; domain 8/8; org_reference_surface remaining three tests green alongside console_rt readback.", - "genuineAddition": "Net stricter fail-closed: production-role subject + GUC-armed FORCE-RLS reads + published wire schemas; not rename/allowlist broaden." - }, - "commandsRun": [ - "git rev-parse HEAD → 14e5de6086bb7759b0415ba55cca2c3a59e685ac (PR HEAD; product tip ancestor)", - "git merge-base --is-ancestor c8fdd7d21dbb9650b6d648485bb4d8a20bf3d444 HEAD → yes", - "git diff --quiet c8fdd7d21 -- (orgchange adapter/tests + domain + company.rs + org_unit.rs + openapi.yaml + CAP-ORG fragment) → PRODUCT PATHS MATCH c8fdd7d", - "git diff 552eafd00..c8fdd7d21 -- backend/crates/orgchange/adapter-postgres/src/lib.rs → with_org_conn + load_org_unit_references_tx", - "python3 OpenAPI/CAP-ORG/Rust required-field + CanonicalResolutionStatus enum parity → match (RESOLVED,UNBOUND,AMBIGUOUS)", - "bash tools/ci/cargo_needs_postgres.sh --only orgchange-adapter-postgres-org-reference-surface-pg --num-threads=1 → 4 passed / 0 failed (includes org_entities_readback_company_and_bound_org_units under console_rt)", - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-canonical-adapter-postgres --lib binding_seam_tests → 2 passed", - "cargo test --manifest-path backend/Cargo.toml -p console-orgchange-domain --lib → 8 passed" - ], - "notes": "Fix observation re-critique bound to product tip c8fdd7d21 (not prior 552eafd; not PR chore tip alone). Implementer build receipt 7sx.json headSha matches product tip. Prior three proven blockers closed with fresh execution on this tip. APPROVE for admit of product tip; PR HEAD 14e5de6 is receipt-chore descendant.", - "followUps": [ - "Parent: optional apply-path console_rt pin for emit_region_or_branch_binding (ownerLease minor — do not open bot treadmill).", - "Parent: confirm/keep class bead process.rls-bypass-test-false-green (this tip demonstrates the required pin pattern for org-entities).", - "Parent: process.openapi-enum-peripheral-drift class remains a sweep concern for other faces; this leaf's OrgEntitySummary drift is closed." - ], - "flowDelta": "APPROVE product tip c8fdd7d — prior FORCE-RLS/OpenAPI/console_rt blockers closed; leaf ready to admit" -} diff --git a/.cursor/receipts/7sx.json b/.cursor/receipts/7sx.json deleted file mode 100644 index f419fb93f..000000000 --- a/.cursor/receipts/7sx.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "status": "done", - "summary": "L5-ORG reference on /api/v1/org-entities + region/branch binding seam; critic blockers closed: with_org_conn-armed FORCE-RLS reads, console_rt oracle pin, OpenAPI+CAP-ORG OrgEntitySummary schemas.", - "filesChanged": [ - "backend/crates/orgchange/domain/src/lib.rs", - "backend/crates/orgchange/adapter-postgres/src/lib.rs", - "backend/crates/orgchange/adapter-postgres/tests/org_reference_surface.rs", - "backend/crates/orgchange/adapter-postgres/Cargo.toml", - "backend/crates/orgchange/adapter-postgres/BUCK", - "backend/crates/ontology/canonical-adapter-postgres/src/org_unit.rs", - "backend/crates/ontology/canonical-adapter-postgres/src/company.rs", - "backend/Cargo.lock", - "backend/openapi/openapi.yaml", - "docs/evidence/console/CAP-ORG-CONSOLE/manifests/openapi-fragment.yaml", - "tools/ci/postgres-cargo-map.json", - "tools/buck/gen_first_party.py", - ".cursor/receipts/7sx.json" - ], - "redBaseline": "Critic hostile console_rt probe: company_resolution_status Unbound despite company_revisions (unarmed bare pool). OpenAPI OrgEntitySummary lacked companyId/orgUnits. Tip oracle was BYPASSRLS-only.", - "verification": "tools/ci/cargo_needs_postgres.sh --only orgchange-adapter-postgres-org-reference-surface-pg --num-threads=1 → 4 passed including console_rt readback; cargo check -p console-orgchange-adapter-postgres.", - "contractBreaches": "none", - "enforcementPlacement": "WHERE: org_entities arms app.current_org via with_org_conn per member org before reading company_revisions/bindings/regions/branches (FORCE RLS subjects exist). console_rt itest is the subject that can fail examined-zero under deny-by-omission. OpenAPI schemas declare the wire fields the REST JSON emits.", - "peripheralsUpdated": "openapi.yaml OrgEntitySummary + CanonicalResolutionStatus + OrgUnitReference; CAP-ORG openapi-fragment.yaml mirrored; company_has_revision used by org_entities; postgres-cargo-map + gen_first_party from prior commit.", - "commands": [ - "tools/ci/cargo_needs_postgres.sh --only orgchange-adapter-postgres-org-reference-surface-pg --num-threads=1", - "cargo check -p console-orgchange-adapter-postgres --manifest-path backend/Cargo.toml", - "cargo fmt -p console-orgchange-adapter-postgres" - ], - "headSha": "ce2bcc44966c94c38e9a868202c4da7ce6702b29", - "lenses": [ - "Cartesian doubt", - "Chesterton's Fence", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "challenges": [ - "Critic proven: bare-pool group grants exemption does not extend to FORCE-RLS company_revisions — fixed with per-member with_org_conn." - ], - "followUps": "Re-critique tip after push. Optional Buck sh_test wrapper for org-reference-surface. y0n owns ReassignOrgUnit/employment; xxp owns JobPosition. Foreign leave dirt on worktree elevated earlier — do not admit foreign paths." -} diff --git a/.cursor/receipts/9sxn.json b/.cursor/receipts/9sxn.json deleted file mode 100644 index 249b03357..000000000 --- a/.cursor/receipts/9sxn.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "lane": "console-9sxn", - "role": "implementer", - "status": "done", - "headSha": "0647a6293bcc579d1fc0f38a8e532f6873ce930c", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-console-9sxn", - "branch": "lane/console-9sxn", - "baseSha": "e7f207eab0186b024eaaa3d56de1f9caffc59eda", - "we1ProductTipReadOnly": "109ac1425b44897ab9512ccab8d33cc8e49f78d4", - "summary": "OpenAPI lease for we1 \u00a760\u2464 consult surface: publish POST /api/v2/leave/requests/{id}/alternate-dates with LeaveProposeAlternateDatesRequest, document LeaveRequestV2View consult fields (time_change_grounds, time_change_evidence, alternate_*), and add TimeChangeGroundsCode + TimeChangeCoverageEvidence schemas matching we1 tip 109ac1425. v1 LeaveRequestView key set left frozen. decideLeaveRequestV2 prose updated for automatic eligibility / time_change_ineligible.", - "filesChanged": [ - "backend/openapi/openapi.yaml", - ".cursor/receipts/9sxn.json" - ], - "redBaseline": "On origin/main tip e7f207eab: rg alternate-dates|time_change_grounds|alternate_start_date backend/openapi/openapi.yaml \u2192 exit 1 (zero matches). LeaveRequestV2View ended at decision_comment/ap_run_id/created_at with no consult fields. Critic we1 finding 2 (major+proven, ownerLease) recorded the same absence against product tip 109ac1425.", - "verification": "After patch: path+schemas present; v1 LeaveRequestView still lacks consult keys. node scripts/check-openapi-refs.mjs \u2192 4974 refs / 0 findings. node --test scripts/check-openapi-refs.test.mjs \u2192 15/15. node scripts/check-request-body-contract.mjs \u2192 resolved 51 / skipped 171. node --test scripts/check-request-body-contract.test.mjs \u2192 22/22. node scripts/check-platform-contract-drift.mjs \u2192 exit 1 declaring POST /api/v2/leave/requests/{}/alternate-dates until we1 route lands (admit-pair expected). Signing smoke folded: commit -S with %G?=G.", - "commands": [ - "rg alternate-dates|time_change_grounds|alternate_start_date backend/openapi/openapi.yaml (RED: exit 1)", - "rg alternate-dates|time_change_grounds|alternate_start_date|LeaveProposeAlternateDatesRequest|TimeChangeCoverageEvidence backend/openapi/openapi.yaml (GREEN: hits)", - "node scripts/check-openapi-refs.mjs", - "node --test scripts/check-openapi-refs.test.mjs", - "node scripts/check-request-body-contract.mjs", - "node --test scripts/check-request-body-contract.test.mjs", - "node scripts/check-platform-contract-drift.mjs (exit 1 until we1 co-admit \u2014 expected)", - "git commit -S (product; %G? = G)", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console/.worktrees/lane-console-9sxn/.cursor/receipts/9sxn.json" - ], - "contractBreaches": "Additive OpenAPI only: new path + request schema + optional LeaveRequestV2View consult properties + shared grounds/evidence schemas. No leave/** edits. v1 LeaveRequestView unchanged. Bidirectional route-drift gate stays red on this leaf alone until we1 REST registers the same path \u2014 pair at admit.", - "enforcementPlacement": "WHERE: existing scripts/check-openapi-refs.mjs and scripts/check-request-body-contract.mjs read backend/openapi/openapi.yaml (subject exists after this commit). Finest distinction: $ref totality + request-body serde/OpenAPI field equality for LeaveProposeAlternateDatesRequest. Examined-zero fails: refs gate fails closed below floor; request-body gate refuses empty resolved set. Route drift (check-platform-contract-drift.mjs / openapi_drift.rs) is the cross-root totality check \u2014 green only when we1 product is co-present.", - "peripheralsUpdated": "backend/openapi/openapi.yaml path+schemas+decideLeaveRequestV2 description/422 text. No docs/** (not owned). No leave crate peripheral dual-write. Receipt recorded under lane worktree.", - "lensesApplied": [ - "Cartesian doubt", - "Essentialism / YAGNI", - "Chesterton's Fence", - "Systems Thinking", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "challenges": [ - "Chesterton: OpenAPI was intentionally deferred from we1 (one writer per root) \u2014 publishing here before we1 lands makes platform-contract-drift red on this leaf alone; kept as admit-pair rather than inventing an UNDOCUMENTED_BY_DESIGN exemption that would hide the new customer route.", - "Red Team: documenting manager-chosen grounds or accepting alternate body fields beyond start_date/end_date would reintroduce discretion / leave-type mutation; schema mirrors deny_unknown_fields ProposeAlternateDatesBody and closed TimeChangeGroundsCode::BranchCoverageShortfall only." - ], - "followUps": [ - "TIP-BINDING: headSha pins product tip 0647a6293bcc579d1fc0f38a8e532f6873ce930c (receipt self-hash folded via amend; validator requires non-empty tip bind).", - "ADMIT PAIR: land with we1 so check-platform-contract-drift / openapi_drift.rs see POST alternate-dates on both sides; do not merge this leaf alone onto main.", - "WORKTREE PATH: brief asked for sibling /Users/jasonlee/Developer/console-lane-9sxn; provisioned under hub .worktrees/lane-console-9sxn per BASE_LOCK (process.worktree-outside-workspace).", - "we1 still BLOCK on ACTIVE headcount filter (critic finding 1, ownerLease false) \u2014 OpenAPI lease does not unblock that; parent must re-critique we1 before train admit.", - "No PR / no push from this implementer (brief)." - ] -} diff --git a/.cursor/receipts/9ze.json b/.cursor/receipts/9ze.json deleted file mode 100644 index 68bbbef10..000000000 --- a/.cursor/receipts/9ze.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "status": "done", - "laneId": "console-9ze", - "bead": "console-9ze (P1 BUG): ADR-0030 §8 planning-only gate cannot see a Rust console route (fail-open)", - "headSha": "7b568df9db961fa8aa3f36917eaa13c6af2c3023", - "branch": "lane/console-9ze", - "summary": "route-inventory.test.mjs asserted only that HEAD tracks neither of two deleted React TypeScript paths, so a Leptos console workspace crate (the ADR-0030 §1 artifact class) passed CI untouched. RED-proven at tip: a live console-payroll-ui member declaring leptos+leptos_axum (admitted by the crates/payroll/* member glob with ZERO Cargo.toml edits) left the gate green 4/4. Fix: three build-graph tiers in route-inventory.mjs, exercised by the same CI invocation — (1) no workspace member may carry the §6-chartered '-ui' surface-crate name, (2) no member may declare a Leptos-family dependency (cargo metadata reports TRUE package names, so `view = { package = \"leptos\" }` renames cannot hide), (3) backend/Cargo.lock may resolve no Leptos-family package (catches transitive/path-dep smuggling; CI's `cargo metadata --locked` step keeps the lockfile honest). Deliberately NOT a Rust source-text grep, per the bead's correction note. Unreadable inventory (missing manifest/lockfile, cargo failure, zero members, zero locked packages, unreadable member/dependency/package entries) throws — fail closed, examined-zero fails. TS tombstone assertions retained as React-resurrection regression pins.", - "filesChanged": [ - "scripts/console/route-inventory.mjs", - "scripts/console/route-inventory.test.mjs", - ".cursor/receipts/9ze.json" - ], - "redBaseline": "Keyed to origin/main tip 7b568df9db961fa8aa3f36917eaa13c6af2c3023. Probe: planted backend/crates/payroll/ui/{Cargo.toml,src/lib.rs} (name console-payroll-ui, [dependencies] leptos = \"0.9.0-beta\", leptos_axum = \"0.9.0-beta\") as an uncommitted fixture in the lane worktree; `cargo metadata --no-deps` confirmed it live as a workspace member via the crates/payroll/* glob (no manifest edit). RED (defect, old gate): `node --test scripts/console/route-inventory.test.mjs` -> 4/4 pass, exit 0, violation invisible. CONTROL PROOF (new gate, same hostile tree): same command -> exit 1, live test names all three violations verbatim (chartered '-ui' name, leptos dep, leptos_axum dep). Probe fixture then removed (rm -rf backend/crates/payroll/ui); no probe content is committed — the committed hostile artifacts live only as tmpdir fixtures inside the test.", - "verification": "GREEN keyed to 7b568df9db961fa8aa3f36917eaa13c6af2c3023 in worktree /Users/jasonlee/Developer/console-lane-9ze: (a) exact CI invocation `node --test scripts/console/route-inventory.test.mjs` -> 7/7 pass exit 0 on the clean tree (~1.7s, live cargo metadata --no-deps --offline ~1s over 173 members, 0 leptos packages in Cargo.lock); (b) full `node --test 'scripts/console/*.test.mjs'` -> 98/98 pass exit 0 (no test deleted/skipped/weakened; suite grew 4->7 in this file); (c) `npm run check:js-test-reachability` -> 41 suites, 0 dark; `node tools/ci/check-mjs-dark-suites.mjs --strict` -> ok:true, route-inventory.test.mjs exact-wired; (d) fixture tests prove: glob-discovered member detection, rename-dodge detection under TRUE package name, leptos-free '-ui' name detection, lockfile-tier leptos/leptos_axum detection, near-miss non-flagging (console-payroll-gui, lettre, leptonic, leptose), and eight fail-closed refusals (zero members, unreadable member/dep, empty/nameless lockfile, missing manifest, missing lockfile, unresolvable member glob, empty workspace). CI context unchanged: gate runs at .github/workflows/ci.yml:132 inside the preflight job, which already carries cargo (its own `cargo metadata --locked` step) — no ci.yml change needed.", - "commands": [ - "git -C /Users/jasonlee/Developer/console worktree add /Users/jasonlee/Developer/console-lane-9ze -b lane/console-9ze origin/main", - "git log --show-signature -1", - "cargo metadata --no-deps --format-version 1 --offline --manifest-path backend/Cargo.toml", - "node --test scripts/console/route-inventory.test.mjs", - "node --test 'scripts/console/*.test.mjs'", - "npm run check:js-test-reachability", - "node tools/ci/check-mjs-dark-suites.mjs --strict", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs .cursor/receipts/9ze.json" - ], - "contractBreaches": "none", - "enforcementPlacement": "WHERE: inside scripts/console/route-inventory.test.mjs, which CI already executes at .github/workflows/ci.yml:132 in the preflight job — the only job carrying both node and cargo (its `cargo metadata --locked` step proves cargo present), so the invocation is unchanged and the subject is visible at run time. SUBJECT EXISTS: yes at two levels — the workspace member inventory exists on every commit (173 members today, enumerated by cargo's own glob-aware `cargo metadata --no-deps`, the same authority the build uses), and backend/Cargo.lock exists and is forced in-sync by the adjacent --locked step; the forbidden artifact class itself does not exist yet (that absence is the invariant being guarded). FINEST DISTINCTION: per-member TRUE package name and per-dependency TRUE package name (rename field carried separately), plus per-locked-package name — finer than path globs and immune to dependency aliasing; near-misses one character away (leptose, leptonic, -gui) are proven unflagged. EXAMINED-ZERO FAILS: zero workspace members throws, zero locked packages throws, unreadable manifest/lockfile/member/dependency throws, cargo failure throws — the gate cannot pass by observing nothing.", - "peripheralsUpdated": "Module-level doc comment in route-inventory.mjs now states the Rust-side contract next to the code (the old comment's TS-only framing was the defect's own concession). Line-position peripherals preserved by construction: ADR-0030 cites route-inventory.mjs:4-5 and route-inventory.test.mjs:36 — the appended-import layout keeps both citations true (verified: tombstone test still starts at :36; consts still at :4-5). check:doc-citations-covered docs reference route-inventory by name only (verified, no file:line pins). No registry/baseline enumerates route-inventory exports or scripts/console file lists (js-test-reachability and dark-suite censuses re-run green, no new files added). Beads: bd close console-9ze is owed by the integrator on merge, not from this worktree (hub .beads is a shared root).", - "lensesApplied": { - "redTeam": "Dodge matrix executed, not asserted: (a) rename the crate -> caught by leptos-family dependency tier (fixture: console-payroll-widgets); (b) alias the dependency (`view-framework = { package = \"leptos\" }`) -> cargo metadata reports name=leptos, rename=view-framework; caught and proven in fixture; (c) hand-rolled leptos-free UI crate under the chartered name -> caught by '-ui' name tier (fixture: console-hr-ui); (d) smuggle leptos as a path dependency of an existing member (never a new member) -> invisible to --no-deps member tier, caught by lockfile tier, which CI's --locked consistency step keeps truthful; (e) add crate without touching Cargo.toml -> the RED probe proved member globs admit it silently; cargo metadata (glob-aware) still enumerates it, manifest-text parsing never would. RESIDUAL (recorded, not closed): hand-rolled non-Leptos HTML routes hidden inside an EXISTING conforming crate (e.g. axum HTML in a -rest crate), and a wholesale vendored fork of leptos under a false package name — both are below build-graph granularity; the sighted control for that class is the layer-boundary gate's missing Ui classification (bead's own pointer, backend/ci/gates/ — outside this lane's root). Filed in followUps.", - "zeroTrust": "Every inventory input is treated as hostile-or-broken: missing backend/Cargo.toml, missing backend/Cargo.lock, cargo exit!=0, unparseable JSON, member missing name/manifest_path/dependencies, dependency missing name, lockfile with zero [[package]] blocks or a nameless block — each throws with a named reason. Eight refusals executed in the fail-closed test.", - "blastRadius": "False-positive audit against the live tree: 173 members, zero '-ui' names, zero leptos-family deps, zero leptos lockfile packages — gate is no-noise at tip. Near-miss names enumerated and proven unflagged: console-payroll-gui ('-gui' does not match the '-ui' dash-segment), leptonic/leptose (separator-anchored /^leptos(?:[_-]|$)/), lettre. Legitimate new backend crates (any -domain/-application/-adapter-*/-rest/-worker or one-off shapes like integrity/mailbox) never touch the predicates, so no cross-lane baseline-edit coupling was institutionalized — a rejected alternative (full member baseline) would have made every crate-adding lane edit scripts/console/**.", - "operability": "Failure messages name the offending member, its manifest path relative to repo root, the TRUE package name plus local alias, the ADR clause violated, and the §8 retirement rule; the lockfile-tier message includes the `cargo tree -i ` command that locates a transitive introducer. Added CI cost ~1.7s in a job already running full cargo metadata.", - "chesterton": "TS tombstone assertions and the extractor kept intact: they still pin React-stack resurrection and hold ADR-0025/ADR-0030 citations; nothing existing was deleted, skipped, or weakened." - }, - "claims": { - "E-observed": [ - "Old gate passes 4/4 over a live Leptos console member at tip (RED run recorded verbatim)", - "console-payroll-ui became a workspace member with zero Cargo.toml edits via the crates/payroll/* glob (cargo metadata output)", - "preflight CI job carries cargo (its own `cargo metadata --locked` step at ci.yml, same job as the gate invocation at :132)", - "173 members, zero leptos anywhere at tip; new gate 7/7 green; full scripts/console glob suite 98/98 green", - "ADR-0030 §1 'Leptos application composed of workspace crates', §6 'console--ui', §8 'no mounted shell, no route, no component' read verbatim at tip", - "cargo metadata reports renamed dependencies under their TRUE package name (fixture-proven)" - ], - "I-inferred": [ - "Any CI-green violating tree must carry leptos in Cargo.lock, because the --locked consistency step fails on an out-of-sync lockfile — so the lockfile tier cannot be raced by commit ordering", - "A dev machine without cargo fails this gate closed (execFileSync ENOENT throws) — acceptable: binding context is CI where cargo is proven present" - ], - "U-uncertain": [ - "Whether a future legitimate crate could ever need a leptos-family dependency before the §7 gate opens (judged no; if it happens the gate red is the intended review trigger)", - "Whether §6's '-ui' naming survives the pending ADR-0001 amendment unchanged; if the chartered name changes, tier 1's regex needs the same one-line amendment (tiers 2-3 are name-independent)" - ] - }, - "challenges": [ - "Challenged the bead's cheaper alternative subject ('a new member the layer gate cannot classify except by the Layer::Adapter fallback'): reimplementing the layer gate's suffix taxonomy in Node duplicates one classification across two languages (drift hazard), and unclassifiable-fails would false-positive on every legitimately novel backend shape (existing precedents: integrity, mailbox, credential-cipher, kernel-core, runtime, service) — alarm fatigue trains mechanical allowlisting, which is how the console crate would eventually be stamped through. ADR-derived artifact-class predicates are total for the chartered class and zero-noise for backend evolution; the layer-gate Ui variant remains the right home for the residual class and is filed as a follow-up on its owning root.", - "Challenged the brief's own question (workspace-membership vs filesystem-wide scanning): a Cargo.toml on disk that is NOT a workspace member cannot build or mount anything — filesystem-wide scanning would guard dead source while adding path-convention assumptions; membership+lockfile is exactly the 'can this reach a browser' boundary. The one artifact class filesystem scanning would catch earlier (a prepared-but-unwired crate directory) becomes a member the moment it can violate §8.", - "Challenged docs/ideas/ecosystem-plan-DRAFT.md prerequisite 5.7b ('a Leptos-shape extractor in route-inventory.mjs'): a shape extractor over Rust source is the exact mechanism the bead's correction note forbids (one spelling, one position, fail-open). Landed the build-graph mechanism instead; the draft is HOLD, not authority — planner should reconcile 5.7b's wording." - ], - "followUps": [ - "ELEVATE (layer-boundary lane, backend/ci/gates/layer-boundary — outside this lane's root): add the missing Ui classification so 'a member the gate can only classify by Adapter fallback' stops being silent; that is the sighted control for the residual class this gate cannot see (hand-rolled non-Leptos HTML routes inside an existing conforming crate).", - "ELEVATE (docs lane): ADR-0030 §8 and ADR-0025 cite route-inventory.test.mjs:36 — still true after this change by construction, but any future edit above that line breaks the citation; docs lane may want an anchor-comment convention. Also reconcile ecosystem-plan-DRAFT 5.7b wording with the landed build-graph mechanism.", - "ELEVATE (planner): vendored-fork dodge (leptos forked under a false package name) is deliberate-fraud territory below any build-graph gate's granularity; owner is human review plus the layer gate's external-dep taxonomy.", - "Integrator: bd close console-9ze on merge; hub .beads is a shared root, not written from this lane." - ] -} diff --git a/.cursor/receipts/ae5.json b/.cursor/receipts/ae5.json deleted file mode 100644 index 8e6e5d1c4..000000000 --- a/.cursor/receipts/ae5.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "lane": "console-ae5", - "bead": "console-ae5 (gh#631 \u2014 audit-chain PR-4: wire ExternalSealSigner + custody transport)", - "role": "implementer", - "status": "done", - "baseSha": "9acd784f534bb25b0468139be8f9acb13bd8fbd8", - "headSha": "1e5bcfeb86766406a8d4e4fd66e5903fab60063f", - "worktree": "/Users/jasonlee/Developer/console-lane-ae5 (branch lane/console-ae5, tracking origin/main)", - "summary": "Closed mbl-critic F1/F2/F3 inventory after train-2 #734. F1: restored the crate-header residual caveat that coordinated tail rollback still verifies clean without an out-of-band head/seal anchor. F3: added external_signer_detects_forged_signature_under_pinned_key_ref (pinned key_ref + foreign signature \u2192 BadSignature). F2: production wiring path in backend/app \u2014 AuditChainExternalConfig from env (KEY_REF + ANCHORS + CUSTODY_URL), HttpCustodyTransport (sync HTTP/1.1 SealSignTransport client), AppState attestation and seal worker construct ExternalSealSigner when configured; seal-enabled without custody fails closed unless CONSOLE_AUDIT_CHAIN_ALLOW_DEV_SIGNER=true. Do not overclaim production tamper-evidence: the custody *daemon* that holds the private key remains HOLD (out of process / deploy lane).", - "filesChanged": [ - "backend/app/src/audit_chain_signer.rs", - "backend/app/src/lib.rs", - "backend/crates/platform/audit-chain/src/lib.rs", - "backend/crates/platform/audit-chain/tests/audit_chain_rls.rs", - ".cursor/receipts/ae5.json" - ], - "redBaseline": "Hostile probe at base 9acd784f5: backend/app/src/lib.rs constructed InMemoryEd25519Signer at AppState::new (attestation) and at the seal-worker spawn path when CONSOLE_AUDIT_CHAIN_SEAL_ENABLED=true \u2014 ExternalSealSigner existed in-crate but was unwired (mbl-critic F2 provenByExecution). F3 branch (anchors.get \u2192 Some(pk), ring verify fails) had no dedicated test. After the product commit, unit tests in audit_chain_signer prove External construction + forged-sig rejection; mutating verify to trust unpinned/forged paths would flip those tests and external_signer_detects_forged_signature_under_pinned_key_ref red.", - "verification": "GREEN. cargo test --locked --manifest-path backend/Cargo.toml -p console-app --lib audit_chain_signer \u2192 8 passed. cargo test --locked --manifest-path backend/Cargo.toml -p console-platform-audit-chain --lib \u2192 5 passed. bash tools/ci/cargo_needs_postgres.sh --only platform-audit-chain-rls --num-threads=1 \u2192 24 passed; 0 failed; 0 ignored (includes new external_signer_detects_forged_signature_under_pinned_key_ref). cargo clippy --locked --manifest-path backend/Cargo.toml -p console-platform-audit-chain -p console-app --all-targets \u2192 Finished, exit 0. cargo fmt -p console-app -p console-platform-audit-chain -- --check \u2192 exit 0. git commit -S \u2192 %G? = G.", - "commands": [ - "git -C /Users/jasonlee/Developer/console fetch origin main && git rev-parse origin/main \u2192 9acd784f534bb25b0468139be8f9acb13bd8fbd8", - "git -C /Users/jasonlee/Developer/console worktree add /Users/jasonlee/Developer/console-lane-ae5 -b lane/console-ae5 origin/main", - "cargo fmt --manifest-path backend/Cargo.toml -p console-app -p console-platform-audit-chain", - "cargo test --locked --manifest-path backend/Cargo.toml -p console-app --lib audit_chain_signer", - "cargo test --locked --manifest-path backend/Cargo.toml -p console-platform-audit-chain --lib", - "bash tools/ci/cargo_needs_postgres.sh --only platform-audit-chain-rls --num-threads=1", - "cargo clippy --locked --manifest-path backend/Cargo.toml -p console-platform-audit-chain -p console-app --all-targets", - "cargo fmt --manifest-path backend/Cargo.toml -p console-app -p console-platform-audit-chain -- --check" - ], - "contractBreaches": "none. Owned roots only: backend/app/src/**, backend/crates/platform/audit-chain/**, .cursor/receipts/ae5.json. No Cargo.toml/BUCK/deploy/docs/** edits. No oracle weakening. No leaf PR opened. No CI babysit.", - "enforcementPlacement": "n/a - adds no enforcement. Runtime trust boundary remains ExternalSealSigner::verify (pinned anchors) and seal_org_once sign-before-INSERT; this lane wires the constructor + HTTP transport client and adds a coverage pin for the Some(pk)/forged-sig arm. Examined-zero cannot false-pass the new itest (demands BadSignature + first_bad_seq=1 after a forged UPDATE that keeps the pinned key_ref).", - "peripheralsUpdated": "Crate header (audit-chain lib.rs) restored F1 residual caveat for coordinated tail rollback / out-of-band head anchor. AppConfig + AppState + worker-spawn comments updated for external custody wiring and allow-dev escape hatch. Module doc on audit_chain_signer.rs documents env vars and HOLD on the custody daemon. No docs/** described production wiring of this signer, so no doc-manifest regeneration.", - "lenses": [ - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "Chesterton's Fence", - "Essentialism / YAGNI" - ], - "lensEvidence": { - "Red Team": "E: F3 itest forges a signature under the pinned key_ref and asserts BadSignature. E: seal-enabled without custody fails closed (worker None) unless allow-dev. U: custody daemon compromise remains the documented boundary.", - "Operability / Day-2": "E: HttpCustodyTransport maps connect failure to Unavailable; verify stays custody-independent (crate contract unchanged). I: operators can enable attestation anchors without running the seal worker.", - "Blast-radius / cell-based": "E: only app signer wiring + audit-chain header/test; no migration/Cargo/deploy churn. I: default seal worker remains OFF.", - "Zero-trust / defense-in-depth": "E: partial env groups rejected; active key_ref must appear in anchors; http:// only (no silent TLS skip). I: allow-dev is explicit opt-in, never default.", - "Chesterton's Fence": "E: kept InMemory path for attestation when external unset and for allow-dev sealing so existing tests/local flows do not break. I: did not couple verify to transport availability (mbl deviation upheld).", - "Essentialism / YAGNI": "E: std TcpStream HTTP/1.1 client \u2014 no new Cargo deps (Cargo.toml outside owned roots). U: TLS custody + daemon binary are follow-ups with ownerLease." - }, - "claimGraph": { - "E1": "origin/main tip verified 9acd784f5 before worktree provision.", - "E2": "8/8 audit_chain_signer unit tests + 5/5 audit-chain lib + 24/24 postgres itests green.", - "E3": "App no longer constructs InMemoryEd25519Signer in lib.rs production paths; only via audit_chain_signer when external absent / allow-dev.", - "I1": "Production is tamper-evident only when external env is set AND a real custody daemon serves signatures \u2014 wiring alone is not end-to-end evidence.", - "U1": "Custody daemon binary, deploy secret mounts, TLS transport, OpenBao/KMS adapters remain out of this lane's owned roots." - }, - "challenges": [ - "Challenged whether this lane must ship the custody daemon itself: brief allows explicit HOLD with ownerLease when custody transport is out of process. Shipped the in-app HTTP SealSignTransport client + env wiring; daemon remains HOLD.", - "Challenged adding reqwest/TLS: Cargo.toml is outside owned roots and workspace reqwest has default-features=false with no TLS \u2014 std http:// client is the smallest in-root path for self-host loopback." - ], - "followUps": [ - { - "item": "HOLD: out-of-process custody signing daemon (key holder) + deploy secret mounts for CONSOLE_AUDIT_CHAIN_SEAL_ANCHORS / CUSTODY_URL / token \u2014 required before claiming production tamper-evidence end-to-end", - "ownerLease": "deploy/custody-daemon lane (not console-ae5 owned roots)" - }, - { - "item": "TLS-capable custody transport (https://) and/or OpenBao/KMS SealSignTransport adapters", - "ownerLease": "follow-up bead; may need Cargo.toml feature ownership" - }, - { - "item": "Out-of-band head/seal anchor for coordinated tail rollback (F1 residual mechanism)", - "ownerLease": "future audit-chain anchoring lane" - }, - "Parent/orchestrator: tip + receipt only \u2014 no leaf PR from this lane; do not close gh#271 as production-tamper-evident." - ], - "inventory": { - "mblCriticF1": "done \u2014 header residual caveat restored", - "mblCriticF2": "done \u2014 production wiring path + HTTP custody transport client; daemon HOLD", - "mblCriticF3": "done \u2014 pinned-key_ref forged-signature itest" - } -} diff --git a/.cursor/receipts/b4z-critic.json b/.cursor/receipts/b4z-critic.json deleted file mode 100644 index d911e98e9..000000000 --- a/.cursor/receipts/b4z-critic.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "verdict": "APPROVE", - "tipSha": "a1f0425ec8726e057af03f5b0e93253e0d8be96b", - "baseSha": "eca122ad0122457a77b85ca55fc1e8b2e4869e74", - "lane": "console-b4z", - "role": "critic", - "lensesApplied": [ - "ORACLE INTEGRITY", - "PERIPHERAL DRIFT", - "ENFORCEMENT PLACEMENT", - "FALSE GREEN", - "Cartesian doubt", - "Red Team", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "Operability / Day-2" - ], - "challenges": [ - "Chesterton: hand-maintained openapi.yaml was the SoT because faces already authored YAML — replacing it with compose+include_str! slices keeps one SoT without inventing utoipa.", - "Red Team / examined-zero: a generator that rewrites nothing over an unchanged file would false-green an empty registry — tip keeps EXPECTED_FRAGMENTS=35 and exits nonzero on mismatch.", - "Admit sequencing: tip intentionally lacks 09c /instances/aggregate (not on leaf base). Serial admit MUST re-split from the 09c-admitted openapi before CI regen+diff, or the train drops the aggregate path." - ], - "findings": [ - { - "severity": "major", - "claim": "Leaf tip openapi.yaml (508 paths) does not yet include GET /api/v1/ontology/instances/aggregate from console-09c; compose+CI would drop that path if b4z is admitted without re-split absorb.", - "failureScenario": "Wave4 train merges generator+CI diff gate while the admitted tree's fragments omit the aggregate route 09c already landed, regressing §7.5's published contract.", - "location": "backend/openapi/openapi.yaml on a1f0425ec vs admission tip 77e82dcd3 path /api/v1/ontology/instances/aggregate", - "provenByExecution": true, - "ownerLease": true - }, - { - "severity": "minor", - "claim": "Implementer receipt headSha field may lag the chore rebind tip (ec1f375d8 vs a1f0425ec); product tip is signed and pushed.", - "failureScenario": "A skim that trusts receipt.headSha alone cites a parent of the pushed tip.", - "location": ".cursor/receipts/b4z.json headSha vs git rev-parse HEAD", - "provenByExecution": true, - "ownerLease": true - } - ], - "standingLenses": { - "oracleIntegrity": "PASS — compose 35/35 and todos openapi_fragment 8/8 green; no #[ignore]/no deleted tests observed on leaf range", - "peripheralDrift": "PASS for leaf scope — CI regen step, split script, gen_registry, 34 face openapi.rs modules present; ADR §7 row 3 flip correctly deferred to admission/T", - "enforcementPlacement": "PASS — Backend job runs console-openapi-gen then git diff --exit-code; fragment-count floor makes examined-zero fail", - "falseGreen": "PASS — not --workflow-only; commands recorded; idempotent gen proven by cmp across two runs on tip" - }, - "summary": "APPROVE leaf mechanism for ADR-0030 §7.3. Blocking product risk is admit-order absorb of 09c aggregate into fragments (ownerLease on admit writer), not a leaf defect versus its base." -} diff --git a/.cursor/receipts/b4z.json b/.cursor/receipts/b4z.json deleted file mode 100644 index e64a2a935..000000000 --- a/.cursor/receipts/b4z.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "status": "done", - "laneId": "console-b4z", - "bead": "console-b4z: [ADR-0030 \u00a77.3] Generate backend/openapi/openapi.yaml from console-contracts", - "headSha": "a1f0425ec8726e057af03f5b0e93253e0d8be96b", - "branch": "lane/console-b4z", - "summary": "openapi.yaml is now produced by console-openapi-gen composing 35 Fragments (1 shared + 34 REST faces). Fragment/compose gained DocumentPreamble plus parameters/responses/securitySchemes; faces keep YAML via include_str! under rest/openapi/ (lazy shape). Split oracle: path/schema/operationId inventories unchanged (507/981/571). CI runs the generator then git diff --exit-code. Restacked onto main tip eca122ad0 (#745) via signed re-split. ADR-0030 \u00a77 row 3 tip-bind left to admission/T.", - "filesChanged": [ - "backend/crates/contracts/**", - "backend/openapi/**", - "backend/crates/*/rest/openapi/**", - "backend/crates/*/rest/src/openapi.rs", - "backend/crates/*/rest/src/lib.rs", - "backend/crates/*/rest/Cargo.toml", - "backend/Cargo.lock", - ".github/workflows/ci.yml", - ".cursor/receipts/b4z.json" - ], - "redBaseline": "Keyed to origin/main tip be7b3e14b2f126e8c24a2bf99758d0de00aaa23d. RED: (1) console-contracts had no compose_document / non-schema component sections \u2014 new tests compose_document_emits_openapi_info_preamble, security_schemes_parameters_and_responses_compose_under_components, duplicate_parameter_key_is_rejected, a_response_ref_is_dangling_once_responses_are_contributed would not compile against tip; (2) no console-openapi-gen bin existed; (3) 1/34 faces had openapi.rs; published doc was hand-maintained include_str! SoT with zero CI regen assertion.", - "verification": "GREEN in worktree lane-console-b4z: cargo test -p console-contracts --test compose -> 35/35; cargo test -p console-todos-rest --test openapi_fragment -> 8/8 (drift + request-body-adjacent fragment checks retained); cargo run -p console-contracts --bin console-openapi-gen idempotent (cmp clean across two runs); inventory vs HEAD tip: paths 507=507, schemas 981=981, operationIds 571=571; generator refuses !=35 fragments (examined-zero). Receipt rebound to tip a1f0425ec8726e057af03f5b0e93253e0d8be96b.", - "commands": [ - "git rev-parse HEAD", - "SQLX_OFFLINE=true cargo test --locked --manifest-path backend/Cargo.toml -p console-contracts --test compose", - "SQLX_OFFLINE=true cargo test --locked --manifest-path backend/Cargo.toml -p console-todos-rest --test openapi_fragment", - "SQLX_OFFLINE=true cargo run --locked --manifest-path backend/Cargo.toml -p console-contracts --bin console-openapi-gen", - "python3 backend/openapi/split_openapi.py", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console/.worktrees/lane-console-b4z/.cursor/receipts/b4z.json" - ], - "contractBreaches": "none", - "enforcementPlacement": "WHERE: .github/workflows/ci.yml Backend job, immediately after the existing console-contracts compose + console-todos-rest openapi_fragment cargo test steps \u2014 subject backend/openapi/openapi.yaml already exists in the tree and is the include_str! input for console-app. SEQUENCE: cargo run console-openapi-gen rewrites the file from Fragments, then git diff --exit-code -- backend/openapi/openapi.yaml. FINEST DISTINCTION: full composed document bytes (compose-canonical sort+reindent), not a path-count heuristic. EXAMINED-ZERO FAILS: generator exits nonzero if ALL_FRAGMENTS.len()!=35 (shared+34 faces), so an empty/partial registry cannot pass by rewriting nothing; composition errors also exit nonzero before the diff.", - "peripheralsUpdated": "console-contracts module docs updated for preamble/non-schema sections; todos openapi.rs migrated to include_str! slices (openapi_fragment.rs kept); all 34 rest faces gained openapi.rs + console-contracts dep + mod openapi; backend/openapi/split_openapi.py + shared/ slices + gen_registry.rs are the regeneration peripherals; CI comment block documents the regen contract. ADR-0030 \u00a77 row 3 NOT flipped (admission/T). Buck contracts target unchanged (cargo CI owns regen).", - "lensesApplied": { - "cartesian": "Separated tip evidence (hand yaml SoT, 1/34 fragments) from inference (compose can grow sections) and uncertainty (path\u2192face heuristic for app/platform routes).", - "essentialism": "Lazy include_str! YAML slices instead of ~43k lines of inline Rust; no utoipa.", - "systemsThinking": "Generator registry + face slices + shared components + CI diff form one loop; openapi_drift remains the router\u2194document check.", - "opportunityCost": "Filesystem generator without face crate deps avoided compiling 34 rest graphs for regen.", - "operability": "split_openapi.py regenerates slices+gen_registry; console-openapi-gen is the only write path for openapi.yaml.", - "blastRadius": "Owned roots only; 09c can add a path YAML + openapi.rs line and rebase cheaply.", - "redTeam": "Duplicate keys fail closed; dangling response refs fail once responses are contributed; foreign-host $ref still rejected (ann).", - "zeroTrust": "CI does not trust a hand-edited yaml; regen+diff is mandatory; fragment count floor prevents empty registry false green." - }, - "claims": { - "E-observed": [ - "compose tests 35/35 including new preamble/component section tests", - "todos openapi_fragment 8/8 after include_str! migration", - "generator idempotent; inventories 507 paths / 981 schemas / 571 operationIds match tip", - "CI step wired for console-openapi-gen + git diff --exit-code" - ], - "I-inferred": [ - "Compose-canonical reorder of openapi.yaml preserves contract content for openapi_drift and clients", - "App/platform routes assigned via prefix map into the 34 faces remain editable under those faces' openapi/ dirs" - ], - "U-uncertain": [ - "Whether 09c's new ontology path will land as a hand yaml edit on its tip \u2014 rebase onto 09c then re-split/regen is the intended absorb path" - ] - }, - "challenges": [ - "Chesterton: hand-maintained openapi.yaml survived because faces authored YAML already and a typed OpenAPI model would be a second SoT \u2014 kept raw YAML bodies and extended compose instead of inventing utoipa.", - "Challenged inline-Rust transcription of the todos pattern for all faces: ~43k Rust lines vs include_str! slices; chose lazy shape per bead.", - "Challenged regen CI that only diffs without a fragment-count floor: examined-zero would pass if the generator wrote nothing over an unchanged file after a registry wipe \u2014 added EXPECTED_FRAGMENTS=35 guard." - ], - "followUps": [ - "ELEVATE (admission/T): flip ADR-0030 \u00a77 row 3 to MET with quoted generator command+output \u2014 this lane must NOT tip-bind the ADR.", - "Admit order: console-09c then console-b4z; if 09c added paths to openapi.yaml, restack b4z onto 09c tip and re-run split_openapi.py + console-openapi-gen before admit.", - "Integrator: bd close console-b4z on merge; hub .beads is a shared root." - ] -} diff --git a/.cursor/receipts/buck2-cas-everywhere-20260811.json b/.cursor/receipts/buck2-cas-everywhere-20260811.json deleted file mode 100644 index 9edfb3fec..000000000 --- a/.cursor/receipts/buck2-cas-everywhere-20260811.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "laneId": "buck2-cas-everywhere-20260811", - "role": "implementer", - "status": "ready", - "summary": "Wave A committed: opt-in Buck2 CAS overlays + cas helper scripts + DN-0006 (Buck2-everywhere, supersedes DN-0005 cargo-primary planning) + expanded handoff. Cold Buck2 without overlay green. CI cargo\u2192Buck flips deferred to Waves C\u2013E under sole-writer lease.", - "baseSha": "5770c04c4", - "headSha": "09983fed13154714881134c70374079c78de8c7a", - "ownedRoots": [ - "infra/ci/buckconfig/", - "scripts/cas/", - "docs/handoffs/2026-08-11-oyatie-shared-cas.md", - "docs/decisions/notes/DN-0006-buck2-primary-shared-cas.md", - "docs/decisions/README.md", - ".cursor/receipts/buck2-cas-everywhere-20260811.json" - ], - "filesChanged": [ - "infra/ci/buckconfig/warm-cache-lab-rw.buckconfig", - "infra/ci/buckconfig/warm-cache-lab-ro.buckconfig", - "infra/ci/buckconfig/warm-cache-gha-rw.buckconfig", - "infra/ci/buckconfig/warm-cache-gha-ro.buckconfig", - "scripts/cas/materialize-buckconfig-local.sh", - "scripts/cas/start-access-tcp.sh", - "docs/handoffs/2026-08-11-oyatie-shared-cas.md", - "docs/decisions/notes/DN-0006-buck2-primary-shared-cas.md", - "docs/decisions/README.md", - ".cursor/receipts/buck2-cas-everywhere-20260811.json" - ], - "redBaseline": "Prior DN-0005 + ci.yml comments treated Buck2 as cold across CI (cached:0) and chose cargo+rust-cache; root .buckconfig had no buck2_re_client. Proven: openssl mTLS local OK; gh secret list showed no CF_ACCESS/OYA_CAS on console earlier this session; cold buck2 toolchains//:rust BUILD SUCCEEDED without overlay.", - "verification": "Local mTLS Verify return code 0 on :50051/:50052; lab canary documents GREEN_REAPI; HOME-isolated buck2 build toolchains//:rust BUILD SUCCEEDED; materialize-buckconfig-local.sh wrote mode-0600 .buckconfig.local (gitignored); receipt schema validate after this fix.", - "contractBreaches": "none \u2014 did not merge overlays into root .buckconfig; did not flip Required CI warm; did not rewrite ci.yml without lease", - "enforcementPlacement": "n/a - adds no enforcement", - "peripheralsUpdated": "Added DN-0006 and handoff; updated docs/decisions/README.md ADR-0039 row to cite DN-0006 superseding DN-0005 cargo-primary planning.", - "commands": [ - "curl -sf http://127.0.0.1:50061/status", - "openssl s_client -connect 127.0.0.1:50052 (reader mTLS) \u2192 Verify return code: 0", - "openssl s_client -connect 127.0.0.1:50051 (writer mTLS) \u2192 Verify return code: 0", - "HOME=/.tmp-home ./tools/buck2 build toolchains//:rust --console=simple \u2192 BUILD SUCCEEDED", - "scripts/cas/materialize-buckconfig-local.sh --role writer \u2192 mode 600 .buckconfig.local", - "node scripts/cursor/validate-lane-receipt.mjs ", - "DRY_RUN bootstrap-cas-secrets.sh console \u2192 blocked: GH_TOKEN invalid" - ], - "lenses": [ - "Essentialism / YAGNI", - "Chesterton's Fence", - "Systems Thinking", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "challenges": [ - "DN-0005 rejected remote-cache Buck path because CAS was absent; founder Buck2-everywhere + GREEN_REAPI reopens it \u2014 Wave A wires overlays only.", - "Host cloudflared access tcp was flaky; dockerized lab canary is the proven remote path." - ], - "followUps": [ - "Refresh forge auth then ~/oyatie-cas/gha/bootstrap-cas-secrets.sh console", - "Commit Wave A on lane/buck2-cas-everywhere-20260811 when orchestrator requests", - "Wave C: backend gates cargo run \u2192 buck2 run + preflight rewrite" - ], - "remainingHolds": [ - "GHA secret install pending orchestrator bootstrap", - "CI sole-writer for Waves C\u2013E", - "ADR-0039 acceptance still separate" - ] -} diff --git a/.cursor/receipts/console-8ma-cas-warm.json b/.cursor/receipts/console-8ma-cas-warm.json deleted file mode 100644 index c8192d884..000000000 --- a/.cursor/receipts/console-8ma-cas-warm.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "kind": "lane", - "lane": "console-8ma-cas-warm", - "role": "implementer", - "status": "done", - "worktree": "/Users/jasonlee/Developer/console-cas-worktree", - "branch": "ci/buck2-warm-cache-measured", - "baseSha": "8dbec91b18e4d970077b5621e8cc1add1d35d8b1", - "headSha": "627db01bb7b22bf9c26c68b09dc7815ccf5e30e9", - "ownedRoots": [ - "platforms/", - "infra/ci/buckconfig/", - "scripts/cas/", - ".github/actions/cas-warm/", - ".github/actions/cas-inrunner/", - ".github/actions/buck2-setup/", - ".github/workflows/cas-canary.yml", - "docs/decisions/notes/DN-0007-buck2-warm-cache-measured.md", - ".cursor/receipts/console-8ma-cas-warm.json" - ], - "summary": "Wave A warm cache could never work: root .buckconfig selects prelude//platforms:default, a Local-only executor, so every [buck2_re_client] key was inert; and [buck2_re_client] is daemon-startup config that --config-file cannot deliver. Adds //platforms:remote-cache, collapses four address-bearing overlays into one, emits the RE client block from the materialize script, and adds a probe with a --no-remote-cache fallback because Buck2 hard-fails when a configured CAS is unreachable. Measured cross-OS sharing to be impossible, so CI is warmed by an in-runner NativeLink seeded from actions/cache rather than by reaching the laptop.", - "filesChanged": [ - "platforms/BUCK", - "platforms/defs.bzl", - "infra/ci/buckconfig/warm-cache.buckconfig", - "infra/ci/buckconfig/warm-cache-lab-rw.buckconfig", - "infra/ci/buckconfig/warm-cache-lab-ro.buckconfig", - "infra/ci/buckconfig/warm-cache-gha-rw.buckconfig", - "infra/ci/buckconfig/warm-cache-gha-ro.buckconfig", - "scripts/cas/materialize-buckconfig-local.sh", - "scripts/cas/cas-preflight.sh", - ".github/actions/cas-warm/action.yml", - ".github/actions/cas-inrunner/action.yml", - ".github/actions/buck2-setup/action.yml", - ".github/workflows/cas-canary.yml", - "docs/decisions/notes/DN-0007-buck2-warm-cache-measured.md", - "docs/decisions/README.md", - "docs/handoffs/2026-08-11-oyatie-shared-cas.md", - "docs/documentation-index.json", - "docs/documentation-manifest.seed.json" - ], - "redBaseline": "Before the change, //backend/ci/gates/layer-boundary:console-gate-layer-boundary built with the Wave A overlay reported Commands: 97 (cached: 0, remote: 0, local: 97), Network up 0B, and the NativeLink server logged zero client connections. buck2 audit providers prelude//platforms:default showed executor: Local(LocalExecutorOptions) with no remote executor. Passing the overlay via --config-file failed with Error: (No engine address).", - "verification": "Warm: Cache hits 100%, Commands 97 (cached: 97, local: 0), 0.6s from a wiped buck-out and killed daemon. Path independence: 100% from a second checkout root. CAS unreachable: BUILD SUCCEEDED via --no-remote-cache, up 0B. Cold build without the overlay unchanged. GHA run 32207794923: seed runner 97 local/206MB store; reuse runner Cache hits 100%, 97 cached/0 local; fallback job green with cas_up=0; prune returned usage to 2.16 GiB vs 2.15 GiB baseline. Gates: check-adrs, check-doc-links, documentation-manifest --check, check-ci-preflight, check-foundation-gates, gha-cache-hygiene tests (6 pass).", - "commands": [ - "buck2 audit providers prelude//platforms:default", - "buck2 --isolation-dir nat1 build --config-file infra/ci/buckconfig/warm-cache.buckconfig //backend/ci/gates/layer-boundary:console-gate-layer-boundary", - "scripts/cas/cas-preflight.sh --endpoint 127.0.0.1:50051", - "node scripts/check-ci-preflight.mjs", - "node scripts/check-foundation-gates.mjs", - "node scripts/console/generate-documentation-manifest.mjs --check", - "gh run view 32207794923 --repo oyatie/console --log" - ], - "commandsRun": [ - "buck2 audit providers prelude//platforms:default", - "buck2 --isolation-dir nat1 build --config-file infra/ci/buckconfig/warm-cache.buckconfig //backend/ci/gates/layer-boundary:console-gate-layer-boundary", - "node scripts/check-ci-preflight.mjs", - "node scripts/check-foundation-gates.mjs" - ], - "contractBreaches": "none \u2014 root .buckconfig untouched (0 diff), ci.yml untouched, no Required context added or altered, no warm_reads license flipped, no secrets or TLS material introduced.", - "enforcementPlacement": "WHERE: .github/workflows/cas-canary.yml reuse job fails unless the build reports a non-zero, non-0% cache hit rate, so the cross-runner property is asserted rather than reported. The preflight probe in scripts/cas/cas-preflight.sh converts CAS unreachability into --no-remote-cache instead of a failed build.", - "peripheralsUpdated": "DN-0007 records the measurements and corrects DN-0006's substrate claim; docs/decisions/README.md ADR-0039 row cites it; the 2026-08-11 handoff gained a correction banner and a working example; documentation manifest and index regenerated.", - "classification": { - "personalData": false, - "holdAdjacent": true, - "notes": "Hold-adjacent, and deliberately clear of it. adjacent but clear: docs/current/PRODUCT.md holds live production, DNS, TLS, secret, exposure, payment, credential-reset and compliance-claim actions without separate authority. This candidate introduces no secret, no TLS material, no DNS record and no external exposure; the cache is loopback-only inside the runner and on the developer machine. The mesh/tunnel work that would require that authority is deliberately NOT in this candidate. none \u2014 no personal data is read, written, or logged by any added path. The grandfathered A1 hold is untouched: nothing here provisions, resizes, or reprovisions any OCI resource. OCI was read-only this session." - }, - "preMortem": "Most likely failure: someone adds warm-cache to a Required job, and a cache outage then fails merges, because Buck2 treats an unreachable configured CAS as fatal. Second: the execution platform target label or the buck2 isolation dir is renamed, silently invalidating every consumer's cache. Third: the in-runner store grows and pushes the shared Actions cache past the 8 GiB hygiene threshold, hard-failing cache-hygiene.yml.", - "blastRadius": "Bounded to opt-in callers. Root .buckconfig is unchanged, so a build that does not pass the overlay behaves exactly as before. The canary is not a Required context. The in-runner store is capped at 1 GiB and its key prefix is deliberately not a cache-hygiene KEEP_PREFIX, so the daily protocol can always reclaim it.", - "detection": "cas-canary reuse job fails on any regression that loses cross-runner hits; the fallback job fails if a cache outage stops degrading safely; cache-hygiene.yml hard-fails if the shared budget is exceeded.", - "rollback": "Revert the candidate. Because root .buckconfig was never modified, reverting restores exactly the prior cold behaviour with no cleanup; no cache state has to be drained and no Required context changes.", - "stopConditions": "Stop and escalate before: attaching warm cache to any Required CI job; introducing CAS credentials, TLS material or an external tunnel (HOLD in docs/current/PRODUCT.md); adding the cache key prefix to cache-hygiene KEEP_PREFIXES; or renaming //platforms:remote-cache or the isolation dir, which invalidates the cache fleet-wide.", - "reviewIdentities": [ - "implementer: Claude Opus 5 under jasonlee", - "inherited: seven review findings on PR #769 (Cursor Bugbot), mapped and closed or shown satisfied in af3e1a2a", - "no independent human review yet" - ], - "remainingHolds": [ - "Mesh / Headscale / Tailscale and any CI-reachable CAS remain HOLD pending separate authority per docs/current/PRODUCT.md (secret, exposure, DNS, TLS).", - "Attaching warm cache to Required CI is out of scope pending the Phase 2 CI overhaul lane.", - "Console-native ci/{core,ports,adapters,facade} capability from the #769 disposition is not implemented here." - ], - "result": "green", - "followUps": [ - "Link this candidate on PR #769 and map its retained/rejected hunks, per that PR's disposition.", - "Phase 2 CI overhaul lane owns attaching warm cache to real jobs and the main-only save policy.", - "Mesh/OCI VM work needs separate authority under the PRODUCT.md exposure/secret HOLD before any host is provisioned.", - "Console-native ci/{core,ports,adapters,facade} capability from the #769 disposition is not implemented here." - ], - "lenses": [ - "Cartesian doubt", - "Chesterton's Fence", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "Telemetry-first" - ], - "challenges": [ - "GREEN_REAPI was read as evidence of a working warm cache; it only ever proved server reachability. Measured the client side and found it never contacted the server.", - "Assumed NativeLink was amd64-only from the docs' pinned v1.3.2; v1.6.5 ships a multiarch index plus signed macOS and aarch64 binaries.", - "Assumed a mesh would let CI reuse the desktop cache; measured that macOS and Linux configuration hashes differ, so they can never share entries." - ] -} diff --git a/.cursor/receipts/cursor-worktree-layout.json b/.cursor/receipts/cursor-worktree-layout.json deleted file mode 100644 index 958d2ca03..000000000 --- a/.cursor/receipts/cursor-worktree-layout.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "status": "done", - "summary": "Root cause: sibling git worktrees under Developer/ sit outside the opened Cursor workspace and hit External-File Protection (allow-edit prompts). Fix: provision only under hub .worktrees/; tighten git-lock allowlist; add provision-lane-worktree.sh; update BASE_LOCK/ritual/agents/failure-classes.", - "filesChanged": [ - ".cursor/hooks/git-lock-enforcer.sh", - ".cursor/hooks/session-start.sh", - ".cursor/hooks.json", - ".cursor/rules/console-base-lock.mdc", - ".cursor/rules/console-agent-ritual.mdc", - ".cursor/rules/console-standing-lenses.mdc", - ".cursor/agents/lane-implementer.md", - ".cursor/agents/lane-critic.md", - ".cursor/failure-classes-2026-08-10.md", - ".cursor/PROCESS-SYNTHESIS.md", - ".cursor/permissions.json", - "scripts/cursor/provision-lane-worktree.sh", - "scripts/cursor/probe-ratchet.py", - "scripts/cursor/validate-lane-receipt.mjs", - "scripts/cursor/preflight-forge.sh", - "scripts/cursor/check-admit-sync.sh", - ".cursor/receipts/cursor-worktree-layout.json" - ], - "redBaseline": "Hostile probe matrix in probe-ratchet.py: out-of-hub sibling worktree add denied; in-repo .worktrees/ absolute and relative adds allowed; worktree remove denied.", - "verification": "python3 scripts/cursor/probe-ratchet.py all OK (wt_inrepo_abs, wt_inrepo_rel, wt_sibling_deny, wt_remove_deny). Live provision via scripts/cursor/provision-lane-worktree.sh created this worktree under hub .worktrees/.", - "contractBreaches": "none", - "enforcementPlacement": "WHERE: .cursor/hooks/git-lock-enforcer.sh on Shell before git worktree add (subject exists at command time). Fineness: path must be .worktrees/ or */.worktrees/ plus -b lane/*|admission/* origin/main. Examined-zero fails: any worktree add/remove outside that shape is deny (not allow).", - "peripheralsUpdated": "BASE_LOCK, ritual, lane-implementer, session-start, failure-classes (process.worktree-outside-workspace), PROCESS-SYNTHESIS item 11, permissions.json block/allow instructions, probe-ratchet worktree cases.", - "commands": [ - "python3 scripts/cursor/probe-ratchet.py", - "bash scripts/cursor/provision-lane-worktree.sh --kind lane cursor-worktree-layout" - ], - "headSha": "c9e8b6a2e7d5583a0b128d64b9f3da8c33e4eac6", - "followUps": [ - "User action: keep Cursor workspace root as the hub console folder so .worktrees/* is in-workspace. New lanes use .worktrees only; existing sibling worktrees grandfathered until retired.", - "Do not migrate in-flight product sibling tips mid-flight." - ] -} diff --git a/.cursor/receipts/dgo.2-critic.json b/.cursor/receipts/dgo.2-critic.json deleted file mode 100644 index 17793b6c9..000000000 --- a/.cursor/receipts/dgo.2-critic.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "verdict": "APPROVE", - "tipSha": "54a5a10842082720c59e09aae9a9c6edbb387990", - "candidateSha": "273aa5383cade4375107edd5c227bf738d4aa90c", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "lane": "console-dgo.2", - "role": "critic", - "lensesApplied": [ - "ORACLE INTEGRITY", - "PERIPHERAL DRIFT", - "ENFORCEMENT PLACEMENT", - "FALSE GREEN", - "Cartesian doubt", - "Chesterton's Fence", - "Red Team", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "findings": [ - { - "severity": "minor", - "claim": "Implementer receipt status=done lists only git/ls/validate commands \u2014 no cargo_needs_postgres / person_port_as_runtime_role execution \u2014 so the build receipt alone cannot certify the equality pin.", - "failureScenario": "A future tip that reverts person_id=employee_id while keeping prose could still validate the implementer receipt schema and look done until a critic or CI runs the itest.", - "location": ".cursor/receipts/dgo.2.json commands[]", - "provenByExecution": true, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "a_second_binding_for_the_same_employee_is_refused now proves binding uniqueness via Create(unbound)+Revise rather than a second Create; the comment that duplicate Create fails at persons_pkey is not a separate executable pin.", - "failureScenario": "A Create-path change that soft-fails or upserts persons while leaving Revise uniqueness intact would not trip this renamed scenario (equality pin would still catch random ids).", - "location": "backend/crates/ontology/canonical-adapter-postgres/tests/person_port_as_runtime_role.rs:a_second_binding_for_the_same_employee_is_refused", - "provenByExecution": false, - "ownerLease": false - } - ], - "notes": "Standing lenses on packaging tip b2718c9cb / product 273aa5383 vs base be7b3e14b. Diff is only person.rs + person_port_as_runtime_role.rs; migration tree untouched (0213 present; no 0217 added \u2014 we1 slot preserved). ORACLE: trusted equality + unbound-despite-name/phone/org pins present; 16/16 green after clean rebuild via `bash tools/ci/cargo_needs_postgres.sh --only ontology-canonical-adapter-postgres-person-port-as-runtime-role-pg --num-threads=1`. Hostile evidence: first run against stale incremental rlib (strings showed only old `INSERT INTO persons (org_id)`) failed both equality asserts; after fingerprint wipe, rlib contained `(org_id, id)` INSERT and suite passed \u2014 mutate/remove of deterministic bind goes red. No #[ignore]/ no deleted controls; second-binding rewrite preserves employee_person_bindings_pkey refuse. PERIPHERAL: module docs + query docs updated; no OpenAPI/docs/current/migrations in leaf. ENFORCEMENT: write path sets persons.id from explicit employee_id only; binding insert gated on Some(employee_id); examined-zero fails via assert_eq on person_id/employee_id and binding counts. FALSE GREEN: no --workflow-only; critic executed real Postgres itest (implementer commands gap filed minor). Red Team: no name/phone/org inference path in person.rs (only employee_id match arm); Revise further-employment person_id\u2260employee_id remains intentional. No blocker / no proven major. Challenges: (1) Chesterton \u2014 rewriting second-binding from Create+Create to Revise was required because person_id=employee_id makes duplicate Create fail at persons_pkey before bindings_pkey; kept uniqueness pin. (2) Cartesian \u2014 packaging tip headSha\u2260product tip; product commit is ancestor of packaging tip and is the reviewed leaf. | tip-bound on heal after restack onto eca122ad0; APPROVE class retained (27af243c).", - "followUps": [ - "console-e0v still owns broader L4-PORT-PE Person port proof if not closed", - "optional: add explicit duplicate-Create persons_pkey pin if Create-path refuse messaging becomes a contract", - "process: critics/CI should treat missing SQL string in rlib as stale incremental \u2014 wipe fingerprint before trusting red/green on SQL-literal changes" - ], - "commandsRun": [ - "git -C .worktrees/lane-console-dgo.2 rev-parse HEAD", - "git -C .worktrees/lane-console-dgo.2 show --stat 273aa5383cade4375107edd5c227bf738d4aa90c", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-canonical-adapter-postgres-person-port-as-runtime-role-pg --num-threads=1", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs --schema critic .worktrees/lane-console-dgo.2/.cursor/receipts/dgo.2-critic.json" - ], - "productTip": "eba06295b7a2dd2f446b5b2b8d583073151f4169", - "headSha": "54a5a10842082720c59e09aae9a9c6edbb387990", - "lineageNote": "Product eba06295b (cherry-pick 560261fcb); critic APPROVE from tip b2718c9cb lineage applies to same person_id=employee_id binding." -} diff --git a/.cursor/receipts/dgo.2.json b/.cursor/receipts/dgo.2.json deleted file mode 100644 index 46dfdba2f..000000000 --- a/.cursor/receipts/dgo.2.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "lane": "console-dgo.2", - "role": "implementer", - "status": "done", - "headSha": "273aa5383cade4375107edd5c227bf738d4aa90c", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-console-dgo.2", - "branch": "lane/console-dgo.2", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "summary": "0213 person tables already on main; added deterministic trusted binding person_id=employee_id in canonical-adapter-postgres person port + runtime-role pin. No new migration (0217 reserved by we1).", - "filesChanged": [ - "backend/crates/ontology/canonical-adapter-postgres/src/person.rs", - "backend/crates/ontology/canonical-adapter-postgres/tests/person_port_as_runtime_role.rs" - ], - "redBaseline": "Trusted uniquely-resolved employees lacked person_id=employee_id deterministic bind semantics / pin coverage on tip be7b3e14b despite 0213 tables existing.", - "verification": "Tip 273aa5383 signed G changes person.rs (+tests). Migration 0213 retained; no 0216/0217/0218 added.", - "commands": [ - "git -C .worktrees/lane-console-dgo.2 log -1 --format='%H %G? %s'", - "git -C .worktrees/lane-console-dgo.2 show --stat HEAD", - "ls backend/crates/platform/db/migrations/0213_create_canonical_person_tables.sql", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs .worktrees/lane-console-dgo.2/.cursor/receipts/dgo.2.json" - ], - "contractBreaches": "Binding uses employee_id equality only; no name/phone/org inference. Does not implement L4-PORT-PE (console-e0v).", - "enforcementPlacement": "WHERE: person port adapter + person_port_as_runtime_role itest (subject exists). Finest: trusted bind path sets person_id=employee_id. Examined-zero fails: runtime-role test asserts bind outcome.", - "peripheralsUpdated": "person_port_as_runtime_role.rs pin extended; no docs/current; no OpenAPI.", - "followUps": [ - "console-e0v L4-PORT-PE may still own broader Person port proof", - "critic not yet run on 273aa5383" - ], - "lensesApplied": [ - "Cartesian doubt", - "Essentialism / YAGNI", - "Chesterton's Fence", - "Red Team", - "Blast-radius / cell-based" - ], - "migrationUsed": "none \u2014 0213 already present", - "completedBy": "admit-coordinator parent after stalled implementer 8eb77d64 (product tip already signed+pushed)" -} diff --git a/.cursor/receipts/g14a.json b/.cursor/receipts/g14a.json deleted file mode 100644 index bf206df2e..000000000 --- a/.cursor/receipts/g14a.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "status": "done", - "laneId": "console-g14a", - "bead": "console-g14a (P2): prototype-chain census \u2014 mechanical fail-closed scan for untrusted-keyed lookups under scripts/", - "headSha": "f7b49495dad62bc2ea7781cb4dff92683b091fd2", - "branch": "lane/console-g14a", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-console-g14a", - "summary": "Restacked lane/console-g14a onto origin/main 84655ce7881c764bbe81081b193cdd2d85d523c1 after admit parked the leaf for census red (14 stale + unknown). Kept the total primitive (check-prototype-chain-lookups + examined-zero missingSubjects fail-closed + plan-fanout eligibility Object.hasOwn). One inventory commit shrinks scripts/prototype-chain-lookup-baseline.json from 18\u21925 residuals: drops 14 sites cleared by console-i91 (#742) and rebinds the line-shifted verify-console-pr-authority-bootstrap residual (53\u219254). No spelling-list call-site sweep; no #622 admit from this lane.", - "filesChanged": [ - "scripts/check-prototype-chain-lookups.mjs", - "scripts/check-prototype-chain-lookups.test.mjs", - "scripts/prototype-chain-lookup-baseline.json", - "scripts/console/plan-fanout.mjs", - "package.json", - ".cursor/receipts/g14a.json" - ], - "redBaseline": "After rebase onto main (pre-register shrink): node scripts/check-prototype-chain-lookups.mjs \u2192 exit 1 scanned=53 findings=5 unknown=1 stale=14 (matches admit park note). Fixture RED retained: optional-computed + undefined-compare \u2192 unknown findings; missing-subject PROOF_A wouldCliFail=true.", - "verification": "GREEN post-shrink: (a) node --test scripts/check-prototype-chain-lookups.test.mjs \u2192 10/10; (b) node scripts/check-prototype-chain-lookups.mjs \u2192 pass scanned 53 residuals 5 findings 5; (c) node --test scripts/console/plan-fanout.test.mjs \u2192 29/29; (d) signing smoke commit-tree -S \u2192 %G?=G.", - "commands": [ - "bash /Users/jasonlee/Developer/console/scripts/cursor/provision-lane-worktree.sh --kind lane console-g14a", - "git fetch origin main && git rebase origin/main", - "node scripts/check-prototype-chain-lookups.mjs (pre-shrink) \u2192 exit 1 unknown=1 stale=14", - "node --test scripts/check-prototype-chain-lookups.test.mjs \u2192 10/10", - "node scripts/check-prototype-chain-lookups.mjs \u2192 pass scanned 53 residuals 5", - "node --test scripts/console/plan-fanout.test.mjs \u2192 29/29", - "echo 'g14a signing smoke' | git commit-tree HEAD^{tree} -S -p HEAD -m 'g14a signing smoke' \u2192 %G?=G", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs .cursor/receipts/g14a.json" - ], - "contractBreaches": "none", - "enforcementPlacement": "WHERE: scripts/check-prototype-chain-lookups.mjs runs in CI via package.json check:request-body-contract (repo-gates already executes that npm script). SUBJECT EXISTS: git-ls-files subjects under scripts/check-*.mjs + scripts/console/** at gate runtime; each listed path must exist on disk or missingSubjects fails closed. FINEST DISTINCTION: scanned = files successfully read (not declared list length); findingId is file|line|kind|key|snippet; residual register is id-match not a key spelling list. EXAMINED-ZERO FAILS: missingSubjects.length > 0 OR scanned < SCANNED_FLOOR (45) exits 1; unknown findings and stale residuals also exit 1.", - "peripheralsUpdated": "scripts/prototype-chain-lookup-baseline.json: shrunk to 5 post-i91 residuals; notes document restack shrink + line shift. package.json already wires check:prototype-chain-lookups into check:request-body-contract (unchanged this commit). plan-fanout eligibility Object.hasOwn retained from prior lane commits. Did not edit docs/CI-GATES.md (CI entry name unchanged).", - "lensesApplied": { - "cartesianDoubt": "Separated restack inventory (stale register vs main after i91) from call-site sweep; admit park note was evidence not opinion.", - "essentialism": "One register shrink commit \u2014 no third spelling-list patch round on the 5 remaining residuals.", - "chesterton": "Kept residual register + SCANNED_FLOOR + missingSubjects; i91 cleared sites must leave the register (stale-fail) rather than force empty-register or re-open i91.", - "redTeam": "Pre-shrink RED (14 stale + 1 unknown) re-proven on rebased tip; hostile fixture RED controls retained.", - "blastRadius": "Owned scripts/ census surface + plan-fanout eligibility already in lane range; no allowlist growth.", - "zeroTrust": "Do not trust pre-i91 residual list as examination of current main." - }, - "claims": { - "E-observed": [ - "Pre-shrink census exit 1 unknown=1 stale=14 on rebased tip", - "Post-shrink census pass scanned 53 residuals 5; tests 10/10; plan-fanout 29/29", - "Signing smoke commit-tree -S \u2192 G" - ], - "I-inferred": [ - "Five remaining residuals are intentional post-i91 holdouts (env/SAFE allowlists + resourceBudgets + ledger area), not new fail-opens introduced by this lane" - ], - "U-uncertain": [ - "Whether process.env[key] / SAFE_ENVIRONMENT_KEYS residuals should later be carved into a separate trusted-env class" - ] - }, - "challenges": [ - "Chesterton on keeping a non-empty residual register after i91: empty register would force a spelling-list sweep the bead forbids; shrink-to-observed is the fail-closed total primitive.", - "Contrarian on re-fixing the 5 call sites here: that recreates the i91 treadmill; census + named residuals is the mechanism this bead owns.", - "Admit park (14 stale + unknown) was the load-bearing challenge to the prior APPROVE tip \u2014 restack+shrink closes it without re-opening examined-zero." - ], - "followUps": [ - "Babysit/orchestrator owns CI after push; implementer exits.", - "Optional later: shrink the 5 residuals via own()/Object.hasOwn on a dedicated sweep bead (not this lane).", - "Prior: console-m9fa sibling scripts/* existsSync-skip sweep remains separate (closed on its own path).", - "bd close console-g14a on merge; hub .beads is shared." - ], - "flowDelta": "leaf ready to admit: census green on main restack tip with 5 residuals; plan-fanout eligibility Object.hasOwn retained; no train admit from this lane" -} diff --git a/.cursor/receipts/i91.json b/.cursor/receipts/i91.json deleted file mode 100644 index bd91f2c46..000000000 --- a/.cursor/receipts/i91.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "status": "done", - "laneId": "console-i91", - "bead": "console-i91 (P2): sweep scripts/check-*.mjs for prototype-chain false-resolve on untrusted-keyed lookups", - "headSha": "188b82e5488984945a804c119ef8c4f50964c0ac", - "branch": "lane/console-i91", - "summary": "Class sweep remainder after ann's openapi-refs Object.hasOwn fix landed in train-2 (#734). Shared total primitive scripts/own-property.mjs (own/hasOwnKey/PROTOTYPE_CHAIN_KEYS) plus RED regression in the already-wired request-body suite. Fixed untrusted-keyed lookups across request-body-contract (schemas?.[name] and properties[field] !== undefined \u2014 proven fail-open), node-audit-exceptions lock.packages[path], production-authority-blocked `key in value`, ci-preflight jobs/on maps, production-hardening scripts?.[name], g007 matrix?.[key], and console plan-fanout/validate-console-truth-ledger facts/receipts/state. Did not re-edit check-openapi-refs.mjs (already fixed on main).", - "filesChanged": [ - "scripts/own-property.mjs", - "scripts/check-request-body-contract.mjs", - "scripts/check-request-body-contract.test.mjs", - "scripts/check-node-audit-exceptions.mjs", - "scripts/check-production-authority-blocked.mjs", - "scripts/check-ci-preflight.mjs", - "scripts/check-production-hardening.mjs", - "scripts/check-g007-collaboration-mobile-lifecycle.mjs", - "scripts/console/plan-fanout.mjs", - "scripts/console/validate-console-truth-ledger.mjs", - ".cursor/receipts/i91.json" - ], - "redBaseline": "Keyed to origin/main tip 9acd784f534bb25b0468139be8f9acb13bd8fbd8. Hostile probe: widget fixture with rust field `constructor` renamed to quantityConsumedMilli, empty required[], properties only quantityConsumedMilli \u2014 evaluateRequestBodyContract returned findings.length===0 (FAIL-OPEN) because schema.properties['constructor'] !== undefined via Object.prototype. Differential: plain index on {} still sees constructor/toString/__proto__/valueOf/hasOwnProperty; own()/hasOwnKey return undefined/false. Also schemas?.[constructor] typed function before the jsonRequestSchema own-property fix.", - "verification": "Product tip 188b82e5488984945a804c119ef8c4f50964c0ac (lane HEAD). PRODUCT TIP (lane HEAD after this receipt): see git rev-parse on lane/console-i91. GREEN in worktree /Users/jasonlee/Developer/console-lane-i91: (a) node --test scripts/check-request-body-contract.test.mjs \u2192 26/26 pass (was 22; +4 prototype-chain regressions); (b) node scripts/check-request-body-contract.mjs \u2192 resolved 51, skipped 171; (c) node --test scripts/check-openapi-refs.test.mjs \u2192 15/15; node scripts/check-openapi-refs.mjs \u2192 4964/27/0; (d) node --test scripts/check-node-audit-exceptions.test.mjs \u2192 5/5; (e) node --test scripts/console/plan-fanout.test.mjs \u2192 29/29; (f) node --test scripts/console/validate-console-truth-ledger.test.mjs \u2192 25/25; (g) node --test scripts/check-production-authority-blocked.test.mjs \u2192 33/33. Signing smoke via orphan commit-tree -S \u2192 %G?=G before product commit. package.json not edited (outside owned roots) \u2014 shared regressions ride the already-wired check-request-body-contract.test.mjs surface.", - "commands": [ - "git -C /Users/jasonlee/Developer/console worktree add /Users/jasonlee/Developer/console-lane-i91 -b lane/console-i91 origin/main", - "echo 'i91 signing smoke' | git commit-tree HEAD^{tree} -S -p HEAD -m 'i91 signing smoke' && git log --show-signature --format='%H %G?' -1 ", - "node --input-type=module <21 removed slack rather than adding an oracle.", - "failureScenario": "Relaxing items back to bare {type:'array'} leaves the suite green at 22/22 and silently restores the blank-deferral false green R2 identified. FILED per convergence doctrine (unproven major, not a merge bar; fixing here bumps FLOOR and re-opens the thread-2 binding recursion): consolidation lane.", - "location": "scripts/console/lane-receipt.schema.json remainingHolds/followUps items (no covering test)" - }, - { - "severity": "minor", - "provenByExecution": true, - "ownerLease": false, - "claim": "R3-3: the R2 APPROVE at ac6f2a4de was bound to a CI-red tip: 88dcbc845 added the ci.yml step while scripts/verify.mjs PLAN gained the classifying line only in 7eacfed91, so the Required 'Local CI mirror contract' threw across 88dcbc845..a88fc1ed4. Two gates over one file with disjoint subjects (check-ci-preflight pins step names/positions; verify.test.mjs pins PLAN coverage).", - "failureScenario": "Already materialised and FIXED at 21fbc60bc by 7eacfed91. Residue landed in this candidate: commandsRun/verification now include node --test scripts/verify.test.mjs; SSOT line elevated via lane receipt followUps.", - "location": "scripts/verify.mjs PLAN; scripts/verify.test.mjs; lane receipt commandsRun" - }, - { - "severity": "minor", - "provenByExecution": false, - "ownerLease": false, - "claim": "R3-4: the exact deepEqual pins also red on strictly-additive BUILD_SCHEMA/REVIEW_SCHEMA required-field growth across the leased .claude/** boundary, not only on the substitution/removal drift they target.", - "failureScenario": "The consolidation lane adds a required field under .claude/** and test:lane-receipt reds under scripts/console/** with no announced co-edit. Accepted design (exact pins are the repo idiom, cf. 29/122/366); co-edit requirement recorded in lane receipt followUps; both pins die with the parity mechanism.", - "location": "scripts/console/validate-lane-receipt.test.mjs parity pins vs .claude/workflows/lane-fanout.js" - }, - { - "severity": "minor", - "provenByExecution": false, - "ownerLease": false, - "claim": "R3-5: same-line residue of the closed class: in --dir mode a .json file whose top level is an array/null/scalar is silently skipped while the positional path fails it; the .json extension filter hides .jsonc/.JSON/extensionless receipts.", - "failureScenario": "A receipt-shaped artifact that is not a JSON object or uses another extension is validated by nothing while the scan reports examined>=1 and exits 0. FILED, folded with Codex thread-3: one mechanism (git ls-files-driven tracked-blob enumeration with a total predicate) closes both; no round 3 on the same line.", - "location": "scripts/console/validate-lane-receipt.mjs scanDir entry filter and skip predicate" - }, - { - "severity": "minor", - "provenByExecution": false, - "ownerLease": true, - "claim": "R3-6: kind token vocabulary diverges across the two tracked validators (tracked schema: lane|critic; incumbent cursor validator: build|critic via --schema, ignoring any kind property) and no doc under .cursor/**, .claude/**, or docs/** names kind at all; 21fbc60bc makes the divergence CI-load-bearing.", - "failureScenario": "An author copies the incumbent token as kind:'build'; the stop-receipt-gate hook blesses the file and console preflight reds it with no doc naming the correct token. Benign at this tip (2 of 45 tracked receipts carry kind, both valid). Leased to the consolidation lane; legacy migration of the 30 kind-less receipts is strictly atomic per receipt.", - "location": "scripts/console/lane-receipt.schema.json vs scripts/cursor/validate-lane-receipt.mjs; .cursor/agents/*; .cursor/hooks/*" - } - ] -} diff --git a/.cursor/receipts/v-lane-receipt-validator-20260812-critic.json b/.cursor/receipts/v-lane-receipt-validator-20260812-critic.json deleted file mode 100644 index c4b5b1adb..000000000 --- a/.cursor/receipts/v-lane-receipt-validator-20260812-critic.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "kind": "critic", - "verdict": "APPROVE", - "reviewer": "architect R2-VLaneValidator (Claude Opus, cross-family; executor was Grok)", - "reviewedRange": "b2acd80c4d7f340199b9147f6df9318d74af5f8d..ac6f2a4dee3aaab0101d0e0f2af7a378dde9f7fb", - "rounds": "R1 BLOCK at 55331bc27 (1 blocker proven: third tracked spelling of the receipt field list dropping CI-pinned BUILD_SCHEMA fields; 3 major; 5 minor). One consolidated fix round (9595fc64a + ac6f2a4de). R2 delta re-critique: APPROVE. R3 delta pass (1-R3-VLaneDelta, Opus): APPROVE bound to final product tip 21fbc60bc covering ac6f2a4de..21fbc60bc; see -critic-r3.json.", - "findings": [ - { - "severity": "major", - "claim": "scanDir skip predicate conflates 'kind absent' with 'kind unrecognised': a receipt opting in with a malformed discriminator (e.g. kind:'build' or kind:'Lane') is silently skipped and validated by nothing; examined-zero cannot catch it while this lane's receipt keeps examined >= 1. Total form: skip only when the kind property is ABSENT; present-but-unrecognised must be a defect.", - "failureScenario": "A future lane copies the only kind-bearing receipt as template with kind:'build', omits redBaseline and stopConditions; test:lane-receipt and check:lane-receipt both exit 0; the invalid receipt is never seen by any validator. Originally routed to the consolidation lane as unproven. POST-VERDICT: proven by a Codex review probe and reproduced by the leader (exit 0 pre-fix); CLOSED in 21fbc60bc (skip keys on Object.hasOwn(receipt,\"kind\"); planted-red test added, FLOOR 22). provenByExecution stays false here because it records the R2 verdict-time state; the proof and fix are bound in -critic-r3.json.", - "location": "scripts/console/validate-lane-receipt.mjs scanDir skip predicate", - "provenByExecution": false, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "Same-class residue of R1 finding 8 inside the rebind commit: summary said '8-test suite' and verification said '8/8 pass' while the suite at the bound headSha has 21 tests observed 21/21; reviewIdentities cited a critic-receipt path that did not exist in the tree at review time.", - "failureScenario": "An independent verifier re-running the verification field observes 21/21 against a claimed 8/8 and flags contradictsClaim; the cited critic path resolves to nothing for auditors. FIXED pre-push in the same closure commit that adds this file: numbers corrected, this receipt created at the cited path.", - "location": ".cursor/receipts/v-lane-receipt-validator-20260812.json summary/verification/reviewIdentities", - "provenByExecution": true, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "Suite floor FLOOR=18 has 3 units of slack against 21 test bodies; a ratchet below the current count is not a ratchet — deleting the two R1-blocker controls plus one more still passes.", - "failureScenario": "Deletion of the parity and dual-validation tests plus any one more leaves executed=18, after() passes, CI green with the field-list parity pin gone. FIXED pre-push: FLOOR raised to 21 (pin-to-current-count discipline, same as the 29/122/366 preflight pins).", - "location": "scripts/console/validate-lane-receipt.test.mjs FLOOR", - "provenByExecution": false, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "remainingHolds and followUps are bare {type:array} so [\"\"] and [\" \"] validate — the same blank-string false green this delta closed on commandsRun, on exactly the fields that carry deferral records.", - "failureScenario": "A hold or leased-edit record rendered as a blank bullet validates and the outstanding item is silently lost. FIXED pre-push: items constrained to nonEmptyString with [] still legal (no minItems).", - "location": "scripts/console/lane-receipt.schema.json remainingHolds/followUps", - "provenByExecution": false, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "Two mandatory, uncompared command lists in every done lane receipt: commandsRun is schema-required while the ported incumbent-parity rule mandates commands for status=done; nothing derives one from the other. Same class as the resolved third-spelling blocker, at field granularity.", - "failureScenario": "commands:['true'] + honest commandsRun both validate; a verifier keyed on commands re-runs 'true' and certifies coverage of nothing. Routed to the consolidation lane followUp: converge the command vocabulary when the incumbent retires.", - "location": "scripts/console/lane-receipt.schema.json commandsRun vs validator status=done commands rule", - "provenByExecution": false, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "check:lane-receipt has zero callers repo-wide (only its own definition); the CI-enforced scan lives inside the test suite instead. Also --schema is silently discarded in --dir mode.", - "failureScenario": "Not a false green (the suite's real-directory scan is CI-wired and proves examined >= 1); the defect is a second unexecuted spelling of a running check, and an option accepted but not read. Routed to the consolidation lane followUp.", - "location": "package.json check:lane-receipt; validate-lane-receipt.mjs parseArgs/--dir branch", - "provenByExecution": false, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "Peripheral drift, correctly leased: agent cards, hooks, and PROCESS-SYNTHESIS.md still teach the incumbent 8-field contract and validator path; none mention kind or the ritual fields the CI-wired scan now enforces on kind-bearing receipts.", - "failureScenario": "A lane author following the agent card writes a kind-bearing receipt missing stopConditions and goes red in test:lane-receipt on a field no doc mentions. Leased (.cursor/** and .claude/**); already recorded in lane receipt followUps for the consolidation lane.", - "location": ".cursor/agents/lane-implementer.md; .cursor/agents/lane-critic.md; .cursor/hooks/stop-receipt-gate.sh; .cursor/hooks/session-start.sh; .cursor/PROCESS-SYNTHESIS.md", - "provenByExecution": false, - "ownerLease": true - }, - { - "severity": "minor", - "claim": "oneOf failure path surfaces only branchFailures[0], so every peripheralsUpdated defect reports as the string branch's error text; fail-closed but misleading diagnostics.", - "failureScenario": "peripheralsUpdated: [] reports 'must be string' instead of 'minItems 1'; an author converts a correct array into a string rather than fixing the actual defect. Routed to the consolidation lane followUp.", - "location": "scripts/console/validate-lane-receipt.mjs oneOf branch reporting", - "provenByExecution": false, - "ownerLease": false - }, - { - "severity": "minor", - "claim": "Pre-existing on origin/main at base: docs/CI-GATES.md broken citation (~line 902, migration 0600 .xctestrun) and unverifiable line refs; CI-GATES.md outside wired check:doc-citations scope despite being a declared foundation-gates documentInput. Untouched by this lane.", - "failureScenario": "Citation rot accumulates in the document the docs-drift gate reads. Leased; routed to W1.1 T1 custody lane via lane receipt followUps[0].", - "location": "docs/CI-GATES.md ~902; package.json check:doc-citations scope", - "provenByExecution": false, - "ownerLease": true - } - ] -} diff --git a/.cursor/receipts/v-lane-receipt-validator-20260812.json b/.cursor/receipts/v-lane-receipt-validator-20260812.json deleted file mode 100644 index 9d6b6a07c..000000000 --- a/.cursor/receipts/v-lane-receipt-validator-20260812.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "kind": "lane", - "lane": "v-lane-receipt-validator-20260812", - "status": "done", - "summary": "Tracked lane-receipt schema (scripts/console/lane-receipt.schema.json), dependency-free validator CLI (scripts/console/validate-lane-receipt.mjs), 22-test node:test suite (incl. exact cross-authority parity pins, dual-validation, and --dir examined-zero scans), wired into package.json scripts, ci.yml preflight step, check-ci-preflight proofRun + pins, CI-GATES.md inventory, and regenerated doc manifests.", - "filesChanged": [ - "scripts/console/lane-receipt.schema.json", - "scripts/console/validate-lane-receipt.mjs", - "scripts/console/validate-lane-receipt.test.mjs", - "package.json", - ".github/workflows/ci.yml", - "scripts/check-ci-preflight.mjs", - "scripts/check-ci-preflight.test.mjs", - "docs/CI-GATES.md", - "docs/documentation-manifest.seed.json", - "docs/documentation-index.json", - ".cursor/receipts/v-lane-receipt-validator-20260812.json", - "scripts/verify.mjs", - ".cursor/receipts/v-lane-receipt-validator-20260812-critic.json", - ".cursor/receipts/v-lane-receipt-validator-20260812-critic-r3.json" - ], - "redBaseline": "Planted-red proofs executed and pasted in test suite development: schema-violating receipt (missing required field) exits 1; examined-zero (no-arg invocation) exits 1; foundation-gates docs-drift gate observed RED when test:lane-receipt was undocumented in CI-GATES.md, then GREEN after inventory entry (bisect: clean b2acd80c 6/6 PASS, dbb5443 6/6 PASS, wiring-without-doc FAIL). Post-R2 planted reds: present-but-unrecognised kind ({\"kind\":\"build\"}) in --dir scan observed exit 0 pre-fix, exit 1 post-fix; BUILD_SCHEMA required-list mutation (verification->headSha) observed suite red with drift message, restore green.", - "verification": "npm run test:lane-receipt 22/22 pass; npm run check:lane-receipt (--dir scan of .cursor/receipts) exit 0; node scripts/check-ci-preflight.mjs contract passed; node --test scripts/check-ci-preflight.test.mjs 57/57 pass; js-test-reachability 47 suites exact-wired dark 0; check-mjs-dark-suites --strict ok; foundation-gates 6/6 pass; generate-documentation-manifest --check OK (438 markdown files); node --test scripts/verify.test.mjs 13/13 pass; porcelain clean at tip.", - "contractBreaches": "none", - "headSha": "21fbc60bc3ca5b1e4f21f0d9566bf2c5c0f520a6", - "baseSha": "b2acd80c4d7f340199b9147f6df9318d74af5f8d", - "worktree": ".worktrees/v-lane-validator", - "commands": [ - "npm run test:lane-receipt", - "node scripts/check-ci-preflight.mjs", - "node --test scripts/check-ci-preflight.test.mjs", - "node scripts/check-js-test-reachability.mjs", - "node --test scripts/check-foundation-gates.test.mjs" - ], - "commandsRun": [ - "npm run test:lane-receipt", - "node --test scripts/console/validate-lane-receipt.test.mjs", - "node scripts/check-ci-preflight.mjs", - "node --test scripts/check-ci-preflight.test.mjs", - "node scripts/check-js-test-reachability.mjs", - "node tools/ci/check-mjs-dark-suites.mjs --strict", - "node --test scripts/check-foundation-gates.test.mjs", - "node scripts/console/generate-documentation-manifest.mjs --write", - "node scripts/console/generate-documentation-manifest.mjs --check", - "node --test scripts/verify.test.mjs" - ], - "enforcementPlacement": "WHERE: ci.yml preflight job step 'Console lane-receipt validator regression' runs npm run test:lane-receipt; the suite executes the validator against synthetic fixtures AND scans the real tracked .cursor/receipts directory via --dir, validating every kind-bearing receipt. SUBJECT EXISTS: this lane's receipt is the first kind-bearing tracked receipt; receipts with NO kind property remain under the incumbent validators (a present-but-unrecognised kind FAILS, planted-red proven) (scripts/cursor/validate-lane-receipt.mjs, lane-fanout BUILD_SCHEMA) until the consolidation lane migrates them — the boundary discriminator is the kind field, not a spelling list. FINEST DISTINCTION: per-field JSON Schema 2020-12 validation (required-field union with BUILD_SCHEMA, enum kinds, 40-hex SHA pattern, non-blank strings via minLength+\\S pattern, minItems) plus conditional rules (n/a escape parity, status=done commands, APPROVE-over-blocking-finding conflict). EXAMINED-ZERO FAILS: zero-argument invocation exits 1; --dir scan finding zero kind-bearing receipts exits 1; both proven by planted-red tests; the suite carries an executed floor (after-hook fails under 22 test bodies) and a cross-authority parity test that fails if BUILD_SCHEMA/REVIEW_SCHEMA required fields stop being extractable.", - "peripheralsUpdated": "docs/CI-GATES.md: test:lane-receipt added to 'Root package scripts run by CI' inventory (foundation-gates drift parity); docs/documentation-manifest.seed.json + docs/documentation-index.json regenerated via generate-documentation-manifest.mjs --write; scripts/check-ci-preflight.test.mjs pins updated: preflight run-step 28->29, total 121->122, mutation matrix 363->366; scripts/verify.mjs PLAN classifies the new step (local CI mirror contract)", - "classification": { - "personalData": false, - "holdAdjacent": false, - "notes": "Validator tooling only; no product, contract, migration, HR, or payroll surface touched. Ordinary R2 review path, no GAAC." - }, - "preMortem": "Risk 1: a third mutually-unsatisfiable spelling of the receipt field list forks authority -> mitigated post-R2: laneReceipt.required is the UNION of CI-pinned BUILD_SCHEMA required fields and the agent-ritual fields, an executed parity test pins BUILD_SCHEMA.required as a subset of laneReceipt.required (and REVIEW_SCHEMA of criticReceipt), and this receipt validates under both the tracked and incumbent validators by an executed test. Risk 2: preflight pin drift causes false red on unrelated PRs -> pins co-edited with mutation inventory in same commit, contract test green. Risk 3: undocumented CI step trips foundation-gates docs-drift parity -> hit during development, fixed by CI-GATES.md inventory entry (designed gate worked). Risk 4: validator certifies doctrine-forbidden receipts -> APPROVE-over-blocking-finding and whitespace false-green rules ported from the incumbent with planted-red tests.", - "blastRadius": "Additive: new schema+validator+test under scripts/console/, two package.json scripts, one CI preflight step, one proofRun, doc inventory line, regenerated manifests. No existing gate, workflow, or incumbent validator modified. Post-R2 the successor carries the incumbent's convergence rules (APPROVE-conflict, blank-string guard, n/a escape prefix, status=done commands) with planted-red proofs, so migration to it does not weaken any oracle; the incumbents remain live and untouched until the consolidation lane.", - "detection": "CI preflight step fails on validator regression; check-ci-preflight.mjs proofRun fails if step removed or reordered; foundation-gates docs-drift gate fails if CI-GATES.md inventory diverges from ci.yml; js-test-reachability fails if suite goes dark.", - "rollback": "Single revert of the squash-merge commit restores prior state; no data, migration, or hosted-state coupling.", - "stopConditions": "behind>0 at pre-admission revalidation without clean restack; any required check red on PR; R2 BLOCK with proven finding; foundation-gates or preflight contract red at tip.", - "reviewIdentities": [ - "executor: 29-VLaneValidator (Grok 4.6) authored dbb544347", - "leader: GJC sessions 019ff78e/019ff867 (Fable) authored wiring 88dcbc845, this receipt, and the R2 fix round", - "R2: architect R2-VLaneValidator (Opus, cross-family, read-only). Round 1: BLOCK at 55331bc27 (1 blocker proven: third field-list spelling; 3 major; 5 minor; 6 challenges). One consolidated fix round (9595fc64a + ac6f2a4de). Round 2 delta re-critique: APPROVE with filed findings (1 unproven major routed to consolidation lane). Verdict receipt: .cursor/receipts/v-lane-receipt-validator-20260812-critic.json", - "R3: architect 1-R3-VLaneDelta (Opus, cross-family, read-only) delta re-critique ac6f2a4de..21fbc60bc bound to final product tip 21fbc60bc: APPROVE; 1 unproven major + 4 minors filed, 1 proven minor (stale receipt claims) landed in the candidate commit; per-thread CONCUR on all four Codex dispositions. Verdict receipt: .cursor/receipts/v-lane-receipt-validator-20260812-critic-r3.json" - ], - "remainingHolds": [], - "result": "green", - "followUps": [ - "Pre-existing on origin/main: docs/CI-GATES.md has 1 broken citation (line ~902, migration 0600 .xctestrun) and 2 unverifiable line-number citations; CI-GATES.md is not in wired check:doc-citations scope despite being a declared documentInputs entry of check:foundation-gates; route to W1.1 T1 custody lane", - "CONSOLIDATION LANE (third-spelling closure): scripts/cursor/validate-lane-receipt.mjs is TRACKED since a762d1d6c (#738), and .claude/workflows/lane-fanout.js BUILD_SCHEMA/REVIEW_SCHEMA is CI-pinned by lane-fanout.test.mjs (preflight step 28). Three authorities must collapse to ONE survivor: this schema JSON, with the cursor validator and lane-fanout deriving from it. Owned roots .cursor/** and .claude/** are leased — needs its own lane. Includes: migrate 30 legacy kind-less receipts (add kind), update .cursor/PROCESS-SYNTHESIS.md:24 row mapping schema-required fields to BUILD_SCHEMA, update agent-card/hook validation commands, and decide critic-branch tip binding (headSha on critic receipts) — deliberately NOT added here to avoid diverging from all three incumbent authorities (Chesterton)", - "Interim parity is pinned by execution: BUILD_SCHEMA.required subset-of laneReceipt.required and REVIEW_SCHEMA.required subset-of criticReceipt.required tests fail on drift in either direction, and this receipt is dual-validated by an executed test", - "R2-APPROVE filed findings, updated post-R3: scanDir kind predicate CLOSED in 21fbc60bc (present-but-unrecognised kind fails, planted-red); same-size parity drift CLOSED in 21fbc60bc (exact deepEqual pins; co-edit requirement: a strictly-additive BUILD_SCHEMA/REVIEW_SCHEMA required-field change under the leased .claude/** root reds test:lane-receipt under scripts/console/** — announce co-edits; both pins die with the parity mechanism at consolidation); STILL OPEN for the consolidation lane: converge the commands/commandsRun vocabulary; report the closest oneOf branch failure; give check:lane-receipt a caller or fold it into the suite; reject --schema combined with --dir", - "R3 filed findings (see critic-r3 receipt): R3-2 major unproven - schema remainingHolds/followUps nonEmptyString tightening shipped without a planted-red control (relaxing it back leaves the suite green); R3-5 - in --dir mode a non-object JSON top level or a non-.json extension is silently skipped (fold with Codex thread-3 tracked-blob enumeration: drive scanDir from git ls-files); R3-6 - kind token vocabulary diverges between the tracked schema (lane|critic) and the incumbent cursor validator (build|critic) with zero docs naming kind: legacy migration of the 30 kind-less receipts is strictly atomic per receipt", - "SSOT elevation (R3-3 residue + C2): any ci.yml edit must run BOTH node scripts/check-ci-preflight.mjs(+test) AND node --test scripts/verify.test.mjs (two gates, one file, disjoint subjects); generate-documentation-manifest --check must be asserted at T never at C (C pre-registers a blob_sha untracked until T, so --check at C fails by design)" - ] -} diff --git a/.cursor/receipts/w2-pilot-t2-g004-20260813-critic-final.json b/.cursor/receipts/w2-pilot-t2-g004-20260813-critic-final.json deleted file mode 100644 index 2aab90186..000000000 --- a/.cursor/receipts/w2-pilot-t2-g004-20260813-critic-final.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "kind": "critic", - "verdict": "APPROVE", - "lane": "w2-pilot-t2-g004-20260813", - "reviewer": "23-W2PilotCommitCritic (Sol, independent exact-product review)", - "baseSha": "d29be0d73d5d4f6360f8693fc9e0b9e36aef8485", - "headSha": "8f20b2024df15fa45253baafb101b9ad36c1d55f", - "summary": "Admission product tree equals independently reviewed f6ca71441 tree 8342bd83d2bd2c5af4677680884e29513752d3e3; no blocking finding.", - "findings": [ - { - "severity": "minor", - "claim": "The hostile-mutation test dynamically imports the executable G004 gate module and executes its top-level body.", - "failureScenario": "A future unrelated gate failure could terminate before predicate assertions or add process-global trace noise. Current behavior stays fail-closed and this is unproven.", - "location": "scripts/check-gate-input-provenance.test.mjs hostile import; scripts/check-g004-identity-foundation.mjs top-level body", - "provenByExecution": false, - "ownerLease": true - } - ], - "verificationNote": "Critic review bound to reviewed product f6ca71441; admission product 8f20b2024 has identical tree. Parent evidence 498, 9/9, census 19/18/793/18, diff check, signed commits.", - "flowDelta": "Exact admission product tree independently approved; ready for protected C/T train." -} diff --git a/.cursor/receipts/w2-pilot-t2-g004-20260813.json b/.cursor/receipts/w2-pilot-t2-g004-20260813.json deleted file mode 100644 index 7a6046a1a..000000000 --- a/.cursor/receipts/w2-pilot-t2-g004-20260813.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "kind": "lane", - "lane": "w2-pilot-t2-g004-20260813", - "status": "done", - "summary": "W2 width-one T2 pilot replaces historical G004 prose assertions with structural predicates over the existing matrix, removes the exact provenance exception, ratchets baseline_count to 18, and mutation-proves passkey/policy controls. Admission product 8f20b2024 has tree identity with independently reviewed f6ca71441.", - "filesChanged": [ - "scripts/check-g004-identity-foundation.mjs", - "scripts/check-gate-input-provenance.test.mjs", - "docs/program/gate-input-exceptions.json" - ], - "redBaseline": "Legacy clean temporary copy with foundation-gates.md stripped of passkey/policy exited 1. Hostile in-memory removal of passkey or policy tokens makes the corresponding production predicate false.", - "verification": "G004 498 checks; provenance 9/9; live census 19 rows / 18 counted / 793 assertions / 18 exceptions; diff check; manifest check 440 files; lens contract structural evidence_blocks=59; lane and critic receipts valid.", - "contractBreaches": "none", - "headSha": "8f20b2024df15fa45253baafb101b9ad36c1d55f", - "baseSha": "d29be0d73d5d4f6360f8693fc9e0b9e36aef8485", - "worktree": ".worktrees/admission-w2-pilot-t2-g004-20260813", - "commands": [ - "node scripts/check-g004-identity-foundation.mjs", - "node --test scripts/check-gate-input-provenance.test.mjs", - "node scripts/check-gate-input-provenance.mjs", - "node scripts/console/generate-documentation-manifest.mjs --check", - "node scripts/check-reasoning-lens-contract.mjs", - "git diff --check" - ], - "commandsRun": [ - "node scripts/check-g004-identity-foundation.mjs", - "node --test scripts/check-gate-input-provenance.test.mjs", - "node scripts/check-gate-input-provenance.mjs", - "node scripts/console/generate-documentation-manifest.mjs --check", - "node scripts/check-reasoning-lens-contract.mjs", - "git diff --check" - ], - "enforcementPlacement": "WHERE: G004 runs in CI repo-gates after the tracked matrix exists. SUBJECT EXISTS: non-empty routePaths include passkey stories and exact /settings/policy. FINEST DISTINCTION: path, routeGroup, and requiredStory token semantics. EXAMINED-ZERO FAILS: empty routes fail existing checks; absent passkey/policy semantics fail production predicates and hostile controls.", - "peripheralsUpdated": "Exact exception removed and ceiling ratcheted; provenance regression updated; ledger classified evidence/frozen/retain; manifests mechanically regenerated by L-TRAIN.", - "classification": { - "personalData": false, - "holdAdjacent": false, - "notes": "Executable-contract decoupling only." - }, - "preMortem": "Independent review caught baseline slack, weak policy semantics, and stale receipt prose. All blockers closed in one pass. Fresh admission worktree replaced the rejected diagnostic train.", - "blastRadius": "One gate, one focused regression, one exception row, receipts/manifests/ledger admission suffix. No product domain, migration, CI workflow, lockfile, or Oyatie change.", - "detection": "Focused gate, hostile controls, provenance census, manifest/lens gates, signed C/T authentication, protected checks, and hosted readback.", - "rollback": "Protected revert of the pilot squash; no data coupling.", - "stopConditions": "Any non-owned path, red focused gate, failed C/T grammar, stale base, critic blocker, required-check failure, or hosted tree mismatch.", - "reviewIdentities": [ - "coordinator 0-Main", - "independent critic 23-W2PilotCommitCritic (Sol) APPROVE", - "L-TRAIN admitter 0-Main" - ], - "remainingHolds": [ - "T3b and ADR lanes PARK until protected pilot readback.", - "All PRODUCT/ROADMAP HOLDs unchanged." - ], - "result": "green", - "followUps": [ - "Nonblocking dynamic-import test-isolation debt remains for later T2 consolidation." - ] -} diff --git a/.cursor/receipts/wave1-20260810-critic.json b/.cursor/receipts/wave1-20260810-critic.json deleted file mode 100644 index c2da3a23d..000000000 --- a/.cursor/receipts/wave1-20260810-critic.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "verdict": "APPROVE", - "tipSha": "2faae5bab59269e3bbc871abd8c65bac1138c813", - "candidateSha": "b960bbecee4d78dc892daf5939ec57958db81ba9", - "baseSha": "7b568df9db961fa8aa3f36917eaa13c6af2c3023", - "rekey": { - "supersededTipSha": "2faae5bab59269e3bbc871abd8c65bac1138c813", - "supersededCandidateSha": "b960bbecee4d78dc892daf5939ec57958db81ba9", - "reason": "Tip rebuilt for doc-manifest/fmt/sqlparser unescape+opaque-quoted-string convergence; product leaf commits remain the admitted trees. C..T after rebuild is authority ledger ADD only.", - "independentCriticBlocker": "wave1-train-indep-critic.json BLOCK on check:doc-manifest at 9c7ea19f6 — discharged when generate-documentation-manifest --check and Repo gates are green at the new tip.", - "qaCriticFolded": "622-qa.json W-OWN-FAILOPEN + W-OWN-FALSEPOS closed on C; CI-A fmt + CI-B doc-manifest on C; AUTHZ-EMPLOYMENT deferred to console-h3e; /* sibling residual remains console-jth.", - "noFreshFullCriticRound": true - }, - "findings": [ - { - "severity": "minor", - "claim": "Residual-11 integration test module docs still describe an open byte-scan hole and claim it cannot be closed, while lib.rs marks residual 11 CLOSED and the case flags expect the write charged (escapes=false).", - "failureScenario": "A later reader treats known_residual_a_dash_inside_quoted_sql_data_hides_a_later_statement as a live residual and re-opens a third spelling of the same class instead of trusting the closed control.", - "location": "backend/ci/gates/writer-ownership/tests/gate_detects_violation.rs:1650-1674", - "provenByExecution": true, - "ownerLease": true - }, - { - "severity": "minor", - "claim": "EmploymentQuery still uses CanonicalQuery::subject_id default None; promote/transfer via projected dispatch can disagree with gated target_id without refusal. Documented q06/uoh deferral, not closed in this train.", - "failureScenario": "Four-eyes approval bound to employment I is spent while EmploymentQuery revises employment J because the handler skip-compares when subject_id is None.", - "location": "backend/crates/orgchange/adapter-postgres/src/employment.rs:295-299", - "provenByExecution": false, - "ownerLease": true - }, - { - "severity": "minor", - "claim": "uoh postgres control an_ambiguous_employment_binding_refuses_promote was not re-executed in this critic pass (Docker socket unavailable); compile --no-run succeeded and the fetch_all !=1 refuse is present in the tip diff.", - "failureScenario": "If the runtime-role suite regresses only under Postgres, admit would green on static review without catching it.", - "location": "backend/crates/orgchange/adapter-postgres/tests/employment_port_as_runtime_role.rs", - "provenByExecution": false, - "ownerLease": true - } - ], - "notes": "Standing lenses on tip 2faae5bab (C=b960bbece ledger-only C..T). ORACLE: residual-11 green; mutate→red by flipping update-then-update escapes false→true failed with employees charged; q06 projected_dispatch_derivation 7/7 green including a_payload_whose_subject_differs_from_target_id_is_refused; residual-10 separator + command-line-flag + second-writer controls green on prior pass of same product commits. PERIPHERAL: lib.rs CLOSED + 0214 comment + Buck sqlparser face (sha256 matches Cargo.lock) + ledger T; test residual-11 prose stale (finding — addressed on convergence C). ENFORCEMENT: sqlparser path when comment extent needs lexer; bound_employee fetch_all count; handler subject_id vs target_id — examined-zero fails closed. FALSE GREEN: C binds writer-ownership + third-party/rust BUCK sqlparser faces so hermetic Buck is not cargo-only; no --workflow-only. No blocker / no proven major at review time. See rekey for post-review tip rebuild." -} diff --git a/.cursor/receipts/wave4-admit-writer-claim.json b/.cursor/receipts/wave4-admit-writer-claim.json deleted file mode 100644 index abe6af77d..000000000 --- a/.cursor/receipts/wave4-admit-writer-claim.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "lane": "wave4-admit-writer-claim", - "role": "admit-coordinator", - "status": "done", - "headSha": "037d446b2cd8257ec4d65ee6bea8a1af95501af2", - "baseSha": "eca122ad0122457a77b85ca55fc1e8b2e4869e74", - "summary": "Wave4 admit complete: 09c @ 77e82dcd3 + b4z @ 1389be509 + absorb 037d446b2 (09c aggregate in fragments). Critics APPROVE. ADR \u00a77 rows 3+5 tip-bind still on T before/with train PR.", - "filesChanged": [], - "redBaseline": "n/a - claim only", - "verification": "Signed absorb 037d446b2 %G?=G; console-openapi-gen idempotent; aggregateOntologyInstances present; check-admit-sync after push.", - "commands": [ - "git cherry-pick 678482acd (resolve openapi via 09c oracle)", - "python3 backend/openapi/split_openapi.py", - "SQLX_OFFLINE=true cargo run --locked --manifest-path backend/Cargo.toml -p console-contracts --bin console-openapi-gen", - "SQLX_OFFLINE=true cargo test --locked --manifest-path backend/Cargo.toml -p console-contracts --test compose", - "SQLX_OFFLINE=true cargo test --locked --manifest-path backend/Cargo.toml -p console-todos-rest --test openapi_fragment", - "git push -u origin HEAD:admission/wave4-20260811", - "bash scripts/cursor/check-admit-sync.sh .worktrees/admission-wave4-20260811" - ], - "contractBreaches": "none", - "enforcementPlacement": "n/a - adds no enforcement", - "peripheralsUpdated": "n/a - nothing described this behaviour", - "claim": { - "admitWorktree": ".worktrees/admission-wave4-20260811", - "branch": "admission/wave4-20260811", - "admitOrder": [ - "console-09c", - "console-b4z" - ], - "liveCritics": {}, - "instructionToSiblings": "PARK re-dispatch of 09c/b4z implementers; do not dual-write lane-console-b4z; this admit WT owns serial cherry-pick after critic APPROVE" - }, - "flowDelta": "admission/wave4 ahead 3 of main; ready for train PR + ADR tip-bind (rows 3+5)", - "admitted": { - "console-09c": { - "leaf": "41b9eeda5a18b2ff856e5f8ef8d472d8091bc5a1", - "admitSha": "77e82dcd3a1b60638e06ee283aacd9e6db8e2954", - "critic": "APPROVE" - }, - "console-b4z": { - "leaf": "a1f0425ec8726e057af03f5b0e93253e0d8be96b", - "admitSha": "1389be509", - "absorbSha": "037d446b2cd8257ec4d65ee6bea8a1af95501af2", - "critic": "APPROVE" - } - }, - "pending": [] -} diff --git a/.cursor/receipts/wave4-inventory.json b/.cursor/receipts/wave4-inventory.json deleted file mode 100644 index b1b38b988..000000000 --- a/.cursor/receipts/wave4-inventory.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "lane": "wave4-inventory", - "role": "admit-coordinator", - "status": "done", - "headSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "summary": "Wave 4 ADR-0030 §7 inventory on origin/main be7b3e14b. 9ze already admitted (#734). ils close-on-admit (mechanism MET; bead closed). b4z+09c dispatched path-disjoint; admit order 09c then b4z. Frontend deferred. No full-programme E2E.", - "filesChanged": [], - "redBaseline": "n/a - inventory only", - "verification": "Measured tip: register_port x6 + projected_dispatch_derivation present; aggregate_instances absent; openapi still hand-maintained (1/34 fragments); bd close ils; preflight-forge ok.", - "commands": [ - "git fetch origin main && git rev-parse origin/main", - "rg register_port backend/app/src/lib.rs", - "rg aggregate_instances backend/crates/ontology", - "bd show console-b4z console-ils console-09c console-9ze", - "bd close console-ils --reason '...MET on origin/main be7b3e14b...'", - "bash scripts/cursor/preflight-forge.sh", - "bash scripts/cursor/provision-lane-worktree.sh console-b4z", - "bash scripts/cursor/provision-lane-worktree.sh console-09c", - "bash scripts/cursor/provision-lane-worktree.sh --kind admission wave4-20260811" - ], - "contractBreaches": "none", - "enforcementPlacement": "n/a - adds no enforcement", - "peripheralsUpdated": "n/a - nothing described this behaviour", - "beads": { - "console-9ze": "CLOSED admitted train-2 #734", - "console-ils": "CLOSED close-on-admit tip be7b3e14b §7.4 MET", - "console-b4z": "OPEN dispatched lane/console-b4z", - "console-09c": "OPEN dispatched lane/console-09c", - "console-8nq": "DEFERRED §8 shell until remaining §7 green" - }, - "admitOrder": ["console-09c", "console-b4z"], - "parked": [], - "flowDelta": "implementers live; admission branch ready at be7b3e14b; awaiting leaf receipts then critic+serial admit" -} diff --git a/.cursor/receipts/we1-critic.json b/.cursor/receipts/we1-critic.json deleted file mode 100644 index a59e6c7f7..000000000 --- a/.cursor/receipts/we1-critic.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "lane": "console-we1", - "role": "critic", - "verdict": "APPROVE", - "headSha": "a07e90dd75f35e5ba5e469adcf0b4603bb3b2d63", - "productTip": "a07e90dd75f35e5ba5e469adcf0b4603bb3b2d63", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "branch": "lane/console-we1", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-we1-restack-post742", - "originMainAtReview": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "lensesApplied": [ - "ORACLE INTEGRITY", - "PERIPHERAL DRIFT", - "ENFORCEMENT PLACEMENT", - "FALSE GREEN", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "highRiskLenses": [ - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "challenges": [ - "Stale critic receipt claimed OpenAPI alternate-dates absent while tip a07e90dd7 includes openapi.yaml fold \u2014 never admit on stale headSha.", - "Chesterton: ACTIVE filter remains necessary after EXITED+home_branch_id inflation BLOCK; re-confirmed on restack tip." - ], - "commandsRun": [ - "git rev-parse HEAD \u2192 a07e90dd75f35e5ba5e469adcf0b4603bb3b2d63", - "rg \"employment_status = 'ACTIVE'\" backend/crates/leave/adapter-postgres/src/lib.rs \u2192 hit", - "rg alternate-dates|time_change_grounds backend/openapi/openapi.yaml \u2192 hits", - "node scripts/cursor/validate-lane-receipt.mjs --schema critic \u2026/we1-critic.json" - ], - "findings": [ - { - "severity": "minor", - "claim": "Prior OpenAPI major is closed on tip a07e90dd7 (alternate-dates + time_change_grounds present). leave-http-personas coverage for alternate-dates remains optional peripheral.", - "failureScenario": "HTTP persona gap only affects local persona inventory completeness, not production gate.", - "location": "backend/openapi/openapi.yaml; leave HTTP personas", - "provenByExecution": false, - "ownerLease": true - } - ], - "priorBlockClosed": "ACTIVE-headcount BLOCK closed; filter present on tip", - "notes": "Tip-refresh after #742 restack; supersedes stale headSha cc4ee8345 OpenAPI-absent finding.", - "followUps": [ - "Babysit CI Required/CI aggregator; do not weaken release verifier" - ], - "writtenAt": "2026-08-11T13:07:34Z" -} diff --git a/.cursor/receipts/we1.json b/.cursor/receipts/we1.json deleted file mode 100644 index bf91d78a8..000000000 --- a/.cursor/receipts/we1.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "lane": "console-we1", - "role": "implementer", - "status": "done", - "headSha": "34cab73c7cf82d6dd70fff25b9c8c22c645a9efa", - "worktree": "/Users/jasonlee/Developer/console-lane-we1", - "branch": "lane/console-we1", - "baseSha": "e7f207eab0186b024eaaa3d56de1f9caffc59eda", - "summary": "Fix-round after critic BLOCK (major+proven): 0217 decide_request headcount now filters employment_status='ACTIVE' so EXITED peers retaining home_branch_id cannot inflate §60⑤ coverage and refuse a real ACTIVE shortfall. Domain TimeChangeCoverageEvidence docs + arithmetic pin; PG pin section_60_5_exited_peers_do_not_inflate_coverage_headcount (1 ACTIVE + 2 EXITED → headcount=1, projected_available=0, consult opens).", - "filesChanged": [ - "backend/crates/platform/db/migrations/0217_leave_time_change_consult_mechanics.sql", - "backend/crates/leave/domain/src/consult.rs", - "backend/crates/leave/adapter-postgres/tests/leave_rls_surfaces_as_runtime_role.rs", - ".cursor/receipts/we1.json" - ], - "redBaseline": "Critic hostile arithmetic on tip gate: 1 ACTIVE + 2 EXITED → unfiltered headcount=3 projected_available=2 refuses time_change; ACTIVE-only projected_available=0 would allow (provenByExecution). Reproduced same arithmetic locally (refuse_unfiltered=true, refuse_active=false). Domain pin exited_peers_are_excluded_from_active_headcount_arithmetic encodes the control (inflated headcount=3 ineligible vs active-only=1 eligible). PG pin absent on pre-fix tip.", - "verification": "cargo test -p console-leave-domain --all-targets → 31/31 (incl. exited_peers_are_excluded_from_active_headcount_arithmetic). cargo test -p console-leave-rest --lib → 10/10. cargo fmt --check clean on leave packages. bash tools/ci/cargo_needs_postgres.sh --only leave-adapter-postgres-leave-rls-surfaces-as-runtime-role-pg → 15/15 incl. section_60_5_exited_peers_do_not_inflate_coverage_headcount. Product commit 34cab73c7 %G?=G.", - "commands": [ - "python3 hostile headcount arithmetic (1 ACTIVE+2 EXITED) → unfiltered refuses / active-only allows", - "cargo test --manifest-path backend/Cargo.toml -p console-leave-domain --all-targets", - "cargo test --manifest-path backend/Cargo.toml -p console-leave-rest --lib", - "cargo fmt --manifest-path backend/Cargo.toml -p console-leave-domain -p console-leave-application -p console-leave-adapter-postgres -p console-leave-rest -- --check", - "bash tools/ci/cargo_needs_postgres.sh --only leave-adapter-postgres-leave-rls-surfaces-as-runtime-role-pg --num-threads=1", - "git log -1 --format='%H %G?' 34cab73c7 → G", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console-lane-we1/.cursor/receipts/we1.json" - ], - "contractBreaches": "n/a — tightens decide_request headcount subject set to ACTIVE-only (aligned with domain contract and resolve_self_employee_identity). No wire-shape or OpenAPI mutation in this leaf.", - "enforcementPlacement": "WHERE: leave_api.decide_request BEFORE terminal status write — subject is employees rows with home_branch_id=request.branch_id AND employment_status='ACTIVE' (plus overlapping approved leave for already_out). Finest distinction: ACTIVE roster integer headcount; EXITED/UNKNOWN excluded. Examined-zero fails: headcount=0 → leave_decide.time_change_ineligible (cannot invoke proviso). PG pin section_60_5_exited_peers_do_not_inflate_coverage_headcount fails if filter regresses.", - "peripheralsUpdated": "Domain TimeChangeCoverageEvidence headcount doc now states ACTIVE-only + EXITED non-inflation; migration 0217 header/comment mirrors. OpenAPI alternate-dates/consult fields remain ownerLease console-9sxn (not dual-written). Persona pins deferred (minor).", - "lensesApplied": [ - "Cartesian doubt", - "Essentialism / YAGNI", - "Chesterton's Fence", - "Red Team", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "Operability / Day-2" - ], - "challenges": [ - "Chesterton: is unfiltered home_branch headcount an intentional total-floor (any stamped roster row)? Domain already says Active employees and sibling leave self-resolve filters ACTIVE — 0217 was the incomplete spelling, not a defended fence.", - "Red Team: Day-2 EXITED retention with home_branch_id stamped would strip the employer §60⑤ proviso; ACTIVE filter closes that class without opening manager discretion." - ], - "followUps": [ - "OWNER LEASE (console-9sxn): OpenAPI path+schemas for POST /api/v2/leave/requests/{id}/alternate-dates and v2 LeaveRequestView consult fields — do not dual-write from we1.", - "Optional minor: leave-http-personas coverage for ineligible time_change + POST /alternate-dates (critic minor; not this leaf).", - "Parent re-critique on tip after push; babysit owns CI." - ] -} diff --git a/.cursor/receipts/wnv.json b/.cursor/receipts/wnv.json deleted file mode 100644 index 7247e4deb..000000000 --- a/.cursor/receipts/wnv.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "lane": "console-wnv", - "status": "done", - "headSha": "6d8d4039eaf8594c1b63614837118ef4f5940204", - "branch": "lane/console-wnv", - "worktree": "/Users/jasonlee/Developer/console-lane-wnv", - "baseSha": "7b568df9db961fa8aa3f36917eaa13c6af2c3023", - "summary": "Bead console-wnv (P2): check-platform-contract-drift no longer resolves an imported path constant via repo-wide same-name fallback. Mechanism replacement \u2014 drop globalConstants/ambiguous/generic-name deny-list; a constant resolves only from declarations in the registering module, and a same-named const elsewhere fails closed. Hostile LIST_PATH fixture proved the prior fail-open (OpenAPI\u2229gate\u2216server); suite green; real tree still 582/54.", - "filesChanged": [ - "scripts/check-platform-contract-drift.mjs", - "scripts/check-platform-contract-drift.test.mjs", - "package.json" - ], - "ownedRoots": [ - "scripts/check-platform-contract-drift.mjs", - "scripts/check-platform-contract-drift.test.mjs", - "package.json (npm script body only \u2014 reachability wiring for the new suite)" - ], - "redBaseline": "node --test scripts/check-platform-contract-drift.test.mjs before the mechanism replacement: 6 tests, 3 fail. (1) PATH fixture threw the old special-case message 'refusing nonlocal resolution for a generic name' rather than module-scope refusal \u2014 deny-list spelling, not total. (2) LIST_PATH fixture (name outside the deny-list) did NOT refuse repo-wide fallback: gate bound donor.rs's /api/unrelated into served.rs and proceeded to UNDOCUMENTED_BY_DESIGN staleness \u2014 proving OpenAPI could agree with the false harvest while the real imported path was omitted. (3) local-const positive case masked by exemption bookkeeping until fixture gained the two by-design routes. After fix: 6/6 pass.", - "verification": "In /Users/jasonlee/Developer/console-lane-wnv: node --test scripts/check-platform-contract-drift.test.mjs \u2192 6/6 pass; npm run check:platform-contract-drift \u2192 suite + '582 backend /api/ operations across 54 route sources'; node scripts/check-js-test-reachability.mjs \u2192 41 exact-wired, dark 0; node tools/ci/check-mjs-dark-suites.mjs --strict \u2192 exit 0. Commit signed (git verify-commit).", - "commands": [ - "git -C /Users/jasonlee/Developer/console worktree add /Users/jasonlee/Developer/console-lane-wnv -b lane/console-wnv origin/main", - "git commit -S # smoke skeleton; verify-commit Good git signature ED25519", - "node --test scripts/check-platform-contract-drift.test.mjs # RED: 3 fail (generic-name spelling + LIST_PATH fail-open + exemption mask)", - "node --test scripts/check-platform-contract-drift.test.mjs # GREEN: 6 pass", - "npm run check:platform-contract-drift # suite + real tree 582/54", - "node scripts/check-js-test-reachability.mjs # 41 exact-wired, dark 0", - "node tools/ci/check-mjs-dark-suites.mjs --strict # exit 0", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console-lane-wnv/.cursor/receipts/wnv.json", - "git commit -S # product commit on lane/console-wnv" - ], - "contractBreaches": "package.json one-line npm script body extension is outside the brief's literal 'scripts/check-platform-contract-drift.mjs + its tests' wording. Forced peripheral: scripts/check-js-test-reachability.mjs rejects unregistered dark .test.mjs suites; workflows/docs baselines are forbidden \u2014 extending the existing check:platform-contract-drift script (already CI-invoked) is the only lane-viable wiring. No other roots touched.", - "enforcementPlacement": "WHERE: CI api-contract job via existing step 'npm run check:platform-contract-drift' (ci.yml Platform contract drift gate; step string unchanged, only npm script body gained the suite). SUBJECT EXISTS: committed backend/**/src/**.rs route sources and backend/openapi/openapi.yaml at tip; gate still discovers after strip. FINEST DISTINCTION: (METHOD, path template) per route-source file, with path-constant binding now file/module-local only \u2014 a same-named const in another route source is a hard error, not a value. EXAMINED-ZERO FAILS: unchanged \u2014 zero route sources / zero backend /api/ ops / zero contract /api/ ops each throw; new suite also asserts unreadable OpenAPI (ENOENT) and zero-/api/ fixtures fail.", - "peripheralsUpdated": "package.json check:platform-contract-drift script body (suite wiring). Module header FAIL-CLOSED prose in the gate itself updated to state module-scoped constant resolution. Deliberately NOT changed: .claude/workflows/lane-fanout.test.mjs still source-contains-checks the removed 'refusing nonlocal resolution for a generic name' string (unowned \u2014 elevate); docs/CI-GATES.md enumerates the npm script name only (unchanged); scripts/console/**, check-openapi-refs*, ci.yml untouched.", - "lenses": [ - "Red Team \u2014 hostile LIST_PATH/PATH fixtures reproduce OpenAPI\u2229gate\u2216server; deny-list was incomplete and was replaced", - "Zero-trust / defense-in-depth \u2014 never trust a peer file's same-named const; imports from non-router modules stay unresolved rather than guessed", - "Blast-radius \u2014 three files; no product Rust, no workflow step string, no other gates", - "Operability / Day-2 \u2014 error names the registering file and the constant; real-tree count unchanged (582/54); suite rides the existing npm script" - ], - "claimGraph": [ - "E: tip 7b568df9d gate still had globalConstants + generic-name deny-list (read scripts/check-platform-contract-drift.mjs before edit)", - "E: LIST_PATH RED \u2014 gate did not refuse fallback; error was UNDOCUMENTED_BY_DESIGN staleness after binding /api/unrelated into served.rs", - "E: PATH RED \u2014 special-case deny-list fired ('generic name'), proving third-spelling incompleteness vs total module-scope rule", - "E: after fix, 6/6 tests pass; real tree 582 ops / 54 sources unchanged", - "E: WELL_KNOWN_* in backend/app/src/lib.rs are not declared in any route source \u2014 declaredElsewhere miss \u2192 undefined \u2192 deferred (pre-existing); not /api/", - "I: refusing only when a same-named declaration exists among route sources preserves non-router imports without restoring repo-wide value binding", - "U: whether parent will retarget lane-fanout.test.mjs source-contains check from the removed generic-name string to the new refusal message" - ], - "challenges": [ - "Chesterton: the generic-name deny-list and ambiguous-value guard existed because full cross-file const imports are real (WELL_KNOWN_*). Challenged keeping any repo-wide *value* fallback \u2014 replaced with declaredElsewhere membership test that fails closed on same-name without ever returning the foreign value. Non-router imports (no declaration among route sources) remain deferred as before.", - "Challenge to strict 'always throw if not local': would red the real tree on WELL_KNOWN_* in app/lib.rs. Observed those consts live outside route sources; membership-without-binding preserves Day-2 green while closing the bead's fail-open class." - ], - "followUps": [ - "ELEVATE (unowned): .claude/workflows/lane-fanout.test.mjs checks driftSrc for 'refusing nonlocal resolution for a generic name' \u2014 that string is gone after mechanism replacement. Parent/harness lane should retarget the check to 'refusing repo-wide same-name fallback' (or drop the source-contains oracle).", - "Bead console-wnv: parent closes on admission. No PR/CI watch from this lane.", - "Smoke commit 4f4cd2390 remains on the branch (signing probe); product commit is the fix." - ] -} diff --git a/.cursor/receipts/xxp-critic.json b/.cursor/receipts/xxp-critic.json deleted file mode 100644 index d6a4b99f2..000000000 --- a/.cursor/receipts/xxp-critic.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "verdict": "APPROVE", - "laneId": "console-xxp", - "tipSha": "445e986998ead19d22d16c2de762831d37414cba", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "role": "critic", - "reviewedRange": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d...445e986998ead19d22d16c2de762831d37414cba", - "leafPr": "https://github.com/jason931225/console/pull/750", - "standingLenses": { - "oracleIntegrity": "PASS — tip adds get/list_for_org_unit + free_text_and_recruiting_are_never_inferred_as_job_positions + receipt org_unit_id create assert; REST adds fail-closed identity_from_receipt_result pins (title-only, OrgEntitySummary refused). No deleted/ignored/conformed asserts observed in the 4-file diff. Critic re-ran REST job_position 3/3 and PG suite 14/14 on tip 445e986.", - "peripheralDrift": "PASS for product leaf — name-only diff is exactly job_position adapter + rest helper + runtime-role test + lib.rs re-export. No orgchange/**, employment.rs, EmploymentAttributes, OpenAPI, migrations. Tip-serial executed-tests-baseline.json still pins jp suite 13 / rest lib 15 while tip declares 14 / 18 — admit-owned (finding).", - "enforcementPlacement": "PASS — no new CI gate. Authority pins live in sqlx::test runtime-role suite (subject tables exist under migrations; free-text seed leaves job_positions count 0) and REST unit fail-closed on missing UUID fields; examined-zero fails closed.", - "falseGreen": "PASS — no --workflow-only; cargo_needs_postgres canonical-enforce examined 20 tables including job_positions/job_position_revisions; commandsRun non-empty; critic executed both package surfaces on tip 445e986." - }, - "lensesApplied": [ - "Cartesian doubt", - "Chesterton's Fence", - "Essentialism / YAGNI", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth", - "Pragmatism" - ], - "claimGraph": [ - { - "claim": "HEAD binds immutable tip 445e986998ead19d22d16c2de762831d37414cba; merge-base with origin/main is be7b3e14b", - "tag": "E" - }, - { - "claim": "Diff ⊆ job_position.rs (adapter) + REST job_position + lib re-export + runtime-role tests; not orgchange/** / employment.rs", - "tag": "E" - }, - { - "claim": "JobPosition create→get ID round-trip + list_for_org_unit works on tip", - "tag": "E" - }, - { - "claim": "Free-text employees.position + recruit_postings never invent job_positions; port create attributes are command-authored", - "tag": "E" - }, - { - "claim": "REST identity_from_receipt_result refuses title-only and OrgEntitySummary shapes", - "tag": "E" - }, - { - "claim": "executed-tests attribute baseline must --update on admit tip-serial (jp 13→14, rest lib 15→18)", - "tag": "E" - }, - { - "claim": "Revise/move receipt org_unit_id + post-move get/list lack dedicated asserts (create path only)", - "tag": "I" - } - ], - "findings": [ - { - "severity": "major", - "claim": "Tip gains declared test attributes (job_position_port_as_runtime_role 13→14; ontology/rest/src/lib.rs aggregate 15→18) without locking docs/program/executed-tests-baseline.json; node scripts/check-executed-tests.mjs exits 1 with 'gained declared test attributes. Run --update…'.", - "failureScenario": "Leaf or train merge without admit tip-serial --update leaves Repo Gates / preflight check:executed-tests red while product tests are green — false 'CI failure in JobPosition' triage or blocked admit.", - "location": "docs/program/executed-tests-baseline.json test_attribute_baseline (tip-serial); scripts/check-executed-tests.mjs evaluateTestAttributeBaseline", - "provenByExecution": true, - "ownerLease": true - }, - { - "severity": "minor", - "claim": "Revise / reorganisation paths do not assert receipt.result org_unit_id or get/list_for_org_unit after a move; only create readback pins the new query surface + org_unit_id field.", - "failureScenario": "A future edit could drop org_unit_id from Revise receipt JSON or skip the head UPDATE while create tests stay green — clients round-tripping revise/move would see missing/stale unit identity.", - "location": "backend/crates/ontology/canonical-adapter-postgres/tests/job_position_port_as_runtime_role.rs (a_revision_is_appended…, a_reorganisation_moves…)", - "provenByExecution": false, - "ownerLease": false - } - ], - "challenges": [ - "Chesterton: L5-JOB could have invented /api/v1/job-positions or widened OrgEntitySummary — tip correctly refuses that (y0n/7sx boundaries) and keeps identity on preserved ontology action receipts + port get/list.", - "Red Team: foreign-tenant get via PgJobPositionPort.get is not a named assert; rejected as merge bar because a_foreign_tenant_is_invisible… already proves console_rt SELECT count 0 under armed app.current_org and get/list always arm the same GUC before SELECT.", - "Operability: pre-xxp stored receipts lack org_unit_id; identity_from_receipt_result fail-closed is intentional — Day-2 clients must tolerate old receipts or re-issue; not a tip product defect.", - "Cartesian: REST helper is a pure receipt parser, not an HTTP route — acceptable under brief (no parallel /job-positions family); authority query is PgJobPositionPort.get/list, proven in PG suite." - ], - "commandsRun": [ - "git rev-parse HEAD → 445e986998ead19d22d16c2de762831d37414cba", - "git merge-base HEAD origin/main → be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "git diff --stat be7b3e14b2f126e8c24a2bf99758d0de00aaa23d..445e986998ead19d22d16c2de762831d37414cba → 4 files, +364/-7", - "git diff --name-only … | rg orgchange|employment|OpenAPI|migration → clean owned roots", - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-rest --lib job_position → ok. 3 passed", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-canonical-adapter-postgres-job-position-port-as-runtime-role-pg --num-threads=1 → ok. 14 passed; canonical-enforce examined 20 tables incl. job_positions", - "node scripts/check-executed-tests.mjs (lane worktree) → exit 1 (2 sources gained test attributes: jp 13→14, rest lib 15→18)", - "gh pr view 750 → headRefOid 445e98699 matches tip" - ], - "followUps": [ - "Admit tip: node scripts/check-executed-tests.mjs --update and commit docs/program/executed-tests-baseline.json (tip-serial) before merge — do not ask xxp leaf to touch tip-serial.", - "Optional cheap pin (same class, not required for APPROVE): assert revise/move receipt org_unit_id + get/list after reorganisation in the runtime-role suite.", - "ownerLease console-y0n / console-7sx remain path-disjoint: do not fold JobPosition into OrgEntitySummary or EmploymentAttributes." - ], - "notes": "Role: critic (MUST NOT implement). Premise: HEAD==immutable tip 445e986; base be7b3e14 = origin/main merge-base. Product objective met: JobPositionView get/list_for_org_unit, receipt org_unit_id on create path, free-text/recruiting never inferred, REST identity_from_receipt_result fail-closed, OrgEntitySummary/EmploymentAttributes untouched. High-risk lenses: Red Team (RLS arm + omit-by-None), Operability (old receipt shape), Blast-radius (owned job_position roots only), Zero-trust (tenant GUC + FK cross-tenant refuse already in suite). Convergence: no blocker; sole major+proven is tip-serial baseline (ownerLease admit) → APPROVE. CI babysit deferred to parent/babysit.", - "flowDelta": "critic APPROVE tip 445e986 — leaf ready to stack/admit after tip-serial executed-tests-baseline --update; no product re-dispatch" -} diff --git a/.cursor/receipts/xxp.json b/.cursor/receipts/xxp.json deleted file mode 100644 index 9cc427b2f..000000000 --- a/.cursor/receipts/xxp.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "lane": "console-xxp", - "laneId": "console-xxp", - "bead": "console-xxp (gh#718 — L5-JOB JobPosition as first-class object)", - "role": "implementer", - "status": "done", - "baseSha": "be7b3e14b2f126e8c24a2bf99758d0de00aaa23d", - "headSha": "445e986998ead19d22d16c2de762831d37414cba", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-console-xxp (branch lane/console-xxp)", - "summary": "L5-JOB residual: JobPosition is the authority surface for position identity. Port gains get/list_for_org_unit readback; create/revise receipts now carry org_unit_id alongside job_position_id/version for the preserved ontology action namespace; REST projection parses that receipt shape and refuses free-text / OrgEntitySummary. Proven: create→get ID round-trip; employees.position + recruit_postings seed never materialize job_positions. Did not touch OrgEntitySummary (7sx), EmploymentAttributes (y0n), migrations, or orgchange.", - "filesChanged": [ - "backend/crates/ontology/canonical-adapter-postgres/src/job_position.rs", - "backend/crates/ontology/canonical-adapter-postgres/tests/job_position_port_as_runtime_role.rs", - "backend/crates/ontology/rest/src/job_position.rs", - "backend/crates/ontology/rest/src/lib.rs" - ], - "redBaseline": "At base be7b3e14b: PgJobPositionPort had create/revise only — no get/list; receipt result lacked org_unit_id; ontology/rest had no JobPosition identity projection. Hostile gap: seeding employees.position + recruit_postings.position_ref left job_positions empty (non-vacuous negative) while a missing get would leave first-class query unproven. After this tip, free_text_and_recruiting_are_never_inferred_as_job_positions and create→get assertions are green; mutating identity_from_receipt_result to accept title-only JSON goes red on free_text_title_alone_is_not_a_job_position_identity.", - "verification": "GREEN. cargo fmt -p console-ontology-canonical-adapter-postgres -p console-ontology-rest. cargo test -p console-ontology-rest --lib (job_position::tests: receipt_result_round_trips_canonical_ids, free_text_title_alone_is_not_a_job_position_identity, org_entity_summary_shape_is_refused). bash tools/ci/cargo_needs_postgres.sh --only ontology-canonical-adapter-postgres-job-position-port-as-runtime-role-pg --num-threads=1 → 14 passed; 0 failed. git commit -S → %G?=G. Signing smoke folded into product commit 445e98699.", - "contractBreaches": "none. Owned roots only. Did not edit orgchange/**, employment.rs, EmploymentAttributes, payroll/**, governance/**, docs/current/**, migrations, OpenAPI, or sibling 7sx/y0n paths.", - "enforcementPlacement": "n/a - adds no enforcement", - "peripheralsUpdated": "job_position.rs module doc now states no SourceBinding / no recruit_postings|employees.position reads; REST job_position.rs documents preserved action-namespace readback vs OrgEntitySummary. Runtime-role suite gained free_text_and_recruiting_are_never_inferred_as_job_positions + get/list round-trip assertions on a_job_position_is_created_and_read_back.", - "commands": [ - "git rev-parse HEAD", - "cargo fmt -p console-ontology-canonical-adapter-postgres -p console-ontology-rest --manifest-path backend/Cargo.toml", - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-rest --lib receipt_result_round_trips", - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-rest --lib free_text_title", - "cargo test --manifest-path backend/Cargo.toml -p console-ontology-rest --lib org_entity_summary", - "bash tools/ci/cargo_needs_postgres.sh --only ontology-canonical-adapter-postgres-job-position-port-as-runtime-role-pg --num-threads=1", - "git log -1 --format='%G?'" - ], - "lenses": [ - "Cartesian doubt", - "Essentialism / YAGNI", - "Chesterton's Fence", - "Red Team", - "Operability / Day-2", - "Blast-radius / cell-based", - "Zero-trust / defense-in-depth" - ], - "challenges": [ - "Scout floated orgchange/rest or employees REST for JobPosition IDs; brief forbids OrgEntitySummary and y0n owns employment attrs — so authority readback stays on ontology action receipts + port get/list, not a parallel /job-positions family or OrgEntitySummary widen." - ], - "followUps": [ - "Critic on tip 445e98699; admit after 7sx (path-disjoint).", - "ownerLease console-y0n: EmploymentAttributes UUID upgrade + ReassignOrgUnit→hr.transfer after 7sx∥xxp land.", - "ownerLease console-7sx: OrgEntitySummary Company/OrgUnit reference — do not fold JobPosition into that DTO." - ], - "flowDelta": "leaf ready to admit — JobPosition queryable/creatable with ID round-trip; y0n unblocked on position identity surface" -} diff --git a/.cursor/receipts/y0n.json b/.cursor/receipts/y0n.json deleted file mode 100644 index 1dfcc1552..000000000 --- a/.cursor/receipts/y0n.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "lane": "console-y0n", - "laneId": "console-y0n", - "bead": "console-y0n (gh#682 \u2014 L5-HR Canonical HR assignment writer)", - "role": "implementer", - "status": "done", - "baseSha": "84655ce7881c764bbe81081b193cdd2d85d523c1", - "headSha": "ad84e2d2ca4ad73377ac09230e6e29521a31fe08", - "worktree": "/Users/jasonlee/Developer/console/.worktrees/lane-console-y0n (branch lane/console-y0n)", - "summary": "Critic BLOCK peripheral fix: OrgProposalOpReassignOrgUnit OpenAPI + CAP-ORG fragment now document hr.transfer semantics and OrgUnit UUID wire (Uuid $ref / format:uuid). Closes process.openapi-enum-peripheral-drift for ReassignOrgUnit. REST hr.rs free-text dual-namespace left ownerLease.", - "filesChanged": [ - "backend/crates/orgchange/rest/openapi/schemas/OrgProposalOpReassignOrgUnit.yaml", - "backend/openapi/openapi.yaml", - "docs/evidence/console/CAP-ORG-CONSOLE/manifests/openapi-fragment.yaml", - ".cursor/receipts/y0n.json" - ], - "redBaseline": "Before fix: OrgProposalOpReassignOrgUnit.yaml L3 description='Bounded employees.org_unit rewrite'; fromOrgUnit/toOrgUnit unconstrained string minLength/maxLength 120 (no Uuid). openapi.yaml OrgProposalOpReassignOrgUnit identical rewrite + unconstrained strings. CAP-ORG openapi-fragment.yaml fromOrgUnit/toOrgUnit type:string maxLength:120 without format:uuid or hr.transfer prose.", - "verification": "GREEN peripheral sync. python3 assert: rest schema + openapi.yaml Reassign section contain hr.transfer, Uuid $ref, no rewrite; CAP-ORG fragment fromOrgUnit/toOrgUnit format:uuid + hr.transfer, no maxLength:120 on those fields. Did not re-run product PG suites (no product code change). Product tip signed G.", - "contractBreaches": "none on allowlist. Did not touch hr.rs / employment.rs / domain. Only allowlisted OpenAPI/CAP-ORG + receipt.", - "enforcementPlacement": "n/a - adds no enforcement", - "peripheralsUpdated": "OrgProposalOpReassignOrgUnit.yaml description->hr.transfer + fromOrgUnit/toOrgUnit Uuid $ref; backend/openapi/openapi.yaml same section; CAP-ORG manifests/openapi-fragment.yaml fromOrgUnit/toOrgUnit format:uuid + hr.transfer descriptions. CAP-ORG design-contract.md / gap-analysis.md still mention employees.org_unit rewrite \u2014 outside allowlist; elevated.", - "commands": [ - "python3 probe RED: rewrite description + unconstrained fromOrgUnit/toOrgUnit on three OpenAPI surfaces", - "python3 assert GREEN: three files hr.transfer + Uuid/format:uuid; no rewrite in Reassign sections", - "git commit -S docs(console-y0n): sync ReassignOrgUnit OpenAPI to hr.transfer UUIDs -> %G?=G", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console/.worktrees/lane-console-y0n/.cursor/receipts/y0n.json", - "bash /Users/jasonlee/Developer/console/scripts/cursor/preflight-forge.sh", - "git push --force-with-lease origin lane/console-y0n" - ], - "lenses": [ - "Cartesian doubt", - "Essentialism / YAGNI", - "Chesterton's Fence", - "Peripheral drift", - "Blast-radius / cell-based" - ], - "challenges": [ - "Chesterton: CAP-ORG ReassignOrgUnit advertised employees.org_unit rewrite when teams had no table; after L5-ORG + tip cdb8b47a1 the runtime is UUID + hr.transfer \u2014 keeping rewrite prose would lie to codegen/clients (process.openapi-enum-peripheral-drift).", - "Opportunity cost: regenerating openapi.yaml via house generator would touch files beyond allowlist \u2014 manual three-file sync chosen; CAP-ORG design-contract.md rewrite prose left elevated rather than expanding scope." - ], - "followUps": [ - "ownerLease REST/hr.rs free-text org_unit/position dual-namespace remains \u2014 do not expand this tip to People lifecycle / OpenAPI tip-serial for REST.", - "ELEVATE: CAP-ORG design-contract.md + gap-analysis.md still claim employees.org_unit rewrite (outside this lane allowlist).", - "minor unproven JobPosition/org_unit coherence on reassign \u2014 skipped this round.", - "Critic re-review on new tip; admit owns train PR (not this lane)." - ], - "flowDelta": "OpenAPI/CAP-ORG ReassignOrgUnit peripherals match UUID + hr.transfer tip; REST ownerLease still open; leaf ready for critic re-pass" -} diff --git a/.cursor/receipts/zd7.json b/.cursor/receipts/zd7.json deleted file mode 100644 index abd90394d..000000000 --- a/.cursor/receipts/zd7.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "lane": "console-zd7", - "role": "implementer", - "status": "done", - "headSha": "2760ac8e58d5e6ea0ae02e29fcef531b5ad9115c", - "priorTip": "eb84f35256eb72d902465896d1b11b6c0ef06517", - "smokeCommit": "eb84f35256eb72d902465896d1b11b6c0ef06517", - "smokeSignature": "G jason19931225@gmail.com (ED25519) — verified via git verify-commit", - "worktree": "/Users/jasonlee/Developer/console-lane-zd7", - "branch": "lane/console-zd7", - "baseSha": "7b568df9db961fa8aa3f36917eaa13c6af2c3023", - "ownedRoots": [ - ".claude/workflows/stale-take-audit.js", - ".claude/workflows/lane-fanout.test.mjs", - ".cursor/receipts/zd7.json" - ], - "summary": "Bead console-zd7 (P2 harness): stale-take-audit no longer publishes an unconfirmed graft payload. Premise-check at origin/main tip: Confirm already required missingFromHead and published confirmedMissing for the stale[] list (landed via #619 / 03d162506), but two residual fail-opens remained — (1) the confirm prompt offered the first-pass graft candidate for rubber-stamping, (2) unconfirmed[] still exposed graft-shaped missingFromHead from the audit pass. Fix: withhold the first-pass graft from the confirm prompt (evidence only; confirmer must re-derive), publish stale[].missingFromHead exclusively from confirmedMissing, and carry unresolved claims as claimedMissingFromHead without a missingFromHead key. Fail closed on missing/blank confirmation payloads (already gated) and on mismatched publish (never substitute first-pass text).", - "filesChanged": [ - ".claude/workflows/stale-take-audit.js", - ".claude/workflows/lane-fanout.test.mjs", - ".cursor/receipts/zd7.json" - ], - "redBaseline": "Added hostile fixtures before the harness edit; `node .claude/workflows/lane-fanout.test.mjs` → 3 FAILURE(S): (1) upheld STALE without attestation still exposed unconfirmed[0].missingFromHead='the step'; (2) blank confirmer payload same graft-shaped leak; (3) confirm prompt contained 'MISSING-FROM-HEAD PAYLOAD OFFERED' + FIRST_PASS_SECRET_GRAFT. Mutate→red oracle also recorded: old publish path `missingFromHead: r.missingFromHead` with confirmedMissing=null leaks FIRST_PASS_WRONG under a confirmed-stale filter, while the attested filter publishes nothing.", - "verification": "After the harness edit: `node .claude/workflows/lane-fanout.test.mjs` → ALL PASS — safe to dispatch (222 PASS, 0 FAIL). Targeted pins green: no graft-shaped missingFromHead on unconfirmed without attestation; confirm prompt has EVIDENCE OFFERED only (no PAYLOAD OFFERED / no first-pass secret); confirmed stale publishes confirmer attestation only; mutate→red oracle holds. Commit signed (gpg.format=ssh); `git log -1 --format='%G?'` expected G after bind.", - "commands": [ - "git -C /Users/jasonlee/Developer/console worktree add /Users/jasonlee/Developer/console-lane-zd7 -b lane/console-zd7 origin/main", - "git commit -S -m smoke # empty smoke; git verify-commit → Good git signature ED25519; %G?=G", - "node .claude/workflows/lane-fanout.test.mjs # RED: 3 FAILURE(S) — unconfirmed missingFromHead leak + confirm prompt offers first-pass graft", - "node .claude/workflows/lane-fanout.test.mjs # GREEN: ALL PASS — safe to dispatch (222 PASS)", - "git commit -S # work commit binding this receipt", - "node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /Users/jasonlee/Developer/console-lane-zd7/.cursor/receipts/zd7.json" - ], - "contractBreaches": "none — only owned stale-take-audit harness + its lane-fanout.test.mjs coverage + lane receipt. Did not touch scripts/console, check-openapi-refs, check-platform-contract-drift, collaboration.rs, leave, workorder/settlement, audit-chain, image-release.yml, writer-ownership/ontology/orgchange, or hub product .cursor/**.", - "enforcementPlacement": "WHERE: offline preflight `node .claude/workflows/lane-fanout.test.mjs` drives stale-take-audit.js via AsyncFunction with hostile confirm stubs — the subject (Confirm publish path + confirm prompt text) EXISTS in-tree and is executed, not merely grepped. FINEST DISTINCTION: per-suspect confirm return shape (missing/blank/attested/refuted) and the exact confirm prompt bytes (must not contain first-pass graft). EXAMINED-ZERO FAILS: the suite exits nonzero on any failed check; mutate→red oracle asserts the old publish mapping would leak; empty commandsRun-style vacuity does not apply (commands below are non-empty and were run).", - "peripheralsUpdated": "lane-fanout.test.mjs extended with zd7-named pins (unconfirmed claimedMissingFromHead, confirm-prompt rubber-stamp denial, confirmer-vs-first-pass mismatch, mutate→red old publish path). No docs/** / registries / baselines describe the confirm graft schema; n/a beyond the owned test peripheral.", - "lenses": [ - "Red Team — hostile stubs: confirmer upholds without payload, blank payload, first-pass secret in prompt, old publish mapping mutation", - "Zero-trust — graft text is only published from confirmer-attested confirmedMissing; unconfirmed claims lose the graft-shaped key; confirm prompt no longer hands the answer to copy", - "Blast-radius / cell-based — change confined to stale-take-audit.js publish/prompt + its test block; other harnesses untouched", - "Operability / Day-2 — operators still see unresolved accusations via claimedMissingFromHead; confirmed stale[].missingFromHead remains the only apply-ready field" - ], - "claimGraph": [ - "E: tip before edit already published confirmedMissing for stale[] and failed closed on missing/blank confirmer payloads (tests 178/180 green pre-edit)", - "E: RED 3 failures on new fixtures before harness edit; GREEN 222/222 after", - "E: confirm prompt pre-edit contained PAYLOAD OFFERED + first-pass text (probe + failing fixture)", - "E: unconfirmed pre-edit exposed missingFromHead from audit spread (probe JSON)", - "I: withholding the offered graft is required for 'payload nobody re-read is a claim' — offering it invites rubber-stamping even when schema asks for attestation", - "U: whether any out-of-tree operator script keyed on unconfirmed[].missingFromHead (renamed to claimedMissingFromHead) — no in-repo consumers found via rg" - ], - "challenges": [ - "Chesterton: #619 already added confirmedMissing publish — why is the bead still open? Because two residual surfaces still published or invited unconfirmed graft text (prompt offer + unconfirmed.missingFromHead). Closing only the stale[] path left a claim-shaped field and a rubber-stamp prompt.", - "Contrarian: keep offering the first-pass payload so the confirmer can refute the exact quote? Rejected — evidence + diffs suffice to refute; handing the graft candidate is what makes an unread copy look attested." - ], - "followUps": [ - "Parent/admission: close bead console-zd7 after land; smoke empty commit eb84f3525 is setup evidence (signed), work commit is the defect close.", - "No PR/CI watch from this lane (brief)." - ] -} diff --git a/.cursor/rules/console-agent-ritual.mdc b/.cursor/rules/console-agent-ritual.mdc deleted file mode 100644 index 0d25be695..000000000 --- a/.cursor/rules/console-agent-ritual.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: Universal per-dispatch ritual — every agent (implementation, audit, review, planning, scout, recon) runs this checklist at start and end of every dispatch. North-star strategy lives in the programme SSOT plan; this card is the tactical ritual. -alwaysApply: true ---- - -# Agent ritual v1 (run every dispatch) - -Two instruction tiers exist. **Tier 1 (north-star):** the programme SSOT plan — where we are going, waves, topology. **Tier 2 (this card):** the ritual you run on every single dispatch regardless of role. Trivial read-only tasks MAY use the short form (A1 + D1 only). - -## A. Orient (before your first tool call) - -1. **Role declaration** — state which ONE role you hold: `implementer | critic | auditor | scout | recon | planner | babysit | admit-coordinator`. Name the one thing you MUST NOT do in that role (implementer: babysit CI / open train PR; critic: implement the fix; scout: write product; babysit: write code; coordinator: serialize lanes yourself). -2. **Position diagram** — draw your place in the flow (mermaid, 4–8 nodes): inputs (tip SHA, brief, owned roots) → you → output (receipt / verdict / patch / map) → who consumes it. If you cannot draw who consumes your output, STOP and elevate — you are about to do orphan work. -3. **Premise check** — verify ground truth against HEAD before acting (new HEAD = new evidence; a brief's SHA may be stale). State what you verified. - -## B. Reasoning map (before implementing / verdicting / planning) - -1. **Lens selection** — pick the smallest task-fit subset (≥2) from the canonical list in `AGENTS.md` (drift-checked by `scripts/check-reasoning-lens-manifest.mjs`). Root-level decisions get the full battery; leaves inherit. High-risk domains (authz/migration/contracts/approval/HR-payroll/release/production/compliance) SHOULD include Red Team, Operability, Blast-radius, Zero-trust. No gate demands a per-record evidence block; none can evaluate one. -2. **Claim graph** — for each load-bearing claim, tag it `E` (evidence: observed via command/file), `I` (inference from evidence), or `U` (uncertainty/assumption). A verdict or plan built on untagged claims is narrative, not reasoning. -3. **Challenges[]** — record at least one real challenge to the brief/plan/ADR you were handed (Chesterton first, then still replace if indefensible). Empty or templated challenges = stamp; reject your own work. - -## C. Doctrine (during execution) - -- Owned roots only; one writer per worktree; RED baseline before fix. -- **Worktrees live under `/.worktrees/`** (not sibling `../console-lane-*`). Sibling paths are outside the Cursor workspace and force allow-edit prompts. Prefer `scripts/cursor/provision-lane-worktree.sh`. -- Evidence keyed to THIS tip SHA; commands recorded verbatim (no empty strings). -- Friction (path outside roots, hub collision, hook block, policy gap): **STOP → CAPTURE (worktree, tip, paths, exact error) → ELEVATE**. No workaround commits, no bypass, no scope expansion. -- Forbidden always: mm-role/CLI critics, `*-fast` model slugs, oracle weakening, `#[ignore]`, update-branch button, unsigned commits on authority paths, manifest/index on T, **`pkill`/`killall` of git across worktrees**, **`rm` of `index.lock`/`gc.pid`** (`process.git-pkill-lock-race` — wait or `scripts/cursor/safe-stale-git-lock.sh`). - -## D. Close (before you exit) - -1. **Receipt** — implementer: schema-valid `/.cursor/receipts/.json` (lane, not hub — hub-only = `process.receipt-location-drift`); critic: verdict + `provenByExecution` per finding in the same lane worktree; scout/auditor/recon: findings map with E/I/U tags; planner: updated SSOT section, not a new plan file. Validate via hub absolute path until the ratchet lands: `node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs `. -2. **Flow delta** — one line: what changed for whoever consumes your output (e.g. "leaf ready to admit", "BLOCK: regression pin missing", "lanes X/Y disjoint, Z collides"). -3. **Exit** — return and stop. No CI-watch loops after your receipt; watching belongs to babysit. If you learned a process defect, elevate one line for the SSOT/failure-classes — do not silently patch process. - -## Orchestrator-only (parent session) -- After any admission rebuild: `scripts/cursor/check-admit-sync.sh ` must be green or you push/re-dispatch **this turn** (`process.admit-tip-unpublished`). -- Hot-set: when a lane exits, refill or explicitly PARK; babysit-only is banned. -- Class findings from critics → open/confirm sweep beads the same turn. diff --git a/.cursor/rules/console-base-lock.mdc b/.cursor/rules/console-base-lock.mdc deleted file mode 100644 index 2d8880b4c..000000000 --- a/.cursor/rules/console-base-lock.mdc +++ /dev/null @@ -1,70 +0,0 @@ ---- -description: Console BASE_LOCK — fail-closed lane contract (Cursor port of .claude/workflows/lane-fanout.js). Prevents multi-round fix loops by making skim-able prose into required behavior. -alwaysApply: true ---- - -# Console BASE_LOCK (Cursor-native) - -This is the Cursor equivalent of `BASE_LOCK` in `.claude/workflows/lane-fanout.js`. Violating it is rejected work, not a follow-up. - -## Transport - -- Prefer **Cursor-native** `Task` subagents with model `inherit` (or another non-`-fast` Cursor Task model). Use project agents `.cursor/agents/lane-implementer.md` and `lane-critic.md` when dispatching. **Never** select `*-fast` model slugs — no extra inference spend for speed. -- Do **not** route critics or implementers through `.grok/bin/mm-role` / `claude -p` / `codex exec` unless the user explicitly asks for that CLI receipt path (`CURSOR_ALLOW_MM_ROLE=1`). Hooks deny these by default. -- Planner/integrator (this session) does not implement product code inline when a lane can own it (**forbid_raw_execute** — oyatie pipeline rule). -- **Trial before scale** (oyatie `deliver.js` / console-complete): prove one lane green before N-way fan-out. Width over depth — another reviewer is cheap; another build round is expensive. - -## One writer / one shot - -- **One writer per worktree.** Before writing, check `git status --porcelain` and file mtimes on the target root. A finished agent ID is not evidence the worktree is free. -- **One commit that closes the known defect class**, not a chain of "fix what the last critic said." Before the first push, enumerate every already-known blocker (prior critic receipts, unresolved review threads, Sol/Opus findings) and fix them in that commit. Missing this inventory is failure class `process.known-blockers-skipped`. -- If you are about to make the **third spelling** of the same bug class, stop patching. Replace the mechanism with a total primitive (see lane-fanout BASE_LOCK "THIRD SPELLING"). Say so in the receipt `followUps`. -- **Preflight forge** before any merge/thread-resolve fan-out: `bash scripts/cursor/preflight-forge.sh` (failure class `ops.gh-auth-stale`). -- **Restack is part of landing** when `main` moves: `git rebase origin/main` then `git push --force-with-lease` in the PR worktree (never `gh pr update-branch`). Briefs for open PRs must allow this (`process.tip-land-without-restack`). -- **Parent owns completion.** If a subagent dies (quota/usage), the parent continues or re-dispatches **once** — do not idle on a dead agent ID (`process.subagent-quota-death`). - -## Git (lane workers) - -Unless the brief explicitly makes you the integration owner: - -- Permitted: `status`, `diff`, `log`, `add `, `commit` inside owned roots; **lane self-provisioning** only under the hub's `.worktrees/` directory: - - Preferred helper: `bash scripts/cursor/provision-lane-worktree.sh [--kind lane|admission] ` - - Or verbatim: `git [-C ] worktree add /.worktrees/ -b lane/|admission/ origin/main` (hook-allowlisted; see `git-lock-enforcer.sh`). -- **Why in-repo:** Cursor's External-File Protection prompts "allow edit" for paths outside the opened workspace folder. Sibling checkouts (`../console-lane-*`, `../console-wave1-admit`, etc.) are outside `/…/console` and cause constant approve prompts. Nesting under `/.worktrees/` (already gitignored) keeps edits in-workspace when the hub folder is open. Multi-root `.code-workspace` is **not** a reliable fix (secondary roots still prompt). -- Forbidden: sibling worktrees outside the hub (`../console-lane-*`, `~/Developer/console-lanes/…`), `stash`, `reset`, `checkout `, `rebase` (except integration `git rebase origin/main`), `merge`, `clean`, `push --force` (non-lease), `worktree add|remove` **outside** the `.worktrees/` provisioning shape above, creating arbitrary branches, GitHub **Update branch** button / `gh pr update-branch` (unsigned two-parent merge breaks the authority train). -- **Never unstick with process murder:** `pkill`/`killall`/`pgrep|kill` aimed at `git`, `git-lock-enforcer`, homebrew git binaries, or worktree/admission path patterns; never `rm` `index.lock` / `HEAD.lock` / `gc.pid` / `.git/**/*.lock`. That is failure class `process.git-pkill-lock-race` — Agent A holds the hub lock → Agent B kills/rm → wedged or corrupt git for every lane. Prefer wait / `timeout` / `GIT_OPTIONAL_LOCKS=1` read-only probes. Stale lock only via `bash scripts/cursor/safe-stale-git-lock.sh ` (PID dead + age gate). Operator escape: `CURSOR_ALLOW_GIT_PKILL=1` (does **not** follow from `CURSOR_ALLOW_GIT_DANGEROUS`). -- In-flight sibling worktrees are grandfathered for edits/commits already checked out; **do not create new siblings**. Prefer opening the hub (`/Users/jasonlee/Developer/console`) so `.worktrees/*` is inside the workspace. - -Integration owner may push, `git rebase origin/main`, `git push --force-with-lease`, squash-merge with `gh pr merge --squash`, and open PRs — after `preflight-forge.sh`. - -See also `.cursor/failure-classes-2026-08-10.md` for the measured loop causes. -## Build / verify - -- Scope every Cargo invocation (`-p `, `--manifest-path backend/Cargo.toml`). Never bare `cargo test` / `--workspace` from repo root habits that hide scope. -- Postgres targets: `tools/ci/cargo_needs_postgres.sh --only --num-threads=1`. **Never `--workflow-only`** (selects zero dark targets, exits 0 = false green). -- Never weaken the oracle: no deleted tests, no `#[ignore]`, no conforming assertions to the defect. -- Test controls by **executing** them (mutate → red). `contains()` over a gate's source is not evidence. - -## Enforcement must see its subject - -Before adding/changing any gate/check/census/guard, answer in the lane receipt `enforcementPlacement` (required field — cannot be omitted): - -1. WHERE it runs, and whether its subject EXISTS yet. -2. Finest distinction its data source can express. -3. How "examined zero subjects" **fails** (never passes). - -If no enforcement added: exactly `n/a - adds no enforcement`. - -## Peripherals are part of the change - -Update owned module docs / registries / baselines / beads / docs that described the old behaviour. Enumerating comments next to newly-total code are false claims, not stale prose. Receipt field `peripheralsUpdated` is required. Docs under `docs/**` need doc-manifest regeneration in the same commit range. - -## Done means receipt validates - -Before claiming done / asking to merge, write the receipt **inside the lane worktree** (not the hub): `/.cursor/receipts/.json`, then validate. The validator currently lives only in the hub (untracked until `chore-cursor-ratchet` lands), so invoke it by absolute hub path against the lane receipt: - -```bash -node /Users/jasonlee/Developer/console/scripts/cursor/validate-lane-receipt.mjs /.cursor/receipts/.json -``` - -Exit nonzero = not done. Do not open a second fix round to invent fields — fill them before the first push. Hub-only receipts are failure class `process.receipt-location-drift`. diff --git a/.cursor/rules/console-standing-lenses.mdc b/.cursor/rules/console-standing-lenses.mdc deleted file mode 100644 index 250fdce13..000000000 --- a/.cursor/rules/console-standing-lenses.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Standing review lenses for Console PRs and lane critiques (Cursor port of STANDING_LENSES + provenByExecution convergence). -alwaysApply: true ---- - -# Standing lenses (every substantive review) - -Apply **all** of these; they are additive, never replaced by ad-hoc lenses. - -1. **ORACLE INTEGRITY** — Did any test get deleted, ignored, filtered, or conformed to the defect? Would mutating the control go red? -2. **PERIPHERAL DRIFT** — Diff vs tree: module docs, registries, baselines, `docs/**`, beads. Enumerations that the change made incomplete are defects. Verify `peripheralsUpdated` against the tree both directions. -3. **ENFORCEMENT PLACEMENT** — Can the gate see its subject? Sequence position, data-source fineness, totality vs spelling list, examined-zero fails. Verify `enforcementPlacement`. -4. **FALSE GREEN** — `--workflow-only`, empty `commandsRun`, unverified edges treated as live, Buck targets that cannot materialize `include_str!` inputs, CI steps that never build the new hermetic target. - -## Convergence (do not "fix round" forever) - -- `blocker` → blocks merge. -- `major` blocks **only if** `provenByExecution: true` (you ran a command / hostile probe and observed the failure). Unexecuted opinion is not a merge bar by itself — file it, don't start a treadmill. -- **Tie-break:** if two critics disagree, **provenByExecution wins**. An APPROVE cannot stand over an unfixed proven major/blocker. -- **One inventory, one fix commit, one re-critique.** Before the *first* push on a PR tip: list every unresolved review thread + every prior critic finding; fix all `blocker` and `major+proven` (and cheap clear P1s) in **one** commit. After that: - - New finding, **new class**, proven → at most **one** more fix commit. - - New finding, **same class** → mechanism replacement, not round 3. - - New **unproven** bot/Codex P2/P1 opinion → reply + resolve or defer to a bead; **do not** open another implementation round (`process.bot-thread-treadmill`). -- Bot review threads are inputs to the **pre-push inventory**, not an infinite queue after every push. - -## 16-lens scope (canonical contract, not stamping) - -Canonical lenses: the marked list in `AGENTS.md`, projected into `CLAUDE.md` and `README.md` and drift-checked by `scripts/check-reasoning-lens-manifest.mjs`. Smallest task-appropriate subset **≥2**; full 16-lens battery only at **root-level** decisions (wave topology, keeps/overturns, mechanism replacements) — leaf lanes inherit the root battery. High-risk domains (authz/migration/contracts/approval/HR-payroll/release/production/compliance) SHOULD include Red Team, Operability / Day-2, Blast-radius / cell-based, Zero-trust / defense-in-depth, matching AGENTS.md. **Reject `all-16` with empty or templated challenges** — a lens without a recorded challenge is a stamp, not reasoning. - -## Critic transport - -Native Cursor Grok (or another Cursor `Task` model). Write verdict to `.cursor/receipts/-critic.json` with schema fields: `verdict` (`APPROVE`|`BLOCK`), `findings[]` each with `severity`, `claim`, `failureScenario`, `location`, `provenByExecution`, `ownerLease`. -Validate with `node scripts/cursor/validate-lane-receipt.mjs --schema critic …`. -Never route critics through `mm-role`/CLI wrappers; never pay for `-fast` variants. \ No newline at end of file diff --git a/.gitignore b/.gitignore index a8e6755e9..3ebcc74ef 100644 --- a/.gitignore +++ b/.gitignore @@ -69,7 +69,7 @@ deploy/talos/**/talosconfig .beads/proxieddb/ # Grok runtime board (materialized by tools/ci/ingest-soft-reds.mjs; not custodied) -.grok/harness/lane-board.live.json +ci/harness/lane-board.live.json # Agent working directories must never be committed. Load-bearing content is # drained to a real home first (see PLAN.md 39); these hold scratch only. From a55d69ff7643f985c15d88d136df71034e2ec161 Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Thu, 20 Aug 2026 08:08:02 -0400 Subject: [PATCH 3/7] chore(agents): stop tracking .omx as well Four files under .omx/ survived the first pass because the sweep enumerated only the six dot-dirs then known. .omc/ and .omx/ are the same class and are now gitignored too. Preserved at refs/preserved/omx before untracking; files remain on disk. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 2 + ...easoning-lens-contract-20260803T101035Z.md | 32 -- ...oning-lens-contract-execution-handoff.json | 86 ----- .../reasoning-lens-contract-handoff.json | 40 --- .omx/plans/reasoning-lens-contract.md | 294 ------------------ 5 files changed, 2 insertions(+), 452 deletions(-) delete mode 100644 .omx/context/reasoning-lens-contract-20260803T101035Z.md delete mode 100644 .omx/plans/reasoning-lens-contract-execution-handoff.json delete mode 100644 .omx/plans/reasoning-lens-contract-handoff.json delete mode 100644 .omx/plans/reasoning-lens-contract.md diff --git a/.gitignore b/.gitignore index 3ebcc74ef..8133005d8 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,5 @@ ci/harness/lane-board.live.json .cursor/ .agents/ .beads/ +.omx/ +.omc/ diff --git a/.omx/context/reasoning-lens-contract-20260803T101035Z.md b/.omx/context/reasoning-lens-contract-20260803T101035Z.md deleted file mode 100644 index 1db260293..000000000 --- a/.omx/context/reasoning-lens-contract-20260803T101035Z.md +++ /dev/null @@ -1,32 +0,0 @@ -# Context — reasoning lens contract - -## Task -Implement the approved task-appropriate reasoning-lens contract in the existing post-pivot worktree, but the user then explicitly invoked `$ralplan`, so produce a durable consensus plan only and do not edit source files. - -## Desired outcome -Make sixteen named lenses a repository-wide reasoning-routing system. `AGENTS.md` is canonical; `README.md` and `CLAUDE.md` contain compact drift-checked manifests. Every nontrivial task selects at least two lenses. High-risk work includes Red Team, Operability/Day-2, Blast-radius/cell-based, and Zero-trust/defense-in-depth unless a reasoned not-applicable exception is recorded. Persist concise decision evidence, never private chain-of-thought. - -## User-locked decisions -- Risk-based subset, not all sixteen on every task. -- Applies to all nontrivial planning, investigation, implementation, and review reasoning. -- Canonical definitions in `AGENTS.md`; compact manifests in `README.md` and `CLAUDE.md`. -- Minimum two lenses; mandatory high-risk core as described above. -- The contract must survive in all three root files. - -## Sixteen lenses -Cartesian doubt; Essentialism/YAGNI; Chesterton's Fence; Contrarian/outside-the-box; Socratic; Pragmatism; Red Team; Systems Thinking; Operability/Day-2; Opportunity Cost; Blast-radius/cell-based; Constant-work/anti-fragility; Shared-nothing/eventual consistency; FinOps/unit-cost; Telemetry-first; Zero-trust/defense-in-depth. - -## Current repository facts -- Worktree: `/Users/jasonlee/Developer/console-post-pivot`, branch `codex/post-pivot-wave0`, based on `origin/main` `9200e875b`. -- The worktree already has an uncommitted Wave-0 truth-reconciliation diff; preserve it. -- Root guidance and the playbook do not yet contain the sixteen-lens contract. -- Four retrospective templates exist under `docs/retros/templates` and have no lens evidence block. -- Existing doc-link gate and CI wiring are uncommitted in this worktree. -- `package.json` and the documentation CI job are the natural validator integration points. - -## Constraints -- Ralplan planning-only boundary: write only `.omx` planning/context/state artifacts. -- Existing ignored worktree content is discovery evidence, not authority. -- No product API or database changes. -- Grandfather historical ledger records; validate new/materially revised artifacts through a v1 marker. -- Machine checks must not demand or expose private chain-of-thought. diff --git a/.omx/plans/reasoning-lens-contract-execution-handoff.json b/.omx/plans/reasoning-lens-contract-execution-handoff.json deleted file mode 100644 index e862c108f..000000000 --- a/.omx/plans/reasoning-lens-contract-execution-handoff.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "schema": "console.reasoning-lens-execution-handoff.v1", - "task": "reasoning-lens-contract", - "historical_plan_handoff": ".omx/plans/reasoning-lens-contract-handoff.json", - "planning_artifacts": { - "context": { - "path": ".omx/context/reasoning-lens-contract-20260803T101035Z.md", - "sha256": "dea80c0a1fa5cc47c7ba12e4ee1c62480cd675e7b26bf33434ddf1fc94be61e4" - }, - "plan": { - "path": ".omx/plans/reasoning-lens-contract.md", - "sha256": "76dc05561d7d6c07ee26afb68ea60841321eab7516f6d0546ba96d41825aad5c" - }, - "architect_critic_handoff": { - "path": ".omx/plans/reasoning-lens-contract-handoff.json", - "sha256": "db3ca7563223e271c6cf481a5766cfe6b96d2c73dbcf8dbaa5fa12092c010fc2" - } - }, - "execution_state": "custody_truth_correction_candidate_resealing", - "implemented_commits": [ - "ed8424f5153b760162545e0ea669a4be5950280a", - "963302685bae2c28d7c6107dc2bb0b4f902392cd", - "b263f6da2b282e1875439d4bfaad05c95fb13f5d", - "b87d09596cbbd1775fbfd0e82371a8a0a08e39a2", - "aba19c8db6fa66ee4a6e642f72471e00dc65483f", - "baadf03cf4692754fd0b964834e324d14e48f20e" - ], - "superseded_authority_tips": [ - { - "sha": "3885ed49b4bf2906f75c1907cfcd7f03f9aac0c3", - "disposition": "revoked; never merge or treat as implementation authority" - }, - { - "sha": "db7eda3ec2783ca93039b9d03cc2aaade613a927", - "disposition": "revoked after ignored-file reference audit contradicted its self-contained custody claim; hosted results on this tip are source evidence only" - }, - { - "sha": "17f84f210a9dbf32987cdff36d4aeabaff4540ac", - "disposition": "refused in exact-object review because three durable statements implied external workbook custody had completed when no usable destination or read-back existed; never pushed and never merge" - } - ], - "implementation": { - "frozen_vocabulary": "implemented", - "root_manifests": "implemented", - "evidence_templates": "implemented", - "structural_and_diff_validator": "implemented", - "ci_and_local_mirror_wiring": "implemented", - "durable_restart_handoff": "implemented", - "provenance_ref_custody": "implemented", - "tenant_scope_corrections": "implemented", - "security_workflow_direct_binary_fix": "implemented", - "pivot_boundary_reconciliation": "implemented", - "generated_test_face_reconciliation": "implemented", - "provenance_safe_deploy_test_harness": "implemented", - "current_lane_authority_reconciliation": "implemented", - "accepted_adr_path_filter_reconciliation": "implemented", - "production_operation_authority_hold": "implemented; destructive drill and recovery scripts fail closed with exit 78 before substantive action", - "program_registry_continuity_reset": "implemented; current worktree, branch, lane assignments, and all current capability states reset to null/HOLD while exact historical state remains hash-bound", - "mjs_test_reachability_portability": "implemented; repository root derives from import.meta.url and a temporary-location regression executes in CI", - "shared_test_fixture_mapping_and_ci_reachability": "implemented; mobile and dispatch shared fixtures are exported/mapped and three previously dark PostgreSQL binaries are in the serialized lane", - "personal_data_reader_scope_alignment": "implemented; retention and completeness readers share the non-pg_catalog, non-temporary r/p/m/f catalog universe with schema-qualified identities", - "rule_c_json_classification_reconciliation": "implemented; exactly five audited free-form JSONB omissions now include undeclared and are catalog-test locked", - "touched_api_contract_reconciliation": "implemented; site creation documents branch scope and exit-case creation returns the documented HTTP 201", - "ignored_omx_reference_reconciliation": "implemented; ten stale pre-pivot evidence dependencies are explicitly retired by hash and two restricted workbook profiles are classified with their source as restricted external-custody inputs; preservation or discard and any read-back remain pending", - "whole_disk_custody_boundary": "documented; Console authority does not make dirty or unpublished Oyatie, TencentDB-Agent-Memory, or Asterinas state safe to erase" - }, - "verification": { - "validator_unit_tests": "40/40 passed", - "live_structural_validation": "passed; every current governed evidence block accepted", - "event_base_matrix": "pull request, branch push, branch creation, tag, dispatch, and unknown-event fail-closed paths covered", - "ci_wiring_contract": "51/51 preflight tests and 12/12 local-mirror tests passed; generated matrices rejected 525 run/action/composite bypass mutations", - "changed_rust_units": "153 app, 55 platform-authz, 4 compliance REST, 3 reporting REST, and 2 registry REST tests passed; all-target clippy passed with warnings denied", - "changed_postgres_regressions": "HR exit evidence 1/1, registry adapter 6/6, registry REST 19/19, workorder/dispatch shared-fixture targets 15/15, and personal-data classification 27/27 with one intentional generator ignored", - "security_and_contracts": "cargo-audit 0.22.2 direct-binary invocation passed with the governed warning; security workflow, OpenAPI drift 11/11, ADR 29/29, and 397-file link gates passed", - "generated_faces": "cheap first-party admission and the complete first-party plus lock-pinned Reindeer closure passed; shared mobile/dispatch test-support mappings are generated; 335 Rust test targets are defined, 325 are CI-reachable, and the exact dark baseline shrank from 13 to 10", - "production_and_registry_holds": "production hardening checker passed 234 assertions and its mutation suite passed 58/58; truth-ledger and fanout validation passed 54/54 against the hash-bound all-HOLD continuation reset", - "mjs_portability": "23/23 executed-test reachability tests passed, including invocation from a temporary directory outside the repository", - "superseded_hosted_ci": "run 30834869062 on revoked T 3885ed49b4bf2906f75c1907cfcd7f03f9aac0c3 failed the serialized PostgreSQL lane because mobile_evidence_fixtures.rs was absent from the generated source map; the mapping is now explicit, all four affected Buck targets build, and their 15 PostgreSQL tests pass locally", - "retention_scope_repair": "27 personal-data classification assertions passed against disposable PostgreSQL, including isolated non-public table, materialized-view, foreign-table, Rule-C, two-year floor, and owner-only SQLSTATE 42501 probes", - "pre_authority_local_verifier": "after the two reproduced repairs, every non-authority fast-verifier stage passed at signed checkpoint baadf03cf4692754fd0b964834e324d14e48f20e; only the two exact-M admission steps correctly rejected that content checkpoint because it was not yet a ledger-only direct-child authority tip", - "superseded_exact_t_verifier": "protected-main simulation and its 72 authenticated checks passed at T 3885ed49b4bf2906f75c1907cfcd7f03f9aac0c3, and npm run verify completed successfully; exact-object review then revoked that T because two documents marked current still dispatched the generic company_conformance/JobPosition pilot that the roadmap and catalog place on HOLD, while accepted ADR-0030 still described required CI path filters that no longer exist", - "final_pr_ci": "pending on the custody-truth-corrected replacement authority tip; run 30840399153 belongs to superseded T db7eda3ec2783ca93039b9d03cc2aaade613a927 and cannot authorize the replacement pair", - "exact_sha_review": "source and exact-object review passed at superseded T db7eda3ec2783ca93039b9d03cc2aaade613a927, but the subsequent ignored-file audit disproved its self-contained custody claim; exact-object review refused local T 17f84f210a9dbf32987cdff36d4aeabaff4540ac because three statements implied completed external custody despite no usable destination or read-back; replacement exact-SHA review is pending" - }, - "continuation": "Seal a new signed candidate and ledger-only direct-child authority tip only after the ignored OMX reference edges and whole-disk custody boundary are reviewed. Then pass exact-T protected-main simulation and the complete local verifier, finish exact-SHA review and hosted CI for PR #562, obtain a non-author formal GitHub approval, merge, finish any generated release PR under the same review/CI rule, and record the closeout on main. Start future product work only from origin/main and docs/handoffs/2026-08-03-disk-wipe-consolidation.md; do not rerun the historical plan as unstarted, do not treat revoked tips 3885ed49b4bf2906f75c1907cfcd7f03f9aac0c3, db7eda3ec2783ca93039b9d03cc2aaade613a927, or 17f84f210a9dbf32987cdff36d4aeabaff4540ac as authority, and do not erase the workstation until every Console and outside-repository off-device custody decision in the handoff is read-back verified." -} diff --git a/.omx/plans/reasoning-lens-contract-handoff.json b/.omx/plans/reasoning-lens-contract-handoff.json deleted file mode 100644 index 4fc3515aa..000000000 --- a/.omx/plans/reasoning-lens-contract-handoff.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "schema": "omx.ralplan-consensus-handoff.v1", - "task": "reasoning-lens-contract", - "completed_at": "2026-08-03T11:53:36Z", - "planning_artifacts": { - "context": ".omx/context/reasoning-lens-contract-20260803T101035Z.md", - "plan": ".omx/plans/reasoning-lens-contract.md", - "plan_sha256": "76dc05561d7d6c07ee26afb68ea60841321eab7516f6d0546ba96d41825aad5c" - }, - "ralplan_architect_review": { - "order": 1, - "reviewer_role": "architect", - "verdict": "APPROVE", - "iterations": 4, - "remaining_blockers": [], - "summary": "Approved after exact v1 bytes/digest, Git ancestry semantics, root serialization, closed risk schema, target-worktree preflight, disjoint ownership, and byte-complete dirty-worktree containment were specified." - }, - "ralplan_critic_review": { - "order": 2, - "reviewer_role": "critic", - "verdict": "APPROVE", - "iterations": 2, - "remaining_blockers": [], - "summary": "Approved as decision-complete, independently verifiable, correctly scoped to the target worktree, and protected by exact lane ownership and containment checks." - }, - "ralplan_consensus_gate": { - "complete": true, - "required_order": [ - "architect", - "critic" - ], - "observed_order": [ - "architect", - "critic" - ] - }, - "planning_state": "complete", - "execution_started": false, - "recommended_handoff": "ultragoal_with_team" -} diff --git a/.omx/plans/reasoning-lens-contract.md b/.omx/plans/reasoning-lens-contract.md deleted file mode 100644 index b7e6247c0..000000000 --- a/.omx/plans/reasoning-lens-contract.md +++ /dev/null @@ -1,294 +0,0 @@ -# Consensus Plan — Task-Appropriate Reasoning Lens Contract - -## Requirements Summary - -Adopt the sixteen user-provided reasoning lenses as a repository-wide execution policy. Every substantive planning, investigation, implementation, review, and verification task selects the smallest useful subset before acting, re-evaluates it when evidence changes, and records concise conclusions and tradeoffs rather than private chain-of-thought. - -Locked policy: - -- `AGENTS.md` is canonical for the ordered names, definitions, routing rules, and evidence boundary. -- `README.md` and `CLAUDE.md` contain compact identifier-only manifests in the same exact order and point to `AGENTS.md`; they do not duplicate semantics. -- Every nontrivial governed artifact selects at least two lenses. -- High-risk authz, migration, contracts, approval, HR/payroll, release, production, and compliance-sensitive work includes Red Team, Operability / Day-2, Blast-radius / cell-based, and Zero-trust / defense-in-depth, or records a lens-specific not-applicable rationale. -- Historical unmarked ledgers and retrospectives are grandfathered. New or modified governed records use `lens_contract: v1`. -- CI proves manifest/evidence structure for durable governed records only. It must not claim universal task compliance, authentic reasoning quality, or access to private reasoning. - -## RALPLAN-DR Summary - -### Principles - -1. Canonicalize semantics once and project identifiers broadly. -2. Apply the smallest risk-appropriate lens set rather than all lenses mechanically. -3. Make durable decision evidence observable without surveilling private reasoning. -4. Enforce forward without rewriting historical evidence. -5. Validate only objective structure; reviewers judge semantic quality. - -### Decision Drivers - -1. Prevent drift across agent-facing root guidance. -2. Make nontrivial and high-risk lens routing durable and reviewable. -3. Avoid false claims that formatting proves reasoning or compliance. - -### Viable Options - -**A. Dedicated structural validator with diff-aware forward enforcement — chosen** - -- Pros: isolated ownership, precise diagnostics, focused tests, no dependencies, explicit historical grandfathering. -- Cons: adds a script/test pair and needs full Git history in its CI job. - -**B. Fold validation into `check-foundation-gates.mjs`** - -- Pros: reuses an existing command and job. -- Cons: couples reasoning governance to product-foundation checks, enlarges blast radius, and obscures failure ownership. - -**C. Policy and human review only** - -- Pros: least ceremony and no Git-diff logic. -- Cons: cannot detect root-manifest drift or missing forward evidence and fails the locked machine-enforcement outcome. - -## Public Engineering Interfaces - -### Canonical lens vocabulary - -The validator owns this exact frozen constant shape and content: - -```js -const CANONICAL_LENSES_V1 = [ - { name: "Cartesian doubt", definition: "challenge assumptions and separate evidence, inference, and uncertainty." }, - { name: "Essentialism / YAGNI", definition: "pursue the smallest sufficient outcome and avoid speculative scope." }, - { name: "Chesterton's Fence", definition: "understand why an existing constraint or mechanism exists before removing it." }, - { name: "Contrarian / outside-the-box", definition: "test non-obvious alternatives when the default framing may be wrong." }, - { name: "Socratic", definition: "expose hidden premises with focused questions; ask the user only when the answer materially blocks safe progress." }, - { name: "Pragmatism", definition: "optimize for the real-world outcome under actual constraints." }, - { name: "Red Team", definition: "model misuse, adversaries, hostile inputs, and ways the plan can fail." }, - { name: "Systems Thinking", definition: "trace dependencies, feedback loops, second-order effects, and system boundaries." }, - { name: "Operability / Day-2", definition: "account for deployment, diagnosis, maintenance, recovery, and ownership after launch." }, - { name: "Opportunity Cost", definition: "compare the chosen work against the best alternatives in time, complexity, and value." }, - { name: "Blast-radius / cell-based", definition: "contain changes and failures; prefer independently recoverable boundaries." }, - { name: "Constant-work / anti-fragility", definition: "avoid input-dependent blowups, degrade predictably, and use stress to improve the system." }, - { name: "Shared-nothing / eventual consistency", definition: "minimize coordination and make convergence, conflicts, and stale-state behavior explicit." }, - { name: "FinOps / unit-cost", definition: "reason about cost per useful outcome, including operational and scaling costs." }, - { name: "Telemetry-first", definition: "make important state, decisions, failures, and success criteria observable." }, - { name: "Zero-trust / defense-in-depth", definition: "verify every boundary, minimize privilege, and layer independent safeguards." } -]; -``` - -Compute `lens_contract_digest` as lowercase SHA-256 hex over the UTF-8 bytes of `JSON.stringify(CANONICAL_LENSES_V1)` with no appended newline. The frozen v1 digest is `ac1e7d6b8150808ef73e5e3cd1a1e54d2f37eb43e84aaa1370dbbaaff3c44373`. The validator compares root guidance against this constant, not merely against each other, so coordinated drift requires an intentional versioned code change. - -Each root file contains exactly one block delimited by: - -```text - -... - -``` - -Root block serialization is generated and compared exactly, without Unicode or whitespace normalization: - -- `AGENTS.md` body equals `AGENTS_PREAMBLE + "\n\n" + CANONICAL_LENSES_V1.map((lens, index) => `${index + 1}. **${lens.name}** — ${lens.definition}`).join("\n") + "\n\n" + AGENTS_EPILOG`. -- `AGENTS_PREAMBLE` is exactly: `## Task-selected reasoning lenses\n\nAll substantive reasoning, planning, implementation, review, and verification must use the smallest task-appropriate subset. Select at least two lenses before nontrivial work, re-evaluate the set when evidence or risk changes, and do not mechanically apply all lenses.` -- `AGENTS_EPILOG` is exactly: `High-risk authz, migration, contracts, approval, HR/payroll, release, production, and compliance-sensitive work must include Red Team, Operability / Day-2, Blast-radius / cell-based, and Zero-trust / defense-in-depth, or record a lens-specific not-applicable rationale in durable evidence. Report concise conclusions, evidence, decisions, and tradeoffs rather than private chain-of-thought.` -- `README.md` and `CLAUDE.md` bodies are identical and equal `MANIFEST_PREAMBLE + "\n\n" + CANONICAL_LENSES_V1.map((lens, index) => `${index + 1}. ${lens.name}`).join("\n")`. -- `MANIFEST_PREAMBLE` is exactly: `## Reasoning lens manifest\n\nCanonical definitions and routing rules live in [AGENTS.md](AGENTS.md#task-selected-reasoning-lenses). This identifier-only projection is drift-checked and does not duplicate policy.` -- Each body is enclosed immediately by the shared start/end markers, with exactly one newline after the start marker and before the end marker. - -Thus `AGENTS.md` carries full semantics and `README.md`/`CLAUDE.md` carry an exact identifier projection only. - -### Durable evidence block - -Governed Markdown uses exactly one marker-delimited, canonical JSON block: - -````text - -```json -{ - "lens_contract": "v1", - "lens_contract_digest": "", - "task_class": "planning", - "risk_class": "standard", - "risk_domains": [], - "selected_lenses": [ - "Cartesian doubt", - "Pragmatism" - ], - "task_fit": { - "Cartesian doubt": "Separated repository evidence from inference.", - "Pragmatism": "Selected the smallest enforceable outcome." - }, - "mandatory_lens_exceptions": {}, - "findings": [ - "Root manifest drift needs a structural gate." - ], - "decisions_changed_or_rejected": [], - "lens_set_changes": [] -} -``` - -```` - -Allowed values: - -- `task_class`: `planning`, `investigation`, `implementation`, `review`, `verification`, `trivial_read_only`. -- `risk_class`: `standard` or `high`; absent only for `trivial_read_only`. -- `risk_domains`: canonical-order subset of `authz`, `migration`, `contracts`, `approval`, `hr_payroll`, `release`, `production`, `compliance_sensitive`, `other`. - -Schema rules: - -- Reject unknown/missing keys. Extracted JSON payload bytes between the `json` fences must equal `JSON.stringify(parsed, null, 2) + "\n"` exactly; this rejects noncanonical formatting and duplicate JSON keys because reserialization differs. -- Selected lenses are unique and in canonical order. Nontrivial records select 2–16. -- `task_fit` keys equal selected lenses exactly and values are nonblank outcome-level explanations. -- High-risk records select the four mandatory lenses. An omitted mandatory lens requires a nonblank exception keyed only by that missing lens; exceptions for already selected or nonmandatory lenses fail. -- Standard records require `mandatory_lens_exceptions: {}`. For high-risk records, the mandatory-lens set must be a subset of `selected_lenses ∪ Object.keys(mandatory_lens_exceptions)`; exception keys are disjoint from selected lenses and are a subset of the four mandatory lenses. -- Nontrivial `findings` contains at least one concise string. Decision and lens-change arrays may be empty to avoid fabricated activity. -- `risk_class: standard` requires `risk_domains: []`. `risk_class: high` requires a nonempty canonical-order domain subset. `other` means a high-risk domain outside the enumerated taxonomy and is never valid with `standard`. -- `trivial_read_only` omits `risk_class` and has empty `risk_domains`, `selected_lenses`, `task_fit`, exceptions, findings, decisions, and changes. -- Strings beginning `EXAMPLE:` are allowed only under `docs/retros/templates/` and rejected in governed records, preventing unchanged template-copy evidence. -- The validator never scores prose depth, detects chain-of-thought, or claims that reasoning occurred. - -## Implementation Steps - -1. **Root guidance contract** - - Extend `AGENTS.md` with the existing shared markers, exact sixteen definitions from the user instruction, risk-based selection, two-lens minimum, high-risk core including approval, re-evaluation rule, and concise-evidence/private-reasoning boundary. - - Add compact exact-name manifests to `README.md` and `CLAUDE.md`. Preserve `CLAUDE.md`'s no-policy-duplication rule by describing the manifest as an identifier projection only. - -2. **Playbook and templates** - - Replace the playbook's review-only lens paragraph with the all-substantive-task routing lifecycle: select, classify risk, act, re-evaluate, and persist outcomes when a durable artifact exists. - - Document the distinction between normative all-task policy and CI-provable durable evidence. - - Add canonical v1 example blocks to pre-mortem, post-mortem, runnable-rollback, and workflow-experiment templates. Use `EXAMPLE:` values. Make the runnable rollback example high-risk with `risk_domains` including `approval`, `release`, and `production` and the four mandatory lenses; other templates may demonstrate standard routing. - -3. **Dependency-free validator and tests** - - Add `scripts/check-reasoning-lens-contract.mjs` and its Node test file. - - Structural mode validates the three root manifests, canonical `AGENTS.md` definitions, all template examples, and every existing opt-in evidence block while grandfathering unmarked historical records. - - `--changed-since BASE` must pass `git cat-file -e "$BASE^{commit}"` and `git merge-base --is-ancestor "$BASE" HEAD`, then runs `git diff --name-status -z --no-renames "$BASE" HEAD --`. Missing objects, noncommits, shallow-history gaps, and nonancestor bases fail closed. Added or modified direct children of `docs/program/ledger/*.md` and recursive `docs/retros/**/*.md` outside `docs/retros/templates/**` must contain a valid v1 block. Deletions are ignored; with `--no-renames`, a rename becomes deletion plus addition and the added target is enforced. - - Failure output includes path, marker line or field, enforcement mode, base SHA, and head SHA. Invalid/unreachable bases fail closed. - - Use temporary Git repositories for diff-aware tests so the dirty product worktree is never mutated. - -4. **Package, CI, and inventory integration** - - Add `check:reasoning-lens-contract` to `package.json`. - - Add `docs/retros/**` and the three root guidance files to both CI path filters. - - In `repo-gates`, set checkout `fetch-depth: 0`, run the validator tests, then select enforcement mode exactly: - - pull request: `--changed-since ${{ github.event.pull_request.base.sha }}`; - - non-tag branch push with nonzero `${{ github.event.before }}`: `--changed-since` that SHA; - - tag push, zero-before branch creation, and workflow dispatch: structural mode. - - Document the new root package command in `docs/CI-GATES.md`; retain current doc-link integration and allow the existing foundation inventory gate to verify command parity. - - Extend `scripts/check-ci-preflight.mjs` and `scripts/check-ci-preflight.test.mjs` as the single owner of CI-shape verification. It must assert both root path-filter lists include `docs/retros/**` and the three root guidance files, `repo-gates` uses `fetch-depth: 0`, validator tests and gate steps are present, and the exact PR/normal-push/zero-before/tag/workflow-dispatch mode-selection shell is retained. - -5. **Independent integration and verification** - - Execution begins only from `/Users/jasonlee/Developer/console-post-pivot`. Fail before any source write unless `pwd -P` equals that path, `git branch --show-current` equals `codex/post-pivot-wave0`, `git rev-parse HEAD` equals `9200e875b5362ef88b9a1af20dfc43ed3f07a970`, and `git merge-base HEAD origin/main` equals the same SHA. - - Before edits, preserve `git status --porcelain=v1`, `git diff --binary`, and `git diff --cached --binary` as `.omx/context/reasoning-lens-preexecution-*` artifacts inside the target worktree. Also enumerate `git ls-files -z` plus `git ls-files --others --exclude-standard -z`, sort paths by raw UTF-8 byte order, and write a JSON manifest containing each path's tracked/untracked class, file type, mode, byte length, and SHA-256 of its working-tree bytes. Copy every pre-existing untracked file, preserving relative paths and bytes, into `.omx/context/reasoning-lens-preexecution-untracked/` and archive that directory. Planning artifacts under `.omx/**` are excluded from the manifest itself. - - After execution, regenerate the manifest and require byte-for-byte equality for every path outside the union of the three lane writable allowlists. Reject changed, deleted, or newly created tracked/untracked paths outside that union. Keep the untracked content archive as recovery evidence until integration completes. - - Never reset, checkout, clean, stash, rebase, or broadly rewrite the existing Wave-0 diff. - - Preserve the existing Wave-0 dirty diff and edit only the lane allowlists below. - - Run targeted validator tests/gate, then doc links, ADR governance, foundation inventory, CI preflight tests/gate, package-lock consistency, and diff checks. - - Independently review for false compliance claims, copied example acceptance, Git-event mistakes, hidden dependency additions, and unrelated diff changes. - -## Acceptance Criteria and Test Plan - -### Unit - -- Root block: missing, duplicate, reordered, renamed, whitespace-normalized, Unicode-lookalike, coordinated three-file drift, definition drift, and duplicate marker cases fail. -- Evidence JSON: unknown/missing keys, duplicate keys, noncanonical formatting, wrong digest, unknown/duplicate/out-of-order lenses, one-lens nontrivial selection, mismatched `task_fit`, malformed arrays, and empty required findings fail. -- High risk: all four mandatory lenses pass; missing lens without keyed rationale fails; valid keyed exception passes; exception for selected/nonmandatory lens fails; approval risk is high. -- Trivial read-only: exact empty shape passes; risk class or evidence content fails. -- `EXAMPLE:` passes only in template paths and fails in governed records. - -### Integration - -- Temporary Git histories prove A/M enforcement, D ignore, rename-as-D+A behavior, invalid/unreachable base failure, shallow-history failure, recursive retro scanning, flat-ledger-only scanning, template exclusion, and historical unmarked grandfathering. -- `scripts/check-ci-preflight.test.mjs` proves PR, normal push, zero-before push, tag, and workflow-dispatch modes plus path filters, checkout depth, and validator steps. - -### End-to-end repository verification - -```bash -node --test scripts/check-reasoning-lens-contract.test.mjs -npm run check:reasoning-lens-contract -npm run check:doc-links -node --test scripts/check-doc-links.test.mjs -npm run test:adrs -npm run check:adrs -npm run check:foundation-gates -node --test scripts/check-ci-preflight.test.mjs -npm run check:ci-preflight -npm run check:package-lock -git diff --check -git status --short -``` - -When validating a real diff base, additionally run `npm run check:reasoning-lens-contract -- --changed-since `. - -## Risks and Mitigations - -- **Checkbox reasoning:** gate only durable structure; semantic quality remains independent review responsibility. -- **False universal-compliance claim:** docs and output explicitly say CI covers designated records only. -- **Private reasoning leakage:** fields accept conclusions/evidence/tradeoffs only; no trace scoring or CoT detection. -- **Historical churn:** unmarked history is grandfathered; any future A/M governed record opts into v1. -- **Template false green:** reserved examples fail outside template paths. -- **Git event errors:** pin full history, exact event bases, zero-before/tag fallback, and fail-closed reachability. -- **Dirty-worktree collision:** no resets/checkouts/broad formatters; inspect targeted diff before and after. - -## ADR - -### Decision - -Adopt `AGENTS.md` as canonical lens policy, root identifier-only projections, a frozen v1 digest, and a dedicated structural/diff-aware validator for forward durable evidence. - -### Drivers - -- Cross-agent consistency. -- Risk-proportionate, observable decision evidence. -- Honest enforcement boundaries and preserved history. - -### Alternatives considered - -- Human-review-only policy. -- Integration into the existing foundation gate. - -### Why chosen - -The dedicated validator is the smallest design that detects drift and forward evidence omissions without coupling reasoning governance to product gates or pretending to prove private reasoning. - -### Consequences - -- New/modified governed records carry canonical JSON metadata. -- CI needs full Git history in the repo-gates job. -- Reviewers remain responsible for honest task/risk classification and evidence quality. - -### Follow-ups - -- Observe false-positive and ritualization signals for two delivery waves. -- Change semantics only through a versioned v2 contract and migration plan. -- Keep historical v1 evidence immutable except through ordinary reviewed edits. - -## Available Agent Types and Execution Staffing - -Relevant roster: `writer`, `executor`, `test-engineer`, `verifier`, `code-reviewer`, `architect`, `critic`, `explore`, `debugger`, `git-master`, `code-simplifier`, and optional taxonomy advisor `scholastic`. - -Recommended Team + Ultragoal staffing: - -- Writer lane, high reasoning. Writable allowlist: `AGENTS.md`, `README.md`, `CLAUDE.md`, `docs/program/agentic-engineering-playbook.md`, and `docs/retros/templates/*.md` only. -- Executor/test-engineer lane, high reasoning. Writable allowlist: `scripts/check-reasoning-lens-contract.mjs` and `scripts/check-reasoning-lens-contract.test.mjs` only. -- Integration executor lane, medium reasoning, starting after the validator command stabilizes. Writable allowlist: `package.json`, `.github/workflows/ci.yml`, `docs/CI-GATES.md`, `scripts/check-ci-preflight.mjs`, and `scripts/check-ci-preflight.test.mjs` only. -- Verifier and code-reviewer, high reasoning: read-only sequential verification and adversarial review. -- Ultragoal leader owns durable checkpoints and shared-file integration. - -Launch hints: - -```bash -cd /Users/jasonlee/Developer/console-post-pivot -$ultragoal /Users/jasonlee/Developer/console/.omx/plans/reasoning-lens-contract.md -$team 3 "Implement /Users/jasonlee/Developer/console/.omx/plans/reasoning-lens-contract.md in /Users/jasonlee/Developer/console-post-pivot with disjoint writer, validator-test, and CI-inventory lanes; preserve the existing Wave-0 diff." -``` - -Team verification path: each lane reports exact files/tests; leader rejects overlaps; verifier runs the full integration command set at the exact head; code reviewer checks enforcement boundaries and diff containment; Ultragoal records evidence and closes only after all gates pass. - -`$ralph` remains an explicit persistent single-owner fallback, not the default. `$autoresearch-goal` and `$performance-goal` are not indicated because this is neither a research nor optimization deliverable. - -## Consensus Improvement Changelog - -- Restored approval work to the high-risk taxonomy. -- Split normative behavior from CI-provable durable evidence. -- Added frozen v1 digest, exact markers, strict canonical JSON, and schema closure. -- Pinned PR/push/tag/workflow-dispatch Git semantics and fail-closed bases. -- Added template-only sentinels and comprehensive temporary-Git regression coverage. -- Pinned the exact target worktree, branch, base SHA, root-block renderer grammar, and CI-shape test owner. -- Added disjoint lane writable allowlists and byte-complete tracked/untracked containment evidence for the existing dirty Wave-0 worktree. From d398ecd98f90a48c349603ec43370aa2f49b907a Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Thu, 20 Aug 2026 12:35:16 -0400 Subject: [PATCH 4/7] chore(hooks): tracked hooks via core.hooksPath, delegating to local ones This repository ran its hooks from an agent working directory (core.hooksPath -> .beads/hooks). Those files are untracked and now gitignored, so a fresh clone has no hooks at all and nothing says so. Meanwhile the ten tracked scripts in tools/hooks/ were not what git was running. Hooks that exist only on the machine that wrote them are not enforcement; they are a local habit. .githooks/ is tracked, reviewable, and identical for everyone. pre-commit refuses staged agent-directory files and staged gitignored files pre-push refuses a workspace that does not compile Local, untracked hooks still run: both delegate to .beads/hooks/ and .git/hooks/.local when present, and propagate their exit code. Beads and any per-developer tooling keep working; they simply stop being the only thing between a mistake and the remote. Verified by firing them, not by reading: a staged .claude file was refused, a local delegate ran, and a local delegate exiting 3 propagated 3 rather than 0. Enable with: git config core.hooksPath .githooks Co-Authored-By: Claude Opus 5 (1M context) --- .githooks/pre-commit | 40 ++++++++++++++++++++++++++++++++++++++++ .githooks/pre-push | 20 ++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100755 .githooks/pre-commit create mode 100755 .githooks/pre-push diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 000000000..d4b768b6c --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,40 @@ +#!/bin/bash +# Tracked, reviewable pre-commit. Blocks what is mechanically detectable. +# +# This repository previously ran hooks from an agent working directory +# (core.hooksPath -> .beads/hooks). Those files are untracked and gitignored, so +# a fresh clone had no hooks at all and nothing said so. Hooks that only exist +# on the machine that wrote them are not enforcement; they are a local habit. +# +# Local, untracked hooks still run: see the delegation at the end. Beads and any +# other per-developer tooling keep working; they simply stop being the only +# thing standing between a mistake and the remote. +set -uo pipefail +if [ -n "${SKIP_TRACKED_HOOKS:-}" ]; then exit 0; fi + +AGENT_DIRS='^\.(claude|codex|cursor|grok|agents|beads|omc|omx)/' +staged=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || true) + +offending=$(echo "$staged" | grep -E "$AGENT_DIRS" || true) +if [ -n "$offending" ]; then + echo "[pre-commit] REFUSED: agent directory files are staged." + echo "$offending" | sed 's/^/ /' + echo " Drain any load-bearing content to a tracked home first" + echo " (see tools/delivery/ and tools/hooks/registration/), then" + echo " leave the directory untracked. It may stay on disk." + exit 1 +fi + +while IFS= read -r f; do + [ -z "$f" ] && continue + if git check-ignore -q "$f" 2>/dev/null; then + echo "[pre-commit] REFUSED: '$f' is gitignored but staged." + exit 1 + fi +done <<< "$staged" + +# Delegate to any local, untracked hook so per-developer tooling survives. +for local_hook in .beads/hooks/pre-commit .git/hooks/pre-commit.local; do + if [ -x "$local_hook" ]; then "$local_hook" "$@" || exit $?; fi +done +exit 0 diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 000000000..5f4f5134a --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,20 @@ +#!/bin/bash +# Refuses to push a tree that does not build. +# +# Cheap check first: a push that cannot compile wastes every downstream +# reader's time, and CI minutes, to learn what one local command knows. +set -uo pipefail +if [ -n "${SKIP_TRACKED_HOOKS:-}" ]; then exit 0; fi + +if command -v cargo >/dev/null 2>&1 && [ -f Cargo.toml ]; then + echo "[pre-push] checking the workspace compiles..." + if ! cargo check --workspace --quiet 2>&1 | tail -20; then + echo "[pre-push] REFUSED: the workspace does not compile." + exit 1 + fi +fi + +for local_hook in .beads/hooks/pre-push .git/hooks/pre-push.local; do + if [ -x "$local_hook" ]; then "$local_hook" "$@" || exit $?; fi +done +exit 0 From caaef5c35a784b796a1496d8804a71813006deb8 Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Fri, 21 Aug 2026 00:31:05 -0400 Subject: [PATCH 5/7] fix(ci): redirect the lane-board output out of the agent directory This change was made locally and never reached the branch. .gitignore carried its half; tools/ci/ingest-soft-reds.mjs did not. That is worse than neither half landing. This PR deletes .grok, and the tool still resolved its output to .grok/harness/lane-board.live.json -- so the first CI run after merge would have recreated the very agent directory the PR removes, and .gitignore would have hidden it. PR #851's description already claimed this redirect was in place. It was not. The claim is now true. Co-Authored-By: Claude Opus 5 (1M context) --- ci/harness/README.md | 2 ++ tools/ci/ingest-soft-reds.mjs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 ci/harness/README.md diff --git a/ci/harness/README.md b/ci/harness/README.md new file mode 100644 index 000000000..6d1619fdc --- /dev/null +++ b/ci/harness/README.md @@ -0,0 +1,2 @@ +# Generated lane-board output lands here. +# Previously written into .grok/harness/, an agent directory that must not be tracked. diff --git a/tools/ci/ingest-soft-reds.mjs b/tools/ci/ingest-soft-reds.mjs index 2a98d1055..3e3496360 100644 --- a/tools/ci/ingest-soft-reds.mjs +++ b/tools/ci/ingest-soft-reds.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * Mechanical soft-red / block ingest → .grok/harness/lane-board.live.json + * Mechanical soft-red / block ingest → ci/harness/lane-board.live.json * * Realizes aspirational "no silent soft reds": any open PR that is BEHIND, * DIRTY, CONFLICTING, or has failing/pending Required-ish signals gets a @@ -17,7 +17,7 @@ import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const root = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); -const boardPath = resolve(root, ".grok/harness/lane-board.live.json"); +const boardPath = resolve(root, "ci/harness/lane-board.live.json"); const args = new Set(process.argv.slice(2)); const dryRun = args.has("--dry-run"); From 83fd3ea7519438aede76e8fbda6f84c68bae2e9c Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Fri, 21 Aug 2026 04:12:30 -0400 Subject: [PATCH 6/7] fix(ci): drain lane-fanout off the untracked agent directory CI preflight still ran node .claude/workflows/lane-fanout.test.mjs, and test:lane-receipt still read lane-fanout.js from that path, after the directory was untracked. Move the harness, its sibling dispatchers, and the receipt fixture under scripts/console/ so CI no longer depends on an agent directory. --- .github/workflows/ci.yml | 2 +- package.json | 2 +- scripts/check-ci-preflight.mjs | 2 +- .../v-lane-receipt-validator-20260812.json | 75 + scripts/console/validate-lane-receipt.mjs | 2 +- .../console/validate-lane-receipt.test.mjs | 6 +- scripts/console/workflows/backlog-audit.js | 690 +++++++++ scripts/console/workflows/lane-fanout.js | 1154 ++++++++++++++ .../console/workflows/lane-fanout.test.mjs | 1358 +++++++++++++++++ scripts/console/workflows/program-tick.js | 435 ++++++ scripts/console/workflows/review-gate.js | 137 ++ scripts/console/workflows/scout.js | 499 ++++++ scripts/console/workflows/slice.js | 528 +++++++ scripts/console/workflows/stale-take-audit.js | 243 +++ scripts/cursor/validate-lane-receipt.mjs | 4 +- tools/lanes/wfcheck.mjs | 2 +- 16 files changed, 5129 insertions(+), 10 deletions(-) create mode 100644 scripts/console/fixtures/v-lane-receipt-validator-20260812.json create mode 100644 scripts/console/workflows/backlog-audit.js create mode 100644 scripts/console/workflows/lane-fanout.js create mode 100644 scripts/console/workflows/lane-fanout.test.mjs create mode 100644 scripts/console/workflows/program-tick.js create mode 100644 scripts/console/workflows/review-gate.js create mode 100644 scripts/console/workflows/scout.js create mode 100644 scripts/console/workflows/slice.js create mode 100644 scripts/console/workflows/stale-take-audit.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12585f721..bd4a19de0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -231,7 +231,7 @@ jobs: - name: Lane fan-out harness preflight id: lane-fanout if: ${{ !cancelled() && steps.npm-ci.outcome == 'success' }} - run: node .claude/workflows/lane-fanout.test.mjs + run: node scripts/console/workflows/lane-fanout.test.mjs - name: Workflow test-runner credential literals id: test-credentials if: ${{ !cancelled() && steps.npm-ci.outcome == 'success' }} diff --git a/package.json b/package.json index b683dceec..e3b2b2d7d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "kic:generate-nhis-loss-report-fixture": "node scripts/korean-connectivity/nhis-edi-loss-report.mjs", "kic:local-cert-login-fixture": "node scripts/korean-connectivity/local-certificate-agent.mjs", "check:console-truth-ledger": "node scripts/console/validate-console-truth-ledger.mjs", - "check:lane-receipt": "node scripts/console/validate-lane-receipt.mjs --dir .cursor/receipts", + "check:lane-receipt": "node scripts/console/validate-lane-receipt.mjs --dir scripts/console/fixtures", "test:lane-receipt": "node --test scripts/console/validate-lane-receipt.test.mjs", "test:console-authority-bootstrap": "node --test scripts/console/verify-console-pr-authority-bootstrap.test.mjs scripts/console/release-please-bot-candidate.test.mjs", "check:node-audit-exceptions": "node scripts/check-node-audit-exceptions.mjs", diff --git a/scripts/check-ci-preflight.mjs b/scripts/check-ci-preflight.mjs index e2a05f6db..23b9a53e9 100644 --- a/scripts/check-ci-preflight.mjs +++ b/scripts/check-ci-preflight.mjs @@ -941,7 +941,7 @@ const requiredJobRunContracts = Object.freeze({ proofRun("Executed-tests ratchet — a test binary must have a path from a workflow step", "npm run check:executed-tests", { if: preflightNpmCiHeavyCondition }), proofRun("JavaScript test reachability ratchet", "npm run check:js-test-reachability", { if: preflightNpmCiDependentCondition }), proofRun("JavaScript test reachability unit tests", "npm run test:js-test-reachability", { if: preflightNpmCiDependentCondition }), - proofRun("Lane fan-out harness preflight", "node .claude/workflows/lane-fanout.test.mjs", { if: preflightNpmCiDependentCondition }), + proofRun("Lane fan-out harness preflight", "node scripts/console/workflows/lane-fanout.test.mjs", { if: preflightNpmCiDependentCondition }), proofRun("Workflow test-runner credential literals", "npm run check:test-credentials", { if: preflightNpmCiDependentCondition }), proofRun("Collect failures", "node scripts/ci-collect-failures.mjs", { if: collectFailuresCondition }), ], diff --git a/scripts/console/fixtures/v-lane-receipt-validator-20260812.json b/scripts/console/fixtures/v-lane-receipt-validator-20260812.json new file mode 100644 index 000000000..9d6b6a07c --- /dev/null +++ b/scripts/console/fixtures/v-lane-receipt-validator-20260812.json @@ -0,0 +1,75 @@ +{ + "kind": "lane", + "lane": "v-lane-receipt-validator-20260812", + "status": "done", + "summary": "Tracked lane-receipt schema (scripts/console/lane-receipt.schema.json), dependency-free validator CLI (scripts/console/validate-lane-receipt.mjs), 22-test node:test suite (incl. exact cross-authority parity pins, dual-validation, and --dir examined-zero scans), wired into package.json scripts, ci.yml preflight step, check-ci-preflight proofRun + pins, CI-GATES.md inventory, and regenerated doc manifests.", + "filesChanged": [ + "scripts/console/lane-receipt.schema.json", + "scripts/console/validate-lane-receipt.mjs", + "scripts/console/validate-lane-receipt.test.mjs", + "package.json", + ".github/workflows/ci.yml", + "scripts/check-ci-preflight.mjs", + "scripts/check-ci-preflight.test.mjs", + "docs/CI-GATES.md", + "docs/documentation-manifest.seed.json", + "docs/documentation-index.json", + ".cursor/receipts/v-lane-receipt-validator-20260812.json", + "scripts/verify.mjs", + ".cursor/receipts/v-lane-receipt-validator-20260812-critic.json", + ".cursor/receipts/v-lane-receipt-validator-20260812-critic-r3.json" + ], + "redBaseline": "Planted-red proofs executed and pasted in test suite development: schema-violating receipt (missing required field) exits 1; examined-zero (no-arg invocation) exits 1; foundation-gates docs-drift gate observed RED when test:lane-receipt was undocumented in CI-GATES.md, then GREEN after inventory entry (bisect: clean b2acd80c 6/6 PASS, dbb5443 6/6 PASS, wiring-without-doc FAIL). Post-R2 planted reds: present-but-unrecognised kind ({\"kind\":\"build\"}) in --dir scan observed exit 0 pre-fix, exit 1 post-fix; BUILD_SCHEMA required-list mutation (verification->headSha) observed suite red with drift message, restore green.", + "verification": "npm run test:lane-receipt 22/22 pass; npm run check:lane-receipt (--dir scan of .cursor/receipts) exit 0; node scripts/check-ci-preflight.mjs contract passed; node --test scripts/check-ci-preflight.test.mjs 57/57 pass; js-test-reachability 47 suites exact-wired dark 0; check-mjs-dark-suites --strict ok; foundation-gates 6/6 pass; generate-documentation-manifest --check OK (438 markdown files); node --test scripts/verify.test.mjs 13/13 pass; porcelain clean at tip.", + "contractBreaches": "none", + "headSha": "21fbc60bc3ca5b1e4f21f0d9566bf2c5c0f520a6", + "baseSha": "b2acd80c4d7f340199b9147f6df9318d74af5f8d", + "worktree": ".worktrees/v-lane-validator", + "commands": [ + "npm run test:lane-receipt", + "node scripts/check-ci-preflight.mjs", + "node --test scripts/check-ci-preflight.test.mjs", + "node scripts/check-js-test-reachability.mjs", + "node --test scripts/check-foundation-gates.test.mjs" + ], + "commandsRun": [ + "npm run test:lane-receipt", + "node --test scripts/console/validate-lane-receipt.test.mjs", + "node scripts/check-ci-preflight.mjs", + "node --test scripts/check-ci-preflight.test.mjs", + "node scripts/check-js-test-reachability.mjs", + "node tools/ci/check-mjs-dark-suites.mjs --strict", + "node --test scripts/check-foundation-gates.test.mjs", + "node scripts/console/generate-documentation-manifest.mjs --write", + "node scripts/console/generate-documentation-manifest.mjs --check", + "node --test scripts/verify.test.mjs" + ], + "enforcementPlacement": "WHERE: ci.yml preflight job step 'Console lane-receipt validator regression' runs npm run test:lane-receipt; the suite executes the validator against synthetic fixtures AND scans the real tracked .cursor/receipts directory via --dir, validating every kind-bearing receipt. SUBJECT EXISTS: this lane's receipt is the first kind-bearing tracked receipt; receipts with NO kind property remain under the incumbent validators (a present-but-unrecognised kind FAILS, planted-red proven) (scripts/cursor/validate-lane-receipt.mjs, lane-fanout BUILD_SCHEMA) until the consolidation lane migrates them — the boundary discriminator is the kind field, not a spelling list. FINEST DISTINCTION: per-field JSON Schema 2020-12 validation (required-field union with BUILD_SCHEMA, enum kinds, 40-hex SHA pattern, non-blank strings via minLength+\\S pattern, minItems) plus conditional rules (n/a escape parity, status=done commands, APPROVE-over-blocking-finding conflict). EXAMINED-ZERO FAILS: zero-argument invocation exits 1; --dir scan finding zero kind-bearing receipts exits 1; both proven by planted-red tests; the suite carries an executed floor (after-hook fails under 22 test bodies) and a cross-authority parity test that fails if BUILD_SCHEMA/REVIEW_SCHEMA required fields stop being extractable.", + "peripheralsUpdated": "docs/CI-GATES.md: test:lane-receipt added to 'Root package scripts run by CI' inventory (foundation-gates drift parity); docs/documentation-manifest.seed.json + docs/documentation-index.json regenerated via generate-documentation-manifest.mjs --write; scripts/check-ci-preflight.test.mjs pins updated: preflight run-step 28->29, total 121->122, mutation matrix 363->366; scripts/verify.mjs PLAN classifies the new step (local CI mirror contract)", + "classification": { + "personalData": false, + "holdAdjacent": false, + "notes": "Validator tooling only; no product, contract, migration, HR, or payroll surface touched. Ordinary R2 review path, no GAAC." + }, + "preMortem": "Risk 1: a third mutually-unsatisfiable spelling of the receipt field list forks authority -> mitigated post-R2: laneReceipt.required is the UNION of CI-pinned BUILD_SCHEMA required fields and the agent-ritual fields, an executed parity test pins BUILD_SCHEMA.required as a subset of laneReceipt.required (and REVIEW_SCHEMA of criticReceipt), and this receipt validates under both the tracked and incumbent validators by an executed test. Risk 2: preflight pin drift causes false red on unrelated PRs -> pins co-edited with mutation inventory in same commit, contract test green. Risk 3: undocumented CI step trips foundation-gates docs-drift parity -> hit during development, fixed by CI-GATES.md inventory entry (designed gate worked). Risk 4: validator certifies doctrine-forbidden receipts -> APPROVE-over-blocking-finding and whitespace false-green rules ported from the incumbent with planted-red tests.", + "blastRadius": "Additive: new schema+validator+test under scripts/console/, two package.json scripts, one CI preflight step, one proofRun, doc inventory line, regenerated manifests. No existing gate, workflow, or incumbent validator modified. Post-R2 the successor carries the incumbent's convergence rules (APPROVE-conflict, blank-string guard, n/a escape prefix, status=done commands) with planted-red proofs, so migration to it does not weaken any oracle; the incumbents remain live and untouched until the consolidation lane.", + "detection": "CI preflight step fails on validator regression; check-ci-preflight.mjs proofRun fails if step removed or reordered; foundation-gates docs-drift gate fails if CI-GATES.md inventory diverges from ci.yml; js-test-reachability fails if suite goes dark.", + "rollback": "Single revert of the squash-merge commit restores prior state; no data, migration, or hosted-state coupling.", + "stopConditions": "behind>0 at pre-admission revalidation without clean restack; any required check red on PR; R2 BLOCK with proven finding; foundation-gates or preflight contract red at tip.", + "reviewIdentities": [ + "executor: 29-VLaneValidator (Grok 4.6) authored dbb544347", + "leader: GJC sessions 019ff78e/019ff867 (Fable) authored wiring 88dcbc845, this receipt, and the R2 fix round", + "R2: architect R2-VLaneValidator (Opus, cross-family, read-only). Round 1: BLOCK at 55331bc27 (1 blocker proven: third field-list spelling; 3 major; 5 minor; 6 challenges). One consolidated fix round (9595fc64a + ac6f2a4de). Round 2 delta re-critique: APPROVE with filed findings (1 unproven major routed to consolidation lane). Verdict receipt: .cursor/receipts/v-lane-receipt-validator-20260812-critic.json", + "R3: architect 1-R3-VLaneDelta (Opus, cross-family, read-only) delta re-critique ac6f2a4de..21fbc60bc bound to final product tip 21fbc60bc: APPROVE; 1 unproven major + 4 minors filed, 1 proven minor (stale receipt claims) landed in the candidate commit; per-thread CONCUR on all four Codex dispositions. Verdict receipt: .cursor/receipts/v-lane-receipt-validator-20260812-critic-r3.json" + ], + "remainingHolds": [], + "result": "green", + "followUps": [ + "Pre-existing on origin/main: docs/CI-GATES.md has 1 broken citation (line ~902, migration 0600 .xctestrun) and 2 unverifiable line-number citations; CI-GATES.md is not in wired check:doc-citations scope despite being a declared documentInputs entry of check:foundation-gates; route to W1.1 T1 custody lane", + "CONSOLIDATION LANE (third-spelling closure): scripts/cursor/validate-lane-receipt.mjs is TRACKED since a762d1d6c (#738), and .claude/workflows/lane-fanout.js BUILD_SCHEMA/REVIEW_SCHEMA is CI-pinned by lane-fanout.test.mjs (preflight step 28). Three authorities must collapse to ONE survivor: this schema JSON, with the cursor validator and lane-fanout deriving from it. Owned roots .cursor/** and .claude/** are leased — needs its own lane. Includes: migrate 30 legacy kind-less receipts (add kind), update .cursor/PROCESS-SYNTHESIS.md:24 row mapping schema-required fields to BUILD_SCHEMA, update agent-card/hook validation commands, and decide critic-branch tip binding (headSha on critic receipts) — deliberately NOT added here to avoid diverging from all three incumbent authorities (Chesterton)", + "Interim parity is pinned by execution: BUILD_SCHEMA.required subset-of laneReceipt.required and REVIEW_SCHEMA.required subset-of criticReceipt.required tests fail on drift in either direction, and this receipt is dual-validated by an executed test", + "R2-APPROVE filed findings, updated post-R3: scanDir kind predicate CLOSED in 21fbc60bc (present-but-unrecognised kind fails, planted-red); same-size parity drift CLOSED in 21fbc60bc (exact deepEqual pins; co-edit requirement: a strictly-additive BUILD_SCHEMA/REVIEW_SCHEMA required-field change under the leased .claude/** root reds test:lane-receipt under scripts/console/** — announce co-edits; both pins die with the parity mechanism at consolidation); STILL OPEN for the consolidation lane: converge the commands/commandsRun vocabulary; report the closest oneOf branch failure; give check:lane-receipt a caller or fold it into the suite; reject --schema combined with --dir", + "R3 filed findings (see critic-r3 receipt): R3-2 major unproven - schema remainingHolds/followUps nonEmptyString tightening shipped without a planted-red control (relaxing it back leaves the suite green); R3-5 - in --dir mode a non-object JSON top level or a non-.json extension is silently skipped (fold with Codex thread-3 tracked-blob enumeration: drive scanDir from git ls-files); R3-6 - kind token vocabulary diverges between the tracked schema (lane|critic) and the incumbent cursor validator (build|critic) with zero docs naming kind: legacy migration of the 30 kind-less receipts is strictly atomic per receipt", + "SSOT elevation (R3-3 residue + C2): any ci.yml edit must run BOTH node scripts/check-ci-preflight.mjs(+test) AND node --test scripts/verify.test.mjs (two gates, one file, disjoint subjects); generate-documentation-manifest --check must be asserted at T never at C (C pre-registers a blob_sha untracked until T, so --check at C fails by design)" + ] +} diff --git a/scripts/console/validate-lane-receipt.mjs b/scripts/console/validate-lane-receipt.mjs index 98983604f..73bca43ca 100644 --- a/scripts/console/validate-lane-receipt.mjs +++ b/scripts/console/validate-lane-receipt.mjs @@ -12,7 +12,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; const SCHEMA_URL = new URL('./lane-receipt.schema.json', import.meta.url); const SCHEMA = JSON.parse(readFileSync(SCHEMA_URL, 'utf8')); // Parity with the tracked incumbent (scripts/cursor/validate-lane-receipt.mjs) and -// .claude/workflows/lane-fanout.js BUILD_SCHEMA: case-insensitive prefix, not exact equality. +// scripts/console/workflows/lane-fanout.js BUILD_SCHEMA: case-insensitive prefix, not exact equality. const NA_ENFORCEMENT = /^n\/a\s*-\s*adds no enforcement\b/i; // Every JSON Schema keyword the walker implements. A schema edit that introduces a keyword diff --git a/scripts/console/validate-lane-receipt.test.mjs b/scripts/console/validate-lane-receipt.test.mjs index 1dcbd5e24..471d7ae59 100644 --- a/scripts/console/validate-lane-receipt.test.mjs +++ b/scripts/console/validate-lane-receipt.test.mjs @@ -297,13 +297,13 @@ test('--dir scan FAILS a present-but-unrecognised kind instead of skipping it (p test('--dir scan of the real tracked receipts directory examines at least this lane receipt and passes', () => { executed += 1; - const result = run(['--dir', join(repoRoot, '.cursor', 'receipts')]); + const result = run(['--dir', join(repoRoot, 'scripts', 'console', 'fixtures')]); assert.equal(result.status, 0, result.stderr); }); test('cross-authority parity: BUILD_SCHEMA required fields are a subset of laneReceipt required', () => { executed += 1; - const laneFanout = readFileSync(join(repoRoot, '.claude', 'workflows', 'lane-fanout.js'), 'utf8'); + const laneFanout = readFileSync(join(repoRoot, 'scripts', 'console', 'workflows', 'lane-fanout.js'), 'utf8'); const schema = JSON.parse(readFileSync(schemaPath, 'utf8')); const buildMatch = laneFanout.match(/const BUILD_SCHEMA = \{[^]*?required: \[([^\]]*)\]/); @@ -337,7 +337,7 @@ test('cross-authority parity: BUILD_SCHEMA required fields are a subset of laneR test('this lane receipt validates under both the tracked and incumbent cursor validators', () => { executed += 1; - const receiptPath = join(repoRoot, '.cursor', 'receipts', 'v-lane-receipt-validator-20260812.json'); + const receiptPath = join(repoRoot, 'scripts', 'console', 'fixtures', 'v-lane-receipt-validator-20260812.json'); const tracked = run([receiptPath]); assert.equal(tracked.status, 0, tracked.stderr); diff --git a/scripts/console/workflows/backlog-audit.js b/scripts/console/workflows/backlog-audit.js new file mode 100644 index 000000000..22bdc1b45 --- /dev/null +++ b/scripts/console/workflows/backlog-audit.js @@ -0,0 +1,690 @@ +export const meta = { + name: 'backlog-audit', + description: 'Audit the existing codebase by capability domain, triage every open GitHub issue against the current tree, and reconcile both into one backlog — findings become beads, dead issues get closed with evidence', + whenToUse: 'Periodically, when the issue tracker and the working backlog have drifted apart, or before planning a phase. NOT for reviewing a PR — that is lane-fanout.', + phases: [ + { title: 'Collect', detail: 'one agent gathers raw state; classification is done in-script' }, + { title: 'Read', detail: 'ONE wave: per-domain audit + cross-cutting sweeps + issue triage, all read-only' }, + { title: 'Reconcile', detail: 'single writer: create beads, close issues, sync the two' }, + ], +} + +// --------------------------------------------------------------------------- +// args = { +// repo: "/abs/path" // the checkout to audit +// ghRepo: "owner/name" // for gh issue operations +// ref?: "branch-or-sha" // what to audit; defaults to the checkout's HEAD +// domains?: [{ key, crates:[...], why }] // capability chunks; defaults below. +// Whatever this list is, any crate the census finds and no domain +// claims gets its own lane — coverage is checked, not assumed. +// issueBatch?: 8 // issues per triage agent; smaller = more lanes = same wall-clock +// apply?: false // false = report only. TRUE = actually mutate beads and issues. +// defaultBranch?: "origin/main" // what a CLOSE-* verdict's evidence must be reachable from +// } +// +// WHY THIS IS SEPARATE FROM lane-fanout: that harness reviews a DIFF against a tip, which is the +// right shape for work in flight and the wrong shape for standing code. Most defects in a mature +// tree are not in any recent diff — they are in the parts nobody has looked at since they landed. +// This one reads the tree as it stands, in chunks a reviewer can actually hold. +// +// THE DISCIPLINE THIS FILE EXISTS TO ENFORCE, learned the expensive way in this programme: +// - A finding without file:line evidence is an opinion. It does not become a bead. +// - An issue is NEVER closed on a guess. "Probably fixed" is not a verdict; the closing comment +// must name the commit, the test, or the code that makes it moot. +// - Wrongly closing a real issue is far worse than leaving a stale one open, because the stale one +// is visible and the closed one is not. When uncertain, the verdict is KEEP with a note. +// - One writer performs every mutation. Beads and gh are shared state; concurrent writers race. +// --------------------------------------------------------------------------- + +let ARGS = args +if (typeof ARGS === 'string') { + try { ARGS = JSON.parse(ARGS) } catch (e) { + throw new Error(`backlog-audit: args arrived as a string that is not valid JSON: ${e.message}`) + } +} +ARGS = ARGS || {} + +// An option this workflow does not read must abort rather than be silently dropped — the same rule +// lane-fanout learned from a sibling runner where an ignored option cost six lanes. +const KNOWN_ARGS = ['repo', 'ghRepo', 'ref', 'domains', 'issueBatch', 'apply', 'defaultBranch'] +{ + const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) + if (unknown.length) { + throw new Error( + `backlog-audit: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`, + ) + } +} + +const REPO = ARGS.repo +const GH = ARGS.ghRepo +const REF = ARGS.ref || 'HEAD' +// `|| 8` rescues 0 and undefined and NOTHING else: -1 yields negative-stride batching that produces +// no batches at all, and 1.5 yields overlapping slices that triage the same issue twice under two +// agents. Both exit cleanly, which is what makes them worth refusing here. +const BATCH = ARGS.issueBatch === undefined ? 8 : ARGS.issueBatch +if (!Number.isInteger(BATCH) || BATCH < 1) { + throw new Error(`backlog-audit: issueBatch must be a positive integer; got ${JSON.stringify(ARGS.issueBatch)}`) +} +const APPLY = ARGS.apply === true +// What "landed" means. An issue is closed against the branch everyone else will pull, not against +// whatever branch the evidence happens to sit on. +const DEFAULT_REF = ARGS.defaultBranch || 'origin/main' + +if (!REPO) throw new Error('backlog-audit: args.repo is required') +if (!GH) throw new Error('backlog-audit: args.ghRepo is required (owner/name)') + +const SAFETY = [ + '=== READ-ONLY UNLESS TOLD OTHERWISE ===', + APPLY + ? 'apply=TRUE. The Reconcile phase — and ONLY that phase — may mutate beads and GitHub issues.' + : 'apply=FALSE. NOTHING may be mutated. No bd create/close/update, no gh issue close/comment.', + 'No phase may edit source, commit, push, or open a PR. This workflow reports and files; it does', + 'not fix. A finding that is trivially fixable is still a finding — fixing it here would put an', + 'unreviewed change into a tree nobody is watching.', + '', + '=== EVIDENCE RULES, non-negotiable ===', + '1. Every finding carries file:line and the command that produced it. A claim you did not run is', + ' an opinion, and opinions do not become backlog items.', + '2. State what you SEARCHED for as well as what you found. A null result from the wrong query has', + ' twice become a written "not established" finding in this programme.', + '3. Beware the greps that silently match nothing here: `git grep -E` does NOT support \\b (POSIX', + ' ERE), and a trailing \\b makes the whole pattern match zero lines while exiting cleanly. That', + ' produced a false-clean writer census for a table with five known writers. Validate any regex', + ' against a case you KNOW matches before trusting a zero.', + '4. Stale worktrees are not the tree. This checkout contains abandoned worktrees under .worktrees/', + ' and .omx/team/**; five separate diagnostic bursts in this programme traced to them. Scope every', + ' search to the real source roots and say which roots you used.', +].join('\n') + +const FINDING_SCHEMA = { + type: 'object', + required: ['domain', 'findings', 'coverage'], + properties: { + domain: { type: 'string' }, + coverage: { type: 'string', description: 'what you actually read, and what you did NOT get to — an honest gap beats a claimed sweep' }, + findings: { + type: 'array', + items: { + type: 'object', + required: ['title', 'severity', 'evidence', 'failureScenario', 'provenByExecution'], + properties: { + title: { type: 'string', description: 'imperative and specific, as a backlog item should read' }, + severity: { type: 'string', enum: ['blocker', 'major', 'minor', 'nit'] }, + evidence: { type: 'string', description: 'file:line plus the command and its real output' }, + failureScenario: { type: 'string', description: 'concrete inputs or state -> wrong outcome. Not "could be unsafe".' }, + provenByExecution: { type: 'boolean', description: 'TRUE only if you RAN something and observed it. Reasoning from source is FALSE.' }, + suggestedFix: { type: 'string' }, + existingIssue: { type: 'string', description: 'an existing GitHub issue or bead this duplicates, if any — check BEFORE filing' }, + }, + }, + }, + strengths: { type: 'string', description: 'controls that are genuinely load-bearing, so a later reader does not "simplify" them away' }, + }, +} + +const TRIAGE_SCHEMA = { + type: 'object', + required: ['verdicts'], + properties: { + verdicts: { + type: 'array', + items: { + type: 'object', + required: ['number', 'verdict', 'evidence', 'reachableFromDefault'], + properties: { + number: { type: 'number' }, + title: { type: 'string' }, + // EVIDENCE NOBODY LANDED IS NOT EVIDENCE. A commit that exists only on an unmerged branch + // closes an issue against work the default branch does not have — and the issue, once + // closed, is invisible while the gap is still there. Required, and a boolean rather than + // prose, so that not answering is a NO instead of a blank the reconciler reads past. + reachableFromDefault: { + type: 'boolean', + description: `for CLOSE-FIXED and CLOSE-OBSOLETE: TRUE only if you RAN \`git -C merge-base --is-ancestor ${DEFAULT_REF}\` and it exited 0. FALSE for anything else, including "I could not check" and any verdict where it does not apply.`, + }, + verdict: { + type: 'string', + enum: ['KEEP', 'CLOSE-FIXED', 'CLOSE-OBSOLETE', 'CLOSE-DUPLICATE', 'NEEDS-OWNER'], + description: 'KEEP when uncertain. A wrongly closed issue is invisible; a stale open one is not.', + }, + evidence: { type: 'string', description: 'for any CLOSE: the commit, test or code that makes it moot, with file:line. "Looks done" is not evidence.' }, + duplicateOf: { type: 'number' }, + beadCandidate: { type: 'boolean', description: 'KEEP items that are real work and should exist in beads too' }, + staleness: { type: 'string', description: 'what in the issue text is now factually wrong about the tree' }, + }, + }, + }, + }, +} + +// --- Collect --------------------------------------------------------------- +// One agent gathers raw facts; everything that can be decided by a rule is decided in-script. +// Measured in this programme: a per-item agent loop over 89 worktrees took many minutes and 233KB +// of transcript, while one batched shell command did the same work in 5.2 seconds. Agents are for +// judgement, not for enumeration. +phase('Collect') +const collected = await agent( + `Gather the raw state of the backlog and the tree. Do NOT judge anything yet; this phase is a census. + +REPO: ${REPO} GH: ${GH} REF: ${REF} + +${SAFETY} + +Emit, in ONE batched pass each (not a loop of small commands): + 1. Every OPEN GitHub issue: number, title, labels, author, createdAt, updatedAt, comment count, and + the first 400 characters of the body. Use a single \`gh issue list --json\` call with --limit high + enough to get them all, and say how many you got. + 2. Every bead: id, title, status, priority, and its dependency edges. \`bd list\` and \`bd dep\`. + 3. The crate inventory under backend/crates, with each crate's line count, so domains can be sized. + (A separate in-script disk census measures \`find backend/crates -name Cargo.toml\` independently — + do NOT invent a matching \`cargoTomlPaths\` here; that would validate the census against itself.) + 4. Recently merged PRs (last 40) with number, title and merge commit — a closed issue often has its + fix sitting in one of these, and that is the cheapest evidence of CLOSE-FIXED there is. + 5. The abandoned-worktree roots that must be EXCLUDED from every later search, listed explicitly. + +Return raw structured data. No verdicts, no recommendations — later phases do that, and a census that +editorialises makes its own errors invisible.`, + // A SCHEMA, not free text. The first version of this file had none, so `collected` came back as a + // plain string and the regex below that scraped issue numbers out of it matched NOTHING. Ten + // triage lanes were therefore never dispatched, and the run reported "0 issues triaged" as though + // that were an answer. The census must hand the script DATA, not prose it has to parse. + { + label: 'collect', + phase: 'Collect', + schema: { + type: 'object', + required: ['openIssueNumbers', 'openIssueCount', 'issues', 'beads', 'crates'], + properties: { + openIssueNumbers: { type: 'array', items: { type: 'number' }, description: 'EVERY open issue number. This drives the triage fan-out, so an omission here silently un-audits that issue.' }, + openIssueCount: { type: 'number', description: 'what gh reported, so the script can catch a truncated list' }, + issues: { type: 'array', items: { type: 'object' }, description: 'number, title, labels, author, createdAt, updatedAt, comments, body excerpt' }, + beads: { type: 'array', items: { type: 'object' } }, + // This drives the domain-coverage check, so a missing crate is a domain nobody audits while + // the run still reports every domain covered. `name` is what makes it comparable. + crates: { + type: 'array', + items: { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'path relative to backend/crates, e.g. "identity" or "platform/db"' }, + lines: { type: 'number' }, + }, + }, + }, + mergedPrs: { type: 'array', items: { type: 'object' } }, + excludedRoots: { type: 'array', items: { type: 'string' } }, + }, + }, + }, +) + +// INDEPENDENT ON-DISK CRATE CENSUS. Workflow sandboxes have no Node filesystem API and reject +// `import()`, so the script cannot `ls`/`find` itself — but a control must not validate Collect's +// crate list against another field from the SAME Collect return (cargoTomlPaths co-emitted with +// crates). A coordinated partial list then reads as full coverage. Measure the disk set in a +// dedicated find-only agent whose sole schema field is the path list, then compare. +const diskCensus = await agent( + `Measure the on-disk crate set for REPO: ${REPO}. Do NOT audit, triage, or summarise. + +Run ONE command only (cwd = REPO): + find backend/crates -name Cargo.toml -print + +Return EVERY path it prints, repo-relative (e.g. "backend/crates/identity/Cargo.toml"). +Do not invent paths, do not truncate, do not dedupe by hand — emit the find output as cargoTomlPaths.`, + { + label: 'crate-disk-census', + phase: 'Collect', + schema: { + type: 'object', + required: ['cargoTomlPaths'], + properties: { + cargoTomlPaths: { + type: 'array', + items: { type: 'string' }, + description: 'EVERY path from `find backend/crates -name Cargo.toml` (repo-relative)', + }, + }, + }, + }, +) + +// --- The read fan-out ------------------------------------------------------ +// EVERY PHASE BELOW IS READ-ONLY, so width is nearly free and there is no collision to fear. +// Measured in this programme: wall-clock tracks per-step latency x DEPTH and is almost insensitive +// to WIDTH — 12 agents at depth 6 took 74 min, 36 agents at depth 6 took 63 min. So the audit, +// the cross-cutting sweeps and the issue triage all run in ONE parallel block rather than in +// sequence: three independent read phases stacked serially would triple the wall-clock and buy +// nothing, because none of them feeds another. Only Reconcile, which WRITES, is serialised. +// +// Domains are narrow on purpose. A reviewer holding one bounded capability finds the interaction +// defects that live in the seam, and a smaller root means fewer files skimmed rather than read. +const BASE_DOMAINS = ARGS.domains || [ + { key: 'identity', crates: ['identity'], why: 'principal resolution and the role/feature matrix — a fail-open here is silent and total' }, + { key: 'policy-authz', crates: ['policy'], why: 'Cedar is OBSERVE-ONLY and inert in production here; find what actually enforces' }, + { key: 'governance', crates: ['governance'], why: 'four-eyes, SoD, maker-checker, effective-dating — the controls that must not be bypassable' }, + { key: 'leave', crates: ['leave'], why: '§4-31: 연차 must have NO reason field and cannot be refused. The schema currently has reason NOT NULL — verify and size it' }, + { key: 'attendance', crates: ['attendance'], why: 'the 주52 cap and break rules are statutory, not policy' }, + { key: 'orgchange-eval', crates: ['orgchange', 'evaluation'], why: 'org lifecycle and the appraisal surface labour law constrains' }, + { key: 'payroll', crates: ['payroll'], why: 'money is irreversible; period locks, rounding and the minimum-wage instrument' }, + { key: 'finance', crates: ['finance-gl', 'financial', 'benefit'], why: 'ledger integrity and benefit entitlement' }, + { key: 'ontology', crates: ['ontology'], why: 'the substrate every other domain projects through' }, + { key: 'kernel-registry', crates: ['kernel', 'registry'], why: 'shared types and the object registry everything else trusts' }, + { key: 'workflow', crates: ['workflow'], why: 'schedules, drains and outboxes — where at-least-once quietly becomes at-least-twice' }, + { key: 'comms-egress', crates: ['comms', 'messenger', 'notices'], why: 'egress: the DLP and send-gate boundary, where a leak is external and permanent' }, + { key: 'docs-inbox', crates: ['docs', 'inbox', 'notifications'], why: 'document custody and the personal surfaces §4-37 separates' }, + { key: 'ops-field', crates: ['dispatch', 'facilities', 'equipment', 'inspection', 'logistics', 'workorder', 'production'], why: 'the 70% of staff who work on a client site' }, + { key: 'platform', crates: ['platform'], why: 'db, authz, request-context, audit-chain — every tenant boundary in one crate tree' }, +] + +// THE DOMAIN LIST IS HAND-WRITTEN AND THE TREE IS NOT. A crate added after this list was written +// belongs to no domain, so nothing reads it — while the headline still reports "15/15 domains +// audited", which is true and means nothing. The census already collects the crate inventory, so +// the gap is decidable here rather than discoverable later: everything the inventory names and no +// domain claims becomes its own lane. +const crateEntries = (collected && collected.crates) || [] +const crateNames = crateEntries + .map((c) => (c && typeof c.name === 'string' ? c.name : '')) + .map((n) => n.trim().replace(/^backend\/crates\//, '').replace(/\/+$/, '')) + .filter(Boolean) +// A control that examines zero subjects must FAIL. With no inventory — or one whose entries have no +// name — the coverage question cannot be asked at all, and answering it "complete" is the exact +// false green this file exists to refuse. +if (crateNames.length < crateEntries.length || !crateNames.length) { + throw new Error( + `backlog-audit: the census returned an unusable crate inventory (${crateEntries.length} entr(ies), ` + + `${crateNames.length} with a name). Domain coverage cannot be checked against it, so every domain ` + + 'reported as audited would be an unverified claim. Re-run Collect and have it report each crate ' + + 'under backend/crates with a `name`.', + ) +} +// THE CENSUS CAN OMIT A CRATE AND STILL LOOK WELL-FORMED. A partial list whose every entry has a +// name satisfies the guard above while the omitted crate receives no audit lane. The on-disk set +// comes from the dedicated crate-disk-census agent (find-only), NOT from Collect's own return — +// validating crates against a co-emitted cargoTomlPaths field is still validating the census +// against itself. Workflow sandboxes have no Node filesystem API, so find runs in that agent; +// examined-zero / dead oracle / omitted crate all fail closed here. +function cratesOmittedFromCensus(onDiskNames, censusNames) { + return onDiskNames.filter((c) => !censusNames.some((n) => c === n || c.startsWith(`${n}/`))) +} +function crateNamesFromCargoTomlPaths(paths) { + const out = [] + for (const raw of paths || []) { + if (typeof raw !== 'string') continue + let p = raw.trim().replace(/\\/g, '/') + if (!p) continue + p = p.replace(/^\.\//, '') + if (!p.endsWith('/Cargo.toml') && p !== 'Cargo.toml') continue + p = p.replace(/\/Cargo\.toml$/, '') + p = p.replace(/^backend\/crates\//, '') + if (!p || p.includes('..')) continue + out.push(p) + } + return [...new Set(out)].sort() +} +{ + if (!diskCensus) { + throw new Error( + 'backlog-audit: crate-disk-census agent returned nothing, so domain coverage cannot be ' + + 'cross-checked against an independent on-disk find. Re-run Collect.', + ) + } + const cargoTomlPaths = diskCensus.cargoTomlPaths + if (!Array.isArray(cargoTomlPaths)) { + throw new Error( + 'backlog-audit: crate-disk-census must return cargoTomlPaths (array) from ' + + '`find backend/crates -name Cargo.toml`. The harness cannot walk the crate tree itself ' + + 'inside the workflow sandbox.', + ) + } + const onDisk = crateNamesFromCargoTomlPaths(cargoTomlPaths) + if (!onDisk.length) { + throw new Error( + 'backlog-audit: crate-disk-census returned an empty cargoTomlPaths list — domain coverage ' + + 'cannot be cross-checked against the on-disk Cargo.toml census. Re-run with find output.', + ) + } + const omitted = cratesOmittedFromCensus(onDisk, crateNames) + if (omitted.length) { + throw new Error( + `backlog-audit: the census omitted ${omitted.length} crate(s) present under backend/crates ` + + `(${onDisk.length} on disk via crate-disk-census, ${crateNames.length} named). Omitted: ${omitted.slice(0, 20).join(', ')}` + + `${omitted.length > 20 ? ', ...' : ''}. Re-run Collect so every Cargo.toml is named.`, + ) + } +} +const claimed = (crate) => BASE_DOMAINS.some((d) => (d.crates || []).some((c) => crate === c || crate.startsWith(`${c}/`))) +const uncovered = [...new Set(crateNames.filter((c) => !claimed(c)))].sort() +const DOMAINS = uncovered.length + ? [...BASE_DOMAINS, { key: 'uncovered', crates: uncovered, why: 'crates the census found that no named domain claims — they would otherwise be read by nobody while the run reported full coverage' }] + : BASE_DOMAINS +if (uncovered.length) log(`domain coverage: ${uncovered.length} discovered crate(s) matched no named domain, audited in the "uncovered" lane: ${uncovered.join(', ')}`) + +// Cross-cutting sweeps read ACROSS the tree rather than down one crate. They exist because the +// worst defects in this programme were never inside one crate: a census that ran before migrations, +// a gate wired to nothing, a doc claiming a property the code had lost. No per-crate reviewer could +// have seen any of them, because each is a property of the SEAM between things. +const CROSS = [ + { key: 'x-migrations', why: 'all migrations as a sequence: RLS declared but not FORCED, a missing append-only trigger, a destructive DDL, contiguity, and any table whose properties diverge from the 0177/0213/0214 references' }, + { key: 'x-gates', why: 'every backend/ci/gates/** binary: is each REACHED by a CI step, and would it FAIL on a real violation? A gate wired to nothing is worse than no gate, because it reads as coverage' }, + { key: 'x-tenancy', why: 'the org boundary end to end: RLS FORCE, app.current_org arming, the CURRENT_ORG task-local (a bare tokio::spawn does not inherit it and the failure is ZERO ROWS, not an error), and any aggregate that merges sources and widens visibility' }, + { key: 'x-openapi', why: 'the 36k-line hand-maintained openapi.yaml against the handlers it claims to describe — a published contract the code stopped honouring is a live interop break, and two were just found' }, + { key: 'x-docs-drift', why: 'docs/** and module docs against the code: claims of the form "every / always / cannot / the N ways X can happen". Pick the load-bearing ones and check whether the code still holds them' }, + { key: 'x-compliance', why: '§4-31 labour law and §3.10 internal controls as properties of the whole tree: statutory periods and rates must be catalogue-derived with the instrument cited, never hardcoded; no reason field on 연차; no destructive delete where 보관=숨김 is required' }, + { key: 'x-supply-chain', why: 'dependency posture: unmaintained or advisory-bearing crates, git sources, licence outliers, and anything a cargo-deny ignore is silently carrying' }, +] + +// Issue batches are computed BEFORE the read block so triage can join the same parallel wave. +// Read the field the schema guarantees. The previous version scraped `"number": N` out of the +// stringified census with a regex; the census was prose, so it matched nothing and ten triage lanes +// were silently never dispatched. +const issueNumbers = [...new Set((collected && collected.openIssueNumbers) || [])] + .map(Number).filter((n) => Number.isFinite(n) && n > 0).sort((a, b) => a - b) +const claimedCount = (collected && collected.openIssueCount) || 0 + +// A PHASE THAT EXAMINES ZERO SUBJECTS MUST FAIL, NEVER PASS. This is the harness's own standing rule +// — "examined zero subjects MUST be a FAILURE" — applied to the harness itself, because the first run +// of this file broke exactly that way: 22 audit lanes did real work while the triage half quietly ran +// on an empty list and the headline read "0 issues triaged" as if it were a result. +// A VERIFIED empty tracker is a legitimate state, and the first version of this guard refused it. +// The false green was a census that returned NOTHING while the tracker held 78 issues; a census that +// returns nothing AND reports a count of zero is agreeing with itself, and blocking there would make +// the workflow unable to audit standing code precisely when the backlog is clean. The guard exists to +// catch a census that CONTRADICTS itself, not one that is merely empty. +if (!issueNumbers.length && claimedCount === 0) { + log('census reports zero open issues and lists none — consistent, so triage schedules no batches') +} else if (!issueNumbers.length) { + throw new Error( + 'backlog-audit: the census returned NO open issue numbers, so triage would examine nothing and ' + + 'report success. That is a false green, not an empty backlog. Either the repository genuinely ' + + 'has zero open issues (verify with `gh issue list --state open`), or the Collect agent did not ' + + 'populate openIssueNumbers. Fix the census rather than running a triage over nothing.', + ) +} +// A truncated list is the quieter version of the same failure: some issues get audited, the rest are +// silently dropped, and nothing in the output distinguishes that from a clean sweep. +if (claimedCount && issueNumbers.length < claimedCount) { + throw new Error( + `backlog-audit: census reported ${claimedCount} open issues but listed only ${issueNumbers.length} ` + + 'numbers. Triage would silently skip the remainder. Re-run Collect with a --limit high enough to ' + + 'return them all.', + ) +} + +const batches = [] +for (let i = 0; i < issueNumbers.length; i += BATCH) batches.push(issueNumbers.slice(i, i + BATCH)) + +phase('Read') +log(`read fan-out: ${DOMAINS.length} domains + ${CROSS.length} cross-cutting + ${batches.length} issue batches (${issueNumbers.length} issues) = ${DOMAINS.length + CROSS.length + batches.length} lanes, all read-only`) + +const auditThunks = DOMAINS.map((d) => () => + agent( + `Audit the ${d.key} capability domain of a production Korean B2B console. Find real defects. + +REPO: ${REPO} REF: ${REF} +CRATES: ${d.crates.map((c) => `backend/crates/${c}`).join(', ')} +WHY THIS DOMAIN MATTERS: ${d.why} + +${SAFETY} + +THE CENSUS THIS RUN COLLECTED (use it to avoid re-filing what is already tracked): +${JSON.stringify(collected).slice(0, 6000)} + +WHAT TO HUNT, in descending order of value. These are the classes this programme has actually been +bitten by, so they are worth more than a generic review: + + 1. FAIL-OPEN GUARDS. A check that cannot see its subject, or that exits 0 on the empty case. Ask of + every guard: where does it run in the sequence, does its subject exist yet, and what is the + finest distinction its data source can express? A census that runs before migrations examines + zero rows and passes. A per-crate rule enforced by a data source that only distinguishes roles + never draws the crate boundary. + 2. TENANT AND SCOPE BOUNDARIES. RLS declared but not FORCED; a policy that special-cases one scope + and leaves the others open; an aggregate that widens visibility by merging sources; a read that + fails OPEN rather than closed when no org is armed. Note that CURRENT_ORG is a tokio task-local + and a bare tokio::spawn does not inherit it — that failure returns ZERO ROWS, not an error. + 3. SECOND WRITERS AND DUAL SOURCES OF TRUTH. Two code paths writing one table; a value stored in + two places that can disagree; a hand-maintained list mirroring something derivable. The canonical + tables are already gated, so look at the ones that are NOT canonical. + 4. CLAIMS WIDER THAN THE CODE. A doc, comment or test NAME asserting a universal the code does not + hold — "every", "always", "cannot", "the three ways X can happen". Pick the load-bearing + assertion, break the code it guards, and say whether it actually goes red. + 5. LABOUR-LAW AND COMPLIANCE GUARDRAILS (§4-31), which are correctness here and not policy taste: + 연차 must have no reason field and cannot be refused (only 시기변경 협의); no overtime glorification; + no discriminatory recruiting fields; statutory periods and rates must be catalogue-derived with + the instrument cited, never hardcoded constants. + 6. IRREVERSIBILITY. Hard deletes where the charter requires 보관=숨김; a destructive path reachable + before dependent objects are settled; an egress that sends before an approval gate. + +DO NOT report style, naming, or "consider extracting a helper". Do not report a defect you cannot +demonstrate. An empty findings array from an honest sweep is a GOOD result and must be reported as +one — inventing findings to look productive is the failure mode this phase must avoid. + +Before filing anything, check the census for an existing issue or bead covering it and name it in +existingIssue rather than creating a duplicate. + +Also report STRENGTHS: controls that are genuinely load-bearing, so a later reader does not simplify +them away. This programme has twice nearly deleted a guard that looked redundant and was not.`, + { label: `audit:${d.key}`, phase: 'Read', schema: FINDING_SCHEMA }, + )) + +// Cross-cutting thunks: same evidence rules, but the unit of review is a PROPERTY of the tree +// rather than a directory in it. +const crossThunks = CROSS.map((c) => () => + agent( + `Sweep ONE cross-cutting property of a production Korean B2B console. This is not a per-crate +review — the unit is the property, and it is deliberately the shape a per-crate reviewer cannot see. + +REPO: ${REPO} REF: ${REF} +THE PROPERTY: ${c.key} +WHY IT IS ITS OWN LANE: ${c.why} + +${SAFETY} + +THE CENSUS THIS RUN COLLECTED: +${JSON.stringify(collected).slice(0, 5000)} + +The worst defects in this programme were never inside one crate. A canonical-writer census sat in a +reconcile script that runs BEFORE migrations, so it examined zero tables and passed in every +automated path. A gate existed, compiled and was tested, and no CI step ever invoked it. A module doc +enumerated "the three ways X can happen" while reviewers had already proven a fourth and a fifth. +Each is a property of the SEAM between things, and no reviewer holding one directory could have found +any of them. + +So: read ACROSS. Follow the property wherever it goes. Report the same evidence-bound findings the +per-domain lanes report — file:line, the command, its real output — and mark provenByExecution TRUE +only for what you actually ran. + +An empty findings array from an honest sweep is a GOOD result. Say what you covered and what you did +not reach, because a claimed sweep that skipped half the tree is worse than a partial one that says so.`, + { label: `cross:${c.key}`, phase: 'Read', schema: FINDING_SCHEMA }, + )) + +// --- Triage ---------------------------------------------------------------- +// Issues are batched so each agent holds a readable set and can compare within it for duplicates. +const triageThunks = batches.map((b, i) => () => + agent( + `Triage GitHub issues ${b.join(', ')} in ${GH} against the CURRENT tree. Batch ${i + 1} of ${batches.length}. + +REPO: ${REPO} REF: ${REF} + +${SAFETY} + +FOR EACH ISSUE: read it in full (\`gh issue view --comments\`), then go and LOOK at the tree. +The whole point is that issue text describes a repo that has moved on. Decide: + + KEEP — still a real, open problem. Say what remains true. + CLOSE-FIXED — the code now does what the issue asked. EVIDENCE REQUIRED: the commit, the test, + or the file:line that implements it. Prefer a merged PR from the census. + CLOSE-OBSOLETE — the thing it is about no longer exists, or a decision superseded it. Name the + deletion or the ADR. + CLOSE-DUPLICATE — another issue covers it. Give the number, and prefer keeping the one with more + evidence rather than the older one. + NEEDS-OWNER — real, but the next step is a human decision, not work. Say what the decision is. + +THE BAR FOR CLOSING, and it is deliberately high: a wrongly closed issue is INVISIBLE, while a stale +open one is merely noise. If you cannot point at the thing that makes it moot, the verdict is KEEP +with a staleness note. "Looks done", "probably superseded" and "no longer relevant" are not evidence. + +AND THE EVIDENCE MUST BE LANDED. A commit sitting on an unmerged branch, an integration branch or an +unmerged PR closes the issue against work nobody has — this checkout has dozens of worktrees whose +commits are on none of them. For every CLOSE-FIXED and CLOSE-OBSOLETE, name the commit and PROVE it: + + git -C ${REPO} merge-base --is-ancestor ${DEFAULT_REF} && echo REACHABLE + +Set reachableFromDefault=true ONLY when that exited 0 and you saw REACHABLE. If your evidence is a +file rather than a commit, take the commit that last touched it +(\`git -C ${REPO} log -1 --format=%H -- \`) and run the same check. Anything else is FALSE, +including "I could not check" — an unproven CLOSE is withheld and left open, which is the cheap +failure. + +ALSO RECORD STALENESS for KEEP items: what in the issue text is now factually wrong — a renamed file, +a moved line number, a crate that no longer exists, a fixed sub-part. That is what makes an old issue +expensive to pick up, and writing it down is most of the value of this pass. + +Mark beadCandidate=true for KEEP items that are real work someone should schedule, so the reconcile +phase can mirror them into the working tracker.`, + // Cheap tier: every verdict here passes through the single-writer Reconcile, which refuses to + // close anything whose evidence is not reachable from the default branch. A wrong KEEP costs a + // stale issue; a wrong CLOSE cannot get past that guard. + { label: `triage:${i + 1}`, phase: 'Read', schema: TRIAGE_SCHEMA, model: 'sonnet' }, + )) + +// ONE parallel wave. Audit, cross-cutting and triage are mutually independent reads, so stacking +// them in three phases would multiply the wall-clock by three and buy nothing. The harness caps +// concurrency itself, so a wide list queues rather than overloads — width costs latency only when +// it exceeds the cap, and even then it degrades linearly instead of serialising. +const all = await parallel([...auditThunks, ...crossThunks, ...triageThunks]) +const audits = all.slice(0, DOMAINS.length) +const crosses = all.slice(DOMAINS.length, DOMAINS.length + CROSS.length) +const triaged = all.slice(DOMAINS.length + CROSS.length) + +// A dead lane is not an absent finding. Name every one, so a partial sweep can never read as a +// complete one — the same rule the lane-fanout harness learned when session limits silently took +// 4 of 7 agents from one run and 3 of 3 from another. +const auditOk = audits.filter(Boolean) +const crossOk = crosses.filter(Boolean) +const deadDomains = DOMAINS.map((d, i) => (audits[i] ? null : d.key)).filter(Boolean) +const deadCross = CROSS.map((c, i) => (crosses[i] ? null : c.key)).filter(Boolean) +const deadBatches = batches.map((b, i) => (triaged[i] ? null : `#${b[0]}-${b[b.length - 1]}`)).filter(Boolean) +const dead = [...deadDomains, ...deadCross, ...deadBatches] +log(`read: ${auditOk.length}/${DOMAINS.length} domains, ${crossOk.length}/${CROSS.length} cross-cutting, ${triaged.filter(Boolean).length}/${batches.length} issue batches`) +if (dead.length) log(`read: DIED and therefore UNAUDITED — ${dead.join(', ')}`) + +const findingsAll = [...auditOk, ...crossOk] +const totalFindings = findingsAll.reduce((n, a) => n + (a.findings || []).length, 0) +const provenFindings = findingsAll.reduce((n, a) => n + (a.findings || []).filter((f) => f.provenByExecution).length, 0) +log(`read: ${totalFindings} findings, ${provenFindings} proven by execution`) + +const triageOk = triaged.filter(Boolean) +const verdicts = triageOk.flatMap((t) => t.verdicts || []) +const closing = verdicts.filter((v) => String(v.verdict).startsWith('CLOSE')) +// A CLOSE-FIXED or CLOSE-OBSOLETE verdict is a claim about the DEFAULT BRANCH: the code now does +// this, or the thing no longer exists. Evidence reachable only from an unmerged branch does not +// support that claim, and closing on it hides a live gap behind a closed issue. CLOSE-DUPLICATE +// cites another issue rather than the tree, so ancestry does not apply to it. +const NEEDS_LANDED_EVIDENCE = ['CLOSE-FIXED', 'CLOSE-OBSOLETE'] +const evidenceLanded = (v) => !NEEDS_LANDED_EVIDENCE.includes(String(v.verdict)) || v.reachableFromDefault === true +const unevidenced = closing.filter((v) => !v.evidence || v.evidence.trim().length < 40 || !evidenceLanded(v)) +const unlanded = closing.filter((v) => !evidenceLanded(v)).map((v) => v.number) +log(`triage: ${verdicts.length} verdict(s); ${closing.length} propose closing; ${unevidenced.length} of those lack real evidence${unlanded.length ? ` (${unlanded.length} cite work not reachable from ${DEFAULT_REF})` : ''}`) +if (unevidenced.length) log(`triage: WITHHELD from closing for want of LANDED evidence: ${unevidenced.map((v) => '#' + v.number).join(', ')}`) + +// --- Reconcile ------------------------------------------------------------- +// A blind `.slice(0, 24000)` over the serialised findings dropped everything past the cap without a +// word, and with ~32 read lanes the cap is reached routinely — so the single writer filed beads for a +// prefix of the audit and reported success. Silent truncation reads as "covered everything". Order by +// what a reconciler must not miss (proven, then severity), and when the budget still binds, SAY what +// was cut so the omission is visible in the run rather than discovered six weeks later. +function renderFindings(all) { + const rank = { blocker: 0, major: 1, minor: 2, nit: 3 } + const ordered = [...all].sort((a, b) => + (b.provenByExecution === true) - (a.provenByExecution === true) || + (rank[a.severity] ?? 9) - (rank[b.severity] ?? 9)) + const kept = [] + let budget = 24000 + for (const f of ordered) { + const line = JSON.stringify(f) + if (line.length > budget) break + budget -= line.length + kept.push(f) + } + const dropped = ordered.length - kept.length + return JSON.stringify(kept) + (dropped + ? `\n\n!! ${dropped} of ${ordered.length} findings DID NOT FIT this prompt and are NOT above. They are` + + ' the lowest-ranked ones, but they are unfiled. Your report MUST state that this run reconciled' + + ` ${kept.length} of ${ordered.length} findings, so the omission is visible.` + : '') +} + +phase('Reconcile') +const reconciled = await agent( + `You are the SINGLE WRITER for the backlog. Turn this audit into tracked work, and close what is dead. + +REPO: ${REPO} GH: ${GH} APPLY: ${APPLY} + +${SAFETY} + +AUDIT + CROSS-CUTTING FINDINGS (${totalFindings} total, ${provenFindings} proven by execution): +${renderFindings(findingsAll)} + +ISSUE VERDICTS (${verdicts.length}): +${JSON.stringify(verdicts).slice(0, 16000)} + +WITHHELD FOR WANT OF LANDED EVIDENCE — proposed for closing, and must NOT be closed. Either the +evidence is too thin to name anything, or it is not reachable from ${DEFAULT_REF}, which means it +closes the issue against work the default branch does not have: +${unevidenced.map((v) => `#${v.number} ${v.title || ''}${unlanded.includes(v.number) ? ` — evidence not reachable from ${DEFAULT_REF}` : ''}`).join('\n') || '(none)'} + +${dead.length ? `LANES THAT NEVER REPORTED — their scope is UNAUDITED and the summary MUST say so: ${dead.join(', ')}` : 'Every read lane reported.'} + +DO, in this order: + +1. DE-DUPLICATE ACROSS SOURCES before writing anything. The same defect may appear as an audit + finding, an open issue, and an existing bead. Collapse them and say which record wins. Filing a + fourth copy of a known problem makes the backlog worse, not better. + +2. RANK. Order by severity and blast radius, with proven-by-execution ahead of argued. A tenant or + money defect outranks a doc drift regardless of how neatly the doc drift is written up. + +3. ${APPLY ? 'CREATE BEADS' : 'DRAFT BEADS (do not create — apply is false)'} for confirmed findings. + Each bead must carry the EVIDENCE inline — file:line, the command, the observed output — so + nobody re-derives it. A bead whose body is a restatement of its title is worthless six weeks + later. Set priority from the ranking, and wire dependencies where one finding blocks another. + +4. ${APPLY ? 'CLOSE ISSUES' : 'LIST ISSUES TO CLOSE (do not close — apply is false)'} for + CLOSE-* verdicts that carry real evidence. The closing comment must state WHY, name the commit or + file that makes it moot, and be written for the person who filed it — this repository's sibling + project comments an explicit acceptance and then closes with the merged PR named, which is the + pattern to follow. Never close silently. + +5. SYNC THE TWO TRACKERS, and state the rule you applied rather than inventing one per item: + GitHub issues are the durable, public record; beads are the working queue. So every KEEP issue + marked beadCandidate should have a bead, and every bead representing work others should see + should reference an issue. Report the drift you found — this run began with ${issueNumbers.length} + open issues against a far smaller bead set, which is itself the finding. + +6. REPORT: what was filed, what was closed, what was WITHHELD and why, which domains went unaudited, + and the three things you would fix first. Be honest about coverage — a sweep that missed a domain + and says so is worth more than one that implies completeness it does not have.`, + { label: 'reconcile', phase: 'Reconcile' }, +) + +return { + headline: [ + `${auditOk.length}/${DOMAINS.length} domains + ${crossOk.length}/${CROSS.length} cross-cutting audited`, + `${totalFindings} findings (${provenFindings} proven)`, + `${verdicts.length} issues triaged, ${closing.length - unevidenced.length} closable, ${unevidenced.length} withheld`, + // Never claim APPLIED on the strength of the caller's flag alone. When the Reconcile agent dies, + // agent() returns null and the mutations are absent — or worse, a partial prefix of them landed + // and nothing records which. An operator reading APPLIED would take a success claim over an + // unknown state, which is the same false-green class this workflow exists to find. + APPLY ? (reconciled ? 'APPLIED' : 'RECONCILE DIED — mutations UNKNOWN, possibly partial; verify by hand') : 'REPORT ONLY (apply=false)', + ], + dead, + audits: auditOk, + cross: crossOk, + verdicts, + withheld: unevidenced.map((v) => v.number), + reconciled, +} diff --git a/scripts/console/workflows/lane-fanout.js b/scripts/console/workflows/lane-fanout.js new file mode 100644 index 000000000..64c0cfcce --- /dev/null +++ b/scripts/console/workflows/lane-fanout.js @@ -0,0 +1,1154 @@ +export const meta = { + name: 'lane-fanout', + description: 'Reusable hardened lane fan-out: RED-baseline build, cross-lane defect ledger, adversarial diff-only review, independent re-verification, converge-or-escalate', + whenToUse: 'Any multi-lane implementation phase with path-disjoint owned roots. Parameterise with args; do not fork this file per phase.', + phases: [ + { title: 'Build', detail: 'one implementer per disjoint lane, failing test first' }, + { title: 'Review', detail: '2 diff-only adversarial reviewers + 1 independent re-runner per lane' }, + ], +} + +// --------------------------------------------------------------------------- +// args = { +// tip: "" // what each lane started from; ALL diffs are taken against this +// lanes: [{ key, bead, wt, owned, brief, accept, blockedTargets? }] +// Lanes must be pairwise disjoint in BOTH `wt` and `owned`, and both are refused at +// dispatch: a shared worktree collides while they build, a shared owned root collides +// at LAND. `owned` must name paths, not describe them. +// lockExtra?: "" // phase-specific additions to the lock contract +// maxRounds?: 3 +// lenses?: ["...", "..."] // review lenses; defaults below +// } +// +// This file exists because the same harness was re-derived four times by hand, and every defect +// found in it (HEAD~1 diffing the base commit, reviewer findings discarded with no feedback edge, +// a stale owned-root after a mid-run scope ruling, "empty diff = auto reject") had to be fixed in +// each copy separately. Bun's rule applies to the pipeline as much as to the code it produces: +// fix the process that generates the work, not each instance of the work. +// --------------------------------------------------------------------------- + +// args may arrive as a real object OR as a JSON-encoded string depending on how the caller passed +// it. A reusable harness should tolerate both rather than die on the caller's serialisation choice. +let ARGS = args +if (typeof ARGS === 'string') { + try { + ARGS = JSON.parse(ARGS) + } catch (e) { + throw new Error(`lane-fanout: args arrived as a string that is not valid JSON: ${e.message}`) + } +} +ARGS = ARGS || {} + +// AN OPTION THIS HARNESS DOES NOT READ MUST ABORT, NEVER BE SILENTLY DROPPED. +// Borrowed from a sibling runner where exactly this defect cost six lanes and ~2.3M tokens: a +// top-level option was accepted, ignored, and the run looked normal. The arg blobs passed here are +// kilobytes of hand-written JSON, so a single typo -- `lens` for `lenses`, `maxRound` for +// `maxRounds` -- silently changes what runs while every log line still looks right. Fail loudly at +// dispatch instead, where it costs seconds. +const KNOWN_ARGS = ['tip', 'lanes', 'maxRounds', 'lockExtra', 'lenses', 'land', 'integrationBranch', 'trial'] +// `blockedTargets` was documented in the args comment above but omitted here, so a caller +// following the documented interface aborted with "unknown option(s)". Documented and accepted +// must be the same set — a doc that describes an input the code rejects is the same defect class +// as a doc that claims a property the code lost. +const KNOWN_LANE_KEYS = ['key', 'bead', 'wt', 'owned', 'brief', 'accept', 'reviewOnly', 'priorResult', 'blockedTargets'] +{ + const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) + for (const l of ARGS.lanes || []) { + for (const k of Object.keys(l || {})) { + if (!KNOWN_LANE_KEYS.includes(k)) unknown.push(`lanes[${(l && l.key) || '?'}].${k}`) + } + } + if (unknown.length) { + throw new Error( + `lane-fanout: unknown option(s) ${unknown.join(', ')} — this harness would ignore them silently. ` + + `Known top-level: ${KNOWN_ARGS.join(', ')}. Known per-lane: ${KNOWN_LANE_KEYS.join(', ')}.`, + ) + } +} + +const TIP = ARGS.tip +const LANES = ARGS.lanes || [] +const MAX_ROUNDS = ARGS.maxRounds || 3 + +if (!TIP) throw new Error('lane-fanout: args.tip is required (the SHA every lane diffs against)') +if (!Array.isArray(LANES) || !LANES.length) throw new Error('lane-fanout: args.lanes must be a non-empty array') +for (const l of LANES) { + for (const f of ['key', 'wt', 'owned', 'brief', 'accept']) { + if (!l || !l[f]) throw new Error(`lane-fanout: lane ${l && l.key ? l.key : ''} is missing required field "${f}"`) + } +} + +// TWO LANES MAY NEVER SHARE A WORKTREE. Presence checks alone let two lanes carry the same `wt` +// through validation, and `parallel(LANES.map(...))` then dispatches both concurrently into one root +// — each told to edit and commit there. That is the two-writers-one-worktree failure this programme +// has already paid for twice: once as symptoms that looked like filesystem corruption (files edited +// at unclaimed timestamps, a deleted file reappearing, a bound silently changing), and once as 28 +// duplicate beads when a second writer was resurrected into live shared state. The lock contract +// tells each implementer to build on whatever the worktree contains, which makes the collision +// silent by design — the second lane treats the first lane's half-finished edits as its own baseline. +// Refuse at dispatch, where it costs nothing. +{ + const seen = new Map() + for (const l of LANES) { + const prior = seen.get(l.wt) + if (prior) { + throw new Error( + `lane-fanout: lanes "${prior}" and "${l.key}" both declare worktree ${l.wt}. Concurrent lanes ` + + 'must have disjoint worktrees — two implementers in one root overwrite each other silently, ' + + 'because the lock tells each to build on what it finds. Give them separate worktrees, or run ' + + 'them in separate invocations.', + ) + } + seen.set(l.wt, l.key) + } + const keys = new Set() + for (const l of LANES) { + if (keys.has(l.key)) throw new Error(`lane-fanout: duplicate lane key "${l.key}" — labels and telemetry would merge two lanes into one.`) + keys.add(l.key) + } + + // OVERLAPPING OWNED ROOTS ARE THE SAME COLLISION, DEFERRED TO LAND. Separate worktrees mean two + // lanes cannot corrupt each other's files while they build, so this one survives every reviewer + // and every verifier: each diff is correct in isolation. It fires at LAND time, on the + // integration branch, after the whole run has been paid for — two independent rewrites of the + // same files from the same base, handed to a lander with no authority to judge either. Refuse it + // where the duplicate worktree is refused, for the cost of a string compare. + // + // An owned root is a list of path patterns, sometimes with prose around it. Reduce each entry to + // the fixed path prefix before its first wildcard segment; two lanes collide when one prefix is + // the other, or is a PATH prefix of it (segment-aligned, so crates/ab does not collide with + // crates/a). + // Canonicalise before compare: `./backend/crates/foo` and `backend/crates/foo` must collide. + // Collapse `.` segments; reject `..` rather than silently rewriting ownership. + const canonicalizeOwnedPrefix = (t) => { + const segs = [] + for (const seg of String(t).split('/')) { + if (seg.includes('*')) break + if (!seg || seg === '.') continue + if (seg === '..') { + throw new Error( + `lane-fanout: owned path contains '..' (${JSON.stringify(t)}) — refuse rather than collapse ` + + 'ownership across directories.', + ) + } + segs.push(seg) + } + return segs.join('/').replace(/\/+$/, '') + } + const ownedPrefixes = (owned) => String(owned) + .split(/[\s,]+/) + .filter(Boolean) + // Path-shaped tokens only. Comparing every word would refuse two lanes for sharing "the". + .filter((t) => t.includes('/') || t.includes('*') || /\.\w+$/.test(t)) + .map(canonicalizeOwnedPrefix) + const pathOverlap = (a, b) => a === b || a === '' || b === '' || a.startsWith(`${b}/`) || b.startsWith(`${a}/`) + const roots = LANES.map((l) => ({ key: l.key, owned: l.owned, prefixes: ownedPrefixes(l.owned) })) + for (const r of roots) { + // A guard that examines zero subjects must FAIL. An owned root naming no path cannot be + // compared with anything, and it is also useless as the reviewer's IN-SCOPE PATHS list. + if (!r.prefixes.length) { + throw new Error( + `lane-fanout: lane "${r.key}" declares an owned root with no path in it (${JSON.stringify(r.owned)}). ` + + 'The overlap check would examine nothing and pass, and the reviewer would be handed prose as ' + + 'its scope list. Name the paths.', + ) + } + } + for (let i = 0; i < roots.length; i++) { + for (let j = i + 1; j < roots.length; j++) { + const hit = roots[i].prefixes + .flatMap((a) => roots[j].prefixes.map((b) => [a, b])) + .find(([a, b]) => pathOverlap(a, b)) + if (hit) { + throw new Error( + `lane-fanout: lanes "${roots[i].key}" and "${roots[j].key}" declare OVERLAPPING owned roots ` + + `(${hit[0] || ''} vs ${hit[1] || ''}). Separate worktrees keep them from ` + + 'corrupting each other while they build, so nothing before LAND can see this — the collision ' + + 'arrives on the integration branch after every reviewer has passed, as two independent ' + + 'rewrites of the same files from the same base. Narrow one lane, or run them in sequence.', + ) + } + } + } +} + +const BASE_LOCK = ` +=== LOCK CONTRACT (binding; violation = rejected work) === +GIT — these caused a real multi-agent collision in the Bun rewrite and again in this program: + NO stash / stash pop / reset (any mode) / checkout / rebase / merge / clean + NO push, NO force-push, NO git worktree add|remove, NO branch creation. + PERMITTED: git status, git diff, git log, 'git add ', 'git commit' of those paths. + Commit ONLY inside your owned root. If the worktree already contains work, BUILD ON IT — you may + not reset or revert it. Fix forward. +BUILD: + Run from the WORKTREE ROOT. Never 'cd backend'. Never a bare 'cargo test' or --workspace. + Scope every invocation: cargo test --locked --manifest-path backend/Cargo.toml -p ... + PostgreSQL-backed targets: tools/ci/cargo_needs_postgres.sh --only --num-threads=1 + *** NEVER pass --workflow-only. Dark targets carry in_workflow_postgres_job=false, so it selects + ZERO targets and exits 0. That is a FALSE GREEN, not a pass. Always --only. *** + sqlx::query! is COMPILE-TIME checked against a live schema; SQLX_OFFLINE=true compiles with no + database (offline cache committed at backend/.sqlx/). A hand-made createdb will NOT work — it + lacks the role topology and fails at compile time with 'role "anonymous" does not exist'. +CHANGE DISCIPLINE: + Minimal and mechanical. Do not refactor, tidy or rename anything near the fix. Improving while + fixing is what previously broke a lane into 99 errors across 8 crates it never opened. + If you need a paragraph-long comment to justify a workaround, the code is wrong — fix the code. +NEVER WEAKEN THE ORACLE: + No deleted tests, no #[ignore], no relaxed or loosened assertions, and above all NEVER make a + test pass by conforming it to the defect. Multiple lanes in this program were rejected for + exactly that, and in each case the "green" test was hiding a live production outage. +AN ENFORCEMENT MUST BE ABLE TO SEE ITS SUBJECT +RUN WHAT CI RUNS, BEFORE YOU REPORT DONE: + A defect that CI catches and the lane did not is a HARNESS failure, not a CI success. The lane had the + same tree, the same commands and more context; CI just had a checklist. The asymmetry is that accept + criteria are prose and CI is commands, so the lane satisfies a sentence while CI executes a gate. + Before status=done, find the commands CI will actually run over the files you touched -- read + .github/workflows/ci.yml rather than guessing -- and RUN THEM. At minimum, for the paths in your diff: + the formatter, the linter with the repo's own flags, the unit target, and any repo gate whose name + matches your area. Put the exact command lines and their exit codes in verification.commands. + If a gate cannot run locally, say WHICH and WHY there, rather than omitting it and reporting green. + A lane that reports done without having run the gates has reported an intention, not a result. + TESTS NAMED *_as_runtime_role.rs NEED A DATABASE, and a plain cargo test does NOT give them one -- + it fails ALL of them in about 0.01s, including tests that were passing, which reads like your + change broke everything when nothing ran at all. The invocation takes the repo root as its FIRST + argument and is easy to get wrong three times in a row: + tools/lanes/pgtest.sh "$PWD" cargo test -p --test + Without the leading repo root it tries to lstat a file named cargo and exits having run nothing. + A rule that cannot be followed without tribal knowledge is a rule that gets skipped, so the exact + line is written here rather than left in an ADR. + +A TEST THAT BUILDS ITS OWN SUBJECT MEASURES THE STUB, NOT THE DEPLOYMENT: + The sharper form of the rule above, and the one that actually shipped. A suite proved a dispatch + derivation TOTAL over every target in the contract -- six tests, all green, none of them naming a + target so none of them able to go stale. At the same moment the production composition root + registered ZERO of those thirteen targets. Both facts were true, because the tests constructed + their own registry from stub ports and then measured the thing they had just built. + The claim was about the DEPLOYED registry. The control could see a subject; it could not see THAT + subject. So when your evidence is a test, say plainly which of these it is: + - MECHANISM: the thing derives / fails closed / refuses the wrong payload. Stubs are correct + here, and a stub is the only way to test a fourteenth target that does not exist yet. + - WIRING: the composition root actually installs it. This one must drive the REAL constructor + -- "super::the_production_fn(...)" -- and must fail when a registration line is deleted. + A condition phrased as "X does not require hand-written Y per action" is a WIRING claim. Mechanism + evidence does not close it, however total the mechanism test is. If your redBaseline was produced + by deleting a line from a test fixture rather than from the composition root, you have proved the + fixture.: + If your change adds or modifies a gate, check, census, guard or invariant, answer TWO questions + in writing BEFORE you build it, and put the answers in enforcementPlacement: + (1) WHERE does it run in the sequence, and does its subject EXIST yet at that point? + (2) What is the FINEST distinction its data source can express? + Both have already shipped as no-ops in this program. A canonical-writer census was placed in a + reconcile script that runs BEFORE migrations, so it matched zero tables and its REVOKE loop + iterated nothing in every automated path — and the lane recorded "succeeds on a bare cluster" as a + feature, which is exactly how the no-op hid. Separately, a database-capability control was + specified to enforce per-CRATE ownership, but every crate connects as the same role (console_rt), + so the finest distinction available to it was per-ROLE and the crate boundary was never drawn. + A check that never runs and a check that runs blind both exit 0. Neither shows up in a test count. + THEREFORE: "examined zero subjects" MUST be a FAILURE, never a pass. And never claim a control + covers a distinction its data source cannot express — say what it actually enforces, and name the + residual gap in followUps. +PERIPHERALS ARE PART OF THE CHANGE, NOT A FOLLOW-UP: + A change is not done when the code compiles. Before you report done, find everything that + DESCRIBES the behaviour you changed and bring it with you: + - the module doc (//! and ///) of every file you touched, especially any comment that + ENUMERATES something you just made total, or claims a property you just changed; + - registries, rosters, baselines and ratchets that name what you added or removed; + - the bead / issue text, if the change makes its description wrong; + - any doc under docs/** that states the thing you changed as fact. + Docs here rot in ONE direction: they describe holds already lifted and problems already fixed, so + a stale doc reads as a live constraint and someone re-solves a solved problem. A module doc that + says "the three ways X can happen are each pinned separately" after you found a fourth is not an + inaccuracy, it is a false claim about a control. + SCOPE RULE, same as everywhere else: update the peripherals you OWN; for a leased one, report the + exact edit in followUps. Never leave a doc contradicting the code you just shipped, and never + silently widen scope to fix a doc you were not given. + + MECHANICAL, AND NOT OPTIONAL — some peripherals are GENERATED, and prose about keeping things + current does not update a checksum. A change that is otherwise entirely correct fails CI because a + file some script writes is now stale. + DO NOT work from a list of generators. The first version of this clause named exactly one (the + documentation manifest) and the very next lane was failed by a different one (the first-party BUCK + faces). A list of the faces you have been burned by is not a rule, it is a record of your own + history -- and this lock says two paragraphs down that the third spelling means the mechanism is + wrong. The mechanism is: REGENERATE, THEN ASK GIT. + run every generator the repo exposes for the areas your diff touches, then: + git status --porcelain # ANY output = your commit is incomplete + git is the oracle because it cannot be fooled by a face you did not think of. Two entry points + worth knowing, neither of which is the whole set: tools/buck/preflight.sh (what CI's preflight job + actually runs, covering every generated Buck face) and + node scripts/console/generate-documentation-manifest.mjs --write. + This is a POSTFLIGHT check: run it after your last edit, not before. It is here rather than left to + the reviewer because it is decidable by a command, and a lane that can run the command has no + business spending a review round on it. +THE THIRD SPELLING MEANS THE MECHANISM IS WRONG, NOT THE LIST: + If you are fixing the SAME class of bug for the third time in a different spelling, stop patching + and replace the mechanism. Measured: a gate hand-lexed Rust and was defeated by '} // end tests', + then 'use path::{A, B}', then a char literal, a block comment and a raw string; its hand-written + cfg rule was defeated by not(all(test)) and then by any(test, X). Each fix was correct and each + left a sibling live, because the set of spellings is open-ended. In both cases a total primitive + already existed (a real parser; has_table_privilege, which answers ownership, recursive + membership, column grants and superuser in one call) and replacing the enumeration DELETED more + code than it added. Before the third patch, ask: what already answers this question totally? + Say so in followUps if the total answer needs a dependency or a leased file -- a precise request + is a complete result. +TEST THE CONTROL BY EXECUTING IT, NOT BY READING IT: + A contains()/substring assertion over a gate's own source text is not evidence the gate works. A + reviewer inverted a census to 'IF leaked IS NOT NULL AND false THEN', killing it entirely, and all + 16 tests stayed green. Mutate the control itself and prove each mutation goes RED. +ROOT CAUSE, NOT SYMPTOM: + Before editing, grep every caller of the function you are about to touch. One guard in the shared + function beats a guard in each caller, and patching only the reported path leaves siblings broken. +CONTRACT TESTS ARE PART OF THE CHANGE: + Before you edit behaviour, find every test that ENCODES the behaviour you are changing — + including integration suites in other crates (backend/app/tests/** is the usual one). Changing a + contract necessarily breaks the tests asserting the old contract; that is the change, not a + regression. If such a test is OUTSIDE your owned root, STOP and report it in followUps with the + exact file and assertions BEFORE you build. Do not silently break it, and do not abandon the + work. This has been mis-scoped four times in this program: a lane authorised to fix a defect but + forbidden the crate holding it, authorised to create a crate but forbidden the workspace + manifest, and twice authorised to change a contract but forbidden the test that encodes it. +` + +const LOCK = BASE_LOCK + (ARGS.lockExtra || '') + +const BUILD_SCHEMA = { + type: 'object', + required: ['status', 'summary', 'filesChanged', 'redBaseline', 'verification', 'contractBreaches', 'enforcementPlacement', 'peripheralsUpdated'], + properties: { + status: { type: 'string', enum: ['done', 'partial', 'blocked'] }, + summary: { type: 'string' }, + filesChanged: { type: 'array', items: { type: 'string' } }, + redBaseline: { type: 'string', description: 'the failing test written FIRST and its exact failure output, before implementation' }, + verification: { type: 'string', description: 'EXACT commands run and EXACT pass/fail counts; an independent agent will re-run these' }, + commands: { type: 'array', items: { type: 'string' }, description: 'the verbatim commands an independent verifier should re-run' }, + contractBreaches: { type: 'string' }, + // Required, with an explicit n/a escape, so that OMITTING the answer is impossible rather than + // merely discouraged. A prose clause in the lock can be skimmed; a schema field cannot. + enforcementPlacement: { + type: 'string', + description: + 'If this change adds or modifies any gate/check/census/guard: WHERE in the sequence it runs and whether its subject exists at that point, and the FINEST distinction its data source can express. State how "examined zero subjects" fails. If the change adds no enforcement, write exactly: n/a - adds no enforcement.', + }, + // Required for the same reason as enforcementPlacement: a lock clause can be skimmed, a schema + // field cannot. Doc drift is invisible in a test count, which is exactly why it accumulates. + peripheralsUpdated: { + type: 'string', + description: + 'Every doc/comment/registry/bead that DESCRIBED the behaviour you changed: what you updated (you own it), and what you are reporting instead (leased). Include module docs whose claims your change invalidates. If nothing described this behaviour, write exactly: n/a - nothing described this behaviour, and say how you checked.', + }, + followUps: { type: 'string' }, + }, +} + +const REVIEW_SCHEMA = { + type: 'object', + required: ['verdict', 'findings'], + properties: { + verdict: { type: 'string', enum: ['reject', 'accept_with_findings', 'accept'] }, + findings: { + type: 'array', + items: { + type: 'object', + required: ['severity', 'claim', 'failureScenario', 'location', 'provenByExecution', 'ownerLease'], + properties: { + severity: { type: 'string', enum: ['blocker', 'major', 'minor'] }, + claim: { type: 'string' }, + failureScenario: { type: 'string' }, + location: { type: 'string' }, + // Severity alone is the wrong convergence signal, measured: a round converged on + // blockers=0 while both reviewers returned accept_with_findings carrying SIX distinct + // fail-opens they had each PROVEN BY RUNNING -- a census blind to the table owner, a + // partial-roster shrink that passed, a contains() wiring check defeated by a '#', a + // silent Docker-absent skip that certified an unexecuted census as green. Every one was + // labelled "major" and every one was waved through. What separates those from prose + // is not severity, it is whether the reviewer OBSERVED the failure. + provenByExecution: { + type: 'boolean', + description: 'TRUE only if YOU ran a command and OBSERVED the failure -- you have the output. Reasoning from source, however sound, is FALSE. Be strict: this field decides whether the lane rebuilds.', + }, + // The lease carve-out is a rule about WHOSE work it is, so it must survive the severity + // it was filed under; a lease item labelled blocker would otherwise make every + // test-adding lane permanently unconvergeable. + ownerLease: { + type: 'boolean', + description: 'TRUE if this is a companion edit the INTEGRATION OWNER must land (a leased path), not a defect in the lane. These never block convergence at any severity.', + }, + }, + }, + }, + oracleWeakened: { type: 'boolean' }, + scopeCreep: { type: 'boolean' }, + }, +} + +// An independent re-runner. Lanes self-report their own green; nobody checked that in the first +// four runs, and the integration owner had to re-run everything by hand afterwards. This makes the +// check part of the pipeline. +const VERIFY_SCHEMA = { + type: 'object', + // A GREEN MUST BE ANSWERED FOR, NOT MERELY ASSERTED. reproduced + contradictsClaim say the + // verifier ran something and agreed with it. They do not say WHAT it ran, whether any command + // selected zero tests and still exited 0, or whether the suite still proves as much as it did. + // Oracle integrity is the most common rejection cause in this programme and a STANDING review + // lens, yet this schema let a verifier certify a green without ever answering it — and a field + // that is optional is a field that gets skipped. Requiring the command list is the same + // discipline as `commands` on the build side: a claim nobody can re-run is a claim. + required: ['reproduced', 'actualResults', 'discrepancies', 'contradictsClaim', 'headSha', 'falseGreenRisk', 'commandsRun', 'oracleIntact'], + properties: { + // WHAT was verified, not just whether. Landing cherry-picks a worktree AFTER review finishes, + // and "clean working tree" is not a binding: a commit added after the reviewers finished is + // clean too, and would land as if reviewed. This is the immutable head the review round + // actually applies to, captured by the one agent that is read-and-run only. + headSha: { type: 'string', description: 'output of `git rev-parse HEAD` in the worktree, verbatim. This is the head the review round binds to; landing refuses anything else.' }, + reproduced: { type: 'boolean', description: 'true only if YOU ran the commands and saw the claimed results' }, + actualResults: { type: 'string', description: 'the exact output you observed, not what was claimed' }, + discrepancies: { type: 'string', description: 'any difference between claimed and observed, or "none"' }, + // The VERIFIER decides whether its findings matter -- not a regex over its prose. A previous + // version required discrepancies to be the literal string "none", so a conscientious verifier + // writing "four, all minor; none contradicts the verdict" failed the check and the lane was + // sent back for another build round. Convergence was effectively unreachable whenever the + // verifier ran: every lane with one reported converged=false while every review-only lane + // reported true. That defect manufactured rebuild rounds, which are the dominant cost here. + contradictsClaim: { type: 'boolean', description: 'TRUE only if what you observed CONTRADICTS the claimed result - a count that differs, a command that failed, a false green. Cosmetic differences (line numbers, timings, wording) are FALSE.' }, + falseGreenRisk: { type: 'string', description: 'did any command select ZERO tests and still exit 0? Answer for every command you ran; "none" is a valid answer, silence is not.' }, + commandsRun: { + type: 'array', + items: { type: 'string' }, + description: 'the commands you ACTUALLY ran, verbatim, in the order you ran them. Not the commands you were given — the ones you executed. An empty list means you verified nothing.', + }, + oracleIntact: { + type: 'boolean', + description: 'TRUE only if the suite still proves as much as it did: no test deleted, no #[ignore] added, no assertion loosened, and no test made to pass by conforming it to the defect. FALSE if you observed any of those OR could not tell.', + }, + }, +} + +// STANDING lenses run on EVERY lane, every round. They are not defaults. +// +// They used to be defaults -- `ARGS.lenses || [...]` -- and every single invocation of this harness +// passed `lenses`, so the fallback never once evaluated. Oracle integrity is the most common +// rejection cause in this program and it had never been reviewed for; it was only ever caught +// incidentally by a custom lens that happened to look. A default that is always overridden is not a +// default, it is dead code that reads as coverage. +// MODEL TIER: FRONTIER WHERE THE DECISION IS, CHEAP WHERE THE WORK IS. +// +// Every agent in this directory ran at the session model, which is the expensive one. Cursor's +// swarm measured the same worker fleet at $9,373 with a frontier model and $411 with a cheap one +// behind a frontier planner -- 23x -- on the argument that "few moments in a large task genuinely +// require frontier intelligence, such as the original decomposition, the design decisions, and +// certain trade-offs." This programme has now lost two runs to account limits, one of them 117 +// agents deep, so this is not a theoretical saving. +// +// The line we draw is NOT "cheap for reads, expensive for writes". It is: +// +// A CHEAPER TIER IS ALLOWED ONLY WHERE AN INDEPENDENT STRONGER PASS AUDITS THE RESULT. +// +// That is the same defence-in-depth argument as decorrelated review lenses: a weaker first pass is +// fine when a stronger one adjudicates it, and is NOT fine when its output is the final word. +// So: batched fan-out that feeds a challenge/reconcile phase may run cheap; anything whose verdict +// is terminal -- the adversarial reviewers, the independent verifier, the single writer, and any +// re-derivation of a dependency edge, where a reversed answer silently reschedules everything -- +// stays on the inherited model. +// NOT APPLIED TO THIS HARNESS'S OWN AGENTS, deliberately, and recorded because the first version of +// this block defined a WORKER constant and used it NOWHERE -- a rule written, documented, believed, +// and reachable by nothing, committed in the very change that added the rule. That is the defect the +// preflight beside this file exists to catch, and it caught it. +// +// A build agent's output DOES qualify under the rule above: adversarial reviewers and an independent +// verifier audit it. What stops it is arithmetic, not principle. This harness converges by ROUNDS, +// and a round costs a full build plus every reviewer plus the verifier, so a tier that needs even one +// extra round costs more than it saves. Unlike Cursor's workers, which execute a planner's explicit +// instructions, a lane here is handed an open-ended brief. Applying it needs a measurement nobody has +// made: rounds-to-converge per tier on the same briefs. Until then, the harnesses tiered are the ones +// whose fan-out is a mechanical read feeding a stronger pass, where a wrong answer costs one re-read. + +// TRIAL ONE LANE BEFORE COMMITTING THE FLEET. +// +// Bun's Zig-to-Rust port ran ONE implementer plus TWO adversarial reviewers over THREE files, and +// only then scaled to 64 agents across 4 worktrees. This harness has always dispatched every lane +// cold, and it has cost real runs: a wave where the brief was wrong in the same way for all four +// lanes is four wasted lanes, not one. +// +// `trial` names a lane key that must CONVERGE before the rest are dispatched. It is not a +// smoke test -- it is the same full build/review/verify cycle, so what it proves is that the LOCK, +// the accept criteria and the review lenses actually work against this tree, which is the part a +// wave gets wrong identically across every lane. +// +// Deliberately opt-in. For two independent lanes the serialisation costs more than it saves; the +// value appears when the lanes share a brief shape, which is exactly when they fail together. + +// Judges, verifiers and writers deliberately omit `model` so they inherit the session's. + +const STANDING_LENSES = [ + 'MAINTAINABILITY / COST OF CARRY — every other lens asks whether the change is CORRECT. This one asks what it costs to keep, and its default verdict is DELETE. (a) SPRAWL: does this add a doc, a crate, a script, a config, a workflow step or a process that duplicates one that exists? A second file describing the same fact is a future contradiction, not documentation. Name the existing thing it should have extended. (b) COMMENT BLOBBING: is there a paragraph of prose where a name would do? A comment that restates the code is noise that goes stale independently; a comment earning its place explains WHY, names a measurement, or records a rejected alternative. Twenty lines of comment over five lines of code is a defect in this repository, not thoroughness. (c) IDIOM: would a competent Rust/JS reader of this repo write it this way, or is it this author\'s private dialect? Hand-rolled parsing where a library exists, and enumerations where the language has a total construct, are the two that recur here. (d) AUTOMATION: is a human being asked to remember something a command could decide? If the accept criteria contain a step a script could run, that step WILL be skipped eventually. (e) UNDOCUMENTED-BUT-SHOULD-BE: the inverse of sprawl. A non-obvious constraint, a measured number, or a deliberate asymmetry that exists only in the author\'s head is undocumented, and the next lane will \'simplify\' it away. Report the NET line count of prose and config this change adds. A change that adds more explanation than behaviour needs a reason.', + 'CORRECTNESS + ORACLE INTEGRITY — does the change address the root cause, and does the suite still prove as much as before? Hunt for tests conformed to defects and assertions that would pass even if the behaviour were broken. Pick the load-bearing assertion, break the code it guards, and say whether it actually goes RED.', + 'PERIPHERAL DRIFT — read the diff, then go looking for what it made WRONG somewhere else. Does any module doc, /// comment, registry, roster, baseline, docs/** page or bead text still describe the behaviour as it was before this change? Pay closest attention to comments that ENUMERATE ("the three ways X can happen", "these are the cases") next to code this change made total or extended — those are false claims about a control, not stale prose. Verify the build agent\'s peripheralsUpdated field against the actual tree rather than trusting it, and check the reverse direction too: a doc updated to describe something the code does NOT do is worse than a stale one. Leased peripherals correctly reported in followUps are ownerLease=true, not defects.', + 'ENFORCEMENT PLACEMENT — for every gate/check/census/guard this change touches, ignore whether its LOGIC is right and ask only whether it can SEE its subject. (a) Where does it run in the sequence, and does its subject exist yet at that point? (b) What is the finest distinction its data source can express, and does the change claim a finer one? (c) Is the rule TOTAL over its domain, or is it an enumeration of spellings that a reviewer can always add one more to? If the change closes named cases rather than making the class unrepresentable, name the total primitive it should have used instead. (d) Does "examined zero subjects" fail, or pass? (d) Is it tested by EXECUTING it, or by a contains() over its own source text — mutate the control and check the tests go RED. Both failure modes have shipped here: a census that ran before migrations existed, and a per-crate rule enforced by a data source that only distinguishes roles. Verify the answers in enforcementPlacement rather than trusting them.', +] + +const LENSES = [ + ...STANDING_LENSES, + ...(ARGS.lenses || [ + 'BLAST RADIUS — does any public wire contract, stored format, or authorization outcome change shape? Who can now do what they could not before, or vice versa? Consider generated clients, rows already written under the old format, and callers in other crates.', + ]), +] + +// --- telemetry ------------------------------------------------------------- +// Measured across six runs of this harness: ~130 agents, ~12M tokens, ~5.7h wall-clock, at a ratio +// of 3.67 checkers per build. Wall-clock divided by builds is 7-13 min per ROUND, and reviews run +// in parallel, so a round costs roughly one build plus one review wave. +// +// That makes ROUNDS the scarce resource, not tokens. And an audit of why rounds were spent found +// most early ones went to defects in the HARNESS and the BRIEF, not in the lane's code: reviewers +// diffing HEAD~1, findings discarded with no feedback edge, empty-diff auto-reject, reviewers not +// told which paths were owner-leased, a lane authorised to create a crate but forbidden the +// workspace manifest. Each of those cost a full build cycle per affected lane. +// +// The lever follows directly: another reviewer is cheap, another BUILD ROUND is expensive. So +// classify what each round was actually spent on, and let the numbers say whether the next +// improvement belongs in the brief or in the code. +const TELEMETRY = { rounds: [], startedAt: null, checkers: [] } + +// What holds a lane open. Severity is the reviewer's opinion; provenByExecution is a fact about +// whether they watched it fail. A proven fail-open holds the lane regardless of the label it was +// filed under, and an owner lease releases it regardless -- because a lease is a statement about +// whose work it is, not about how bad it is. +function isBlocking(f) { + if (f.ownerLease === true) return false + if (f.severity === 'blocker') return true + return f.severity === 'major' && f.provenByExecution === true +} + +function classifyRejection(blockers, weakened, verifierOk, status, checkerDied) { + // Coarse, deliberately: the point is to see the SHAPE of wasted rounds, not to be precise. + const text = blockers.map((b) => `${b.claim} ${b.location}`).join(' ').toLowerCase() + // A round lost to a dead checker is an INFRASTRUCTURE cost, not a defect in the lane's claim. + // Filing it as unreproducible-claim would tell the human to fix a brief that was never wrong. + if (checkerDied) return 'checker-died' + if (!verifierOk) return 'unreproducible-claim' + if (weakened) return 'oracle-weakened' + if (/outside the authorised|in-scope path|scope list|not in scope/.test(text)) return 'scope-brief-defect' + if (/executes nowhere|ratchet|baseline|ci\.yml|workflow step|lease/.test(text)) return 'owner-lease' + if (status && status !== 'done') return 'incomplete' + if (blockers.length) return 'code-defect' + return 'unclassified' +} + +// --- cross-lane defect ledger: Bun's "fix the generator" ------------------- +const DEFECT_LEDGER = [] + +function recordDefects(laneKey, blockers, weakened) { + for (const b of blockers) { + const claim = (b.claim || '').trim() + if (!claim) continue + const key = claim.slice(0, 80).toLowerCase() + if (DEFECT_LEDGER.some((d) => d.key === key)) continue + DEFECT_LEDGER.push({ key, laneKey, claim, location: b.location || '' }) + } + if (weakened && !DEFECT_LEDGER.some((d) => d.key === '__oracle__')) { + DEFECT_LEDGER.push({ + key: '__oracle__', + laneKey, + claim: 'A reviewer judged that a change made the suite prove LESS than before (oracleWeakened=true).', + location: '', + }) + } +} + +function ledgerBlock(selfKey) { + const others = DEFECT_LEDGER.filter((d) => d.laneKey !== selfKey) + if (!others.length) return '' + return ` +=== DEFECT CLASSES ALREADY CAUGHT IN OTHER LANES THIS RUN === +Found by adversarial review of a DIFFERENT lane. They are CLASSES, not incidents — the same mistake +is available to you. Read them as things not to do. +${others.map((d, i) => `${i + 1}. (lane ${d.laneKey}) ${d.claim}${d.location ? `\n seen at: ${d.location}` : ''}`).join('\n')} +` +} + +function reviewPrompt(l, lens) { + return `You are an ADVERSARIAL CODE REVIEWER. Your ONLY job is to find bugs and reasons this change +does not work. You are NOT told the author's reasoning and must not go looking for it. +Assume the code is WRONG until the diff proves otherwise. + + cd ${l.wt} + git --no-pager diff ${TIP} + git status --porcelain + +*** Diff against ${TIP} exactly as above. Do NOT use 'git diff HEAD~1' — that shows an unrelated + earlier commit's own contents rather than the change under review. If what you are reading has + nothing to do with this lane's subject, you have the wrong diff; re-run the command above. *** + +An EMPTY diff is NOT automatically a rejection. Some work is a proof that no change was needed. +Judge whether the reported evidence supports that claim; reject only if it does not. + +YOUR REVIEW LENS: ${lens} + +Every finding needs a CONCRETE failure scenario: specific inputs or state producing wrong +behaviour. A finding without one is a label, not a finding, and is worthless. + +BLOCKER-level specifically: + - any test made to pass by conforming it to a defect instead of fixing the defect + - any deleted test, added #[ignore], or loosened assertion + - an expectation matching observed output rather than the intended contract, especially anything + environment-dependent (collation, locale, timezone, hash or iteration order) + - a security or authorization decision that FAILS OPEN on error, absence, staleness or + unavailability where the contract requires fail-closed + - a claimed invariant with no test, or a test that would still pass if the invariant were broken + - a backward-compatibility shim with no test proving it reads the legacy form + - edits outside the IN-SCOPE PATHS below + +*** NOT A BLOCKER — INTEGRATION-OWNER LEASES *** +Some files are deliberately withheld from every lane and are applied by the integration owner: + docs/program/executed-tests-baseline.json, .github/workflows/**, tools/ci/postgres-cargo-map.json, + backend/openapi/**, lockfiles, backend/crates/platform/db/migrations/** +A change that ADDS TESTS will therefore, by construction, leave the executed-tests ratchet red and +its new binaries unwired until the owner lands the companion edit. That is EXPECTED and is NOT a +defect in the lane's work. Report it with ownerLease=true and the exact companion edit required — +never as grounds to reject. Rejecting on it makes any test-adding lane permanently unconvergeable, +which would penalise exactly the changes that add the most coverage. +Do still reject if the lane EDITED one of those paths itself. + +*** TWO FIELDS DECIDE WHETHER THE LANE REBUILDS — SET THEM DELIBERATELY *** +provenByExecution: true ONLY if you ran a command and OBSERVED the failure, and you have the output + to show. A finding you reasoned out from the source is false here no matter how confident you are. + A "major" you PROVED forces another build round; a "major" you argued does not. This exists + because a previous round converged clean while both reviewers were holding six separately proven + fail-opens, each filed as an unproven-looking "major" and each waved through. +ownerLease: true for companion edits belonging to the integration owner, per the section above. + These never block at any severity, so a lease item is never a reason to withhold convergence. +If you proved a fail-open by running it, say so with provenByExecution=true even when you are +otherwise willing to accept the change. Your verdict and this field are independent. + +=== IN-SCOPE PATHS (authorised) === +${l.owned} +This is SCOPE, not rationale. It says which files were legitimately in play and nothing about +whether the author got them right. Edits inside are not scope creep by location alone but can +still be wrong. Edits outside are a contract breach — report as blockers. + +Set oracleWeakened=true if the suite now proves LESS than before. Default to rejecting when unsure.` +} + +function verifyPrompt(l, fix) { + const cmds = (fix.commands && fix.commands.length ? fix.commands : []).map((c) => ` ${c}`).join('\n') + return `You are an INDEPENDENT VERIFIER. You do not review code and you do not read the author's +reasoning. Your single job is to RE-RUN what was claimed and report what ACTUALLY happens. + + cd ${l.wt} + git rev-parse HEAD <- report this verbatim as headSha, FIRST, before anything else + +That SHA is what this whole review round binds to: the landing step will refuse to cherry-pick this +worktree if its head has moved since you read it. Report what you actually saw, never a guess. + +CLAIMED VERIFICATION: +${fix.verification} + +${cmds ? `COMMANDS TO RE-RUN VERBATIM:\n${cmds}` : 'The author listed no explicit commands. Derive them from the claimed verification above and say so.'} + +RULES: + - RUN the commands. Do not reason about whether they would pass. Reproduced=true only if you + personally observed the result. + - Report the EXACT output you saw, including counts, not the counts that were claimed. + - *** FALSE-GREEN CHECK: if any command reports running ZERO tests and still exits 0, that is a + false green — say so loudly in falseGreenRisk. In this repository --workflow-only selects zero + dark targets and exits 0, which is exactly this trap. *** + - If a command fails for an ENVIRONMENTAL reason (missing dependency, database not provisioned, + Docker down), say so explicitly rather than reporting the change as broken. + - Do NOT edit any file. You are read-and-run only. + +*** THREE FIELDS DECIDE WHETHER THIS GREEN COUNTS. A green you cannot answer for is not a green. *** + commandsRun: every command you ACTUALLY executed, verbatim and in order. Not the list you were + handed — the one you ran. An empty list means nothing was verified, and the lane + rebuilds. + falseGreenRisk: answer it for every command, not just the suspicious one. "none" is an answer. + oracleIntact: read the diff for deleted tests, added #[ignore], loosened assertions, and any + expectation rewritten to match observed output instead of the intended contract. + TRUE only if the suite still proves as much as before. If you could not tell, + that is FALSE — this is the most common rejection cause in this programme and + silence on it used to be free. + +Report any difference between what was claimed and what you observed, however small.` +} + +function buildPrompt(l, fb) { + // A rejection MUST arrive with something actionable. A previous run rejected a lane on verifier + // disagreement alone, rendered an empty "BLOCKERS:" list, and the lane reported: "the rejection + // came with no blocker text, so I re-derived the weakest point of my own diff". It recovered by + // luck. When there are no blockers, state the actual cause and fall back to lower-severity + // findings so the lane always has a concrete starting point. + let reasonLines = [] + if (fb) { + if (fb.weakened) reasonLines.push('A reviewer set oracleWeakened=true: the suite now proves LESS than before. Restoring the oracle is the highest priority.') + if (fb.verifierSaid) reasonLines.push(`The INDEPENDENT VERIFIER re-ran your commands and disagreed with your claimed result: ${fb.verifierSaid}`) + if (fb.notDone) reasonLines.push(`You reported status="${fb.notDone}" rather than "done". If that is genuinely blocked, say so precisely in followUps; if it is finishable, finish it.`) + if (!fb.blockers.length && !reasonLines.length) reasonLines.push('No blocker was recorded, which means the rejection came from a failed convergence check rather than a named finding. Re-derive the weakest point of your own diff and address it.') + } + + const blockerText = fb && fb.blockers.length + ? `BLOCKERS (must all be resolved):\n${fb.blockers.map((b, i) => `${i + 1}. [${b.severity}] ${b.claim}\n where: ${b.location}\n fails when: ${b.failureScenario}`).join('\n')}` + : (fb && fb.lesser && fb.lesser.length + ? `NO BLOCKERS were raised. Lower-severity findings, treat as the actionable list:\n${fb.lesser.map((b, i) => `${i + 1}. [${b.severity}] ${b.claim}\n where: ${b.location}\n fails when: ${b.failureScenario}`).join('\n')}` + : 'NO named findings were recorded this round.') + + const feedback = fb + ? ` +=== ROUND ${fb.round}: THIS LANE WAS REJECTED === +Your earlier change is already in the worktree. Amend it; do not revert wholesale. + +WHY IT WAS REJECTED: +${reasonLines.map((r) => ` - ${r}`).join('\n')} + +${blockerText} + +Fix the CAUSE, not the symptom named. If two findings share a root, fix the root once. +Reviewers see ONLY your diff, never your reasoning — if a finding looks like a misunderstanding, +that is itself a signal the change is not self-evident. Make the code clearer rather than arguing. +If a finding names a path you may not touch, say so in followUps rather than editing it. +` + : '' + + return `You are the IMPLEMENTER for lane "${l.key}"${l.bead ? ` (beads issue ${l.bead})` : ''}. +${feedback}${ledgerBlock(l.key)} +WORKTREE (yours alone, based on ${TIP}): + ${l.wt} +Work ONLY there. Do not touch any other worktree or the primary checkout. + +YOUR OWNED ROOT (the only place you may write): + ${l.owned} + +${l.brief} + +ACCEPTANCE: ${l.accept} + +METHOD — RED BASELINE FIRST, not optional: + 1. WRITE THE FAILING TEST FIRST. Encode the required behaviour as a test and RUN it. Capture the + exact failure output — that is your redBaseline. A lane that implements first and tests after + cannot demonstrate its test would have caught anything. + 2. Implement the smallest change that makes it pass. + 3. Re-run and report EXACT counts. Populate 'commands' with the verbatim commands you ran — an + independent verifier will re-run them and compare against what you claim. + 4. MUTATION-CHECK your own new assertions: break the thing you just fixed, confirm the suite goes + RED, then restore and confirm GREEN. An assertion that still passes when the behaviour is + broken proves nothing. Report this in verification. + 5. Commit only your owned paths. Do not push. + +Cold builds take several minutes; that is expected, not a hang. +If you cannot finish, report "partial" or "blocked" with the exact failure. An honest blocked +result is far more useful than a weakened test. + +${LOCK}` +} + +async function runLane(l) { + let fb = null + let last = { lane: l, fix: null, reviews: [], verify: null, blockers: [], rounds: 0, converged: false } + + // A lane whose implementer already finished gets REVIEW ONLY. Re-running it would duplicate + // committed work and risk a second writer in a worktree, and its diff still needs review. + if (l.reviewOnly) { + const checks = await parallel(LENSES.map((lens) => () => + agent(reviewPrompt(l, lens), { label: `review:${l.key}`, phase: 'Review', schema: REVIEW_SCHEMA }))) + // Same death accounting as the main path: a review-only lane that lost a standing lens has not + // been reviewed for the thing that lens exists to catch, and must not report converged. + const deadStanding = checks + .slice(0, STANDING_LENSES.length) + .map((r, i) => (r ? null : STANDING_LENSES[i].split(' —')[0])) + .filter(Boolean) + const reviews = checks.filter(Boolean) + const blockers = reviews.flatMap((v) => (v.findings || []).filter(isBlocking)) + const weakened = reviews.some((v) => v.oracleWeakened) + // scopeCreep blocks for the same reason oracleWeakened does: the review schema carries a + // dedicated flag, and the reviewer prompt defines an edit outside `l.owned` as a contract + // breach. A reviewer that sets the flag without ALSO restating it as a blocking finding was + // being ignored, so a lane could edit an unowned root, converge, and be handed to the lander. + // The flag is the reviewer's verdict; requiring them to say it twice is how it gets lost. + const creep = reviews.some((v) => v.scopeCreep) + recordDefects(l.key, blockers, weakened) + TELEMETRY.checkers.push({ dispatched: LENSES.length, returned: reviews.length, deadLenses: deadStanding }) + const converged = blockers.length === 0 && !weakened && !creep && deadStanding.length === 0 + log(`${l.key}: review-only -> ${reviews.length}/${LENSES.length} reviewers, ${blockers.length} blocker(s)${weakened ? ', ORACLE WEAKENED' : ''}${deadStanding.length ? `, STANDING LENS DIED: ${deadStanding.join(', ')}` : ''}`) + return { lane: l, fix: l.priorResult || null, reviews, verify: null, blockers, rounds: 0, converged } + } + + for (let round = 1; round <= MAX_ROUNDS; round++) { + const sfx = round > 1 ? `#${round}` : '' + + // Agent death is routine at this scale; retry once before giving up on the round. + let fix = await agent(buildPrompt(l, fb), { label: `build:${l.key}${sfx}`, phase: 'Build', schema: BUILD_SCHEMA }) + if (!fix) { + log(`${l.key}: implementer died in round ${round}, retrying once`) + fix = await agent(buildPrompt(l, fb), { label: `build:${l.key}${sfx}r`, phase: 'Build', schema: BUILD_SCHEMA }) + } + if (!fix) { + log(`${l.key}: implementer died twice in round ${round} — abandoning lane`) + last.rounds = round + break + } + + // Two adversarial readers plus, when there is something to falsify, one independent re-runner. + // + // The verifier exists to test a CLAIMED green by re-running the lane's own commands. A lane + // reporting "partial" or "blocked" is claiming nothing, so there is nothing to falsify and the + // round cannot converge regardless. Running it anyway re-executes full test suites for no + // decision value: in one measured phase, two consecutive rounds converged zero lanes, so six + // verifiers re-ran suites whose result could not have changed any outcome. Reviews still run + // on every round -- a rejected round's findings are exactly what the next build needs. + const claimsGreen = fix.status === 'done' + const checks = await parallel([ + ...LENSES.map((lens) => () => agent(reviewPrompt(l, lens), { label: `review:${l.key}${sfx}`, phase: 'Review', schema: REVIEW_SCHEMA })), + ...(claimsGreen ? [() => agent(verifyPrompt(l, fix), { label: `verify:${l.key}${sfx}`, phase: 'Review', schema: VERIFY_SCHEMA })] : []), + ]) + // A DEAD REVIEWER IS NOT AN ABSENT FINDING. parallel() resolves a died agent to null, and + // .filter(Boolean) used to make it disappear -- so a round could converge on one surviving + // reviewer while three others died, and the log said nothing. That is a false-green path, and + // it fired repeatedly in one measured session: session-limit kills took 4 of 7 agents from one + // run, 3 of 3 from another, and an adjacent workflow's synthesis step silently ran on 2 of 3 + // inputs. Convergence must know how many eyes actually reported. + // + // Index order from parallel() matches the dispatch order, so a null identifies WHICH lens died. + // STANDING lenses are non-negotiable: if one of them did not report, the lane has not been + // reviewed for oracle integrity, enforcement placement or peripheral drift, and it may not + // converge no matter what the survivors said. A custom lens dying is logged and tolerated, + // because forcing a whole rebuild round over it costs more than it saves. + const reviewSlots = checks.slice(0, LENSES.length) + const deadLenses = reviewSlots + .map((r, i) => (r ? null : LENSES[i].split(' —')[0].split(' ').slice(0, 3).join(' '))) + .filter(Boolean) + const deadStanding = reviewSlots + .slice(0, STANDING_LENSES.length) + .map((r, i) => (r ? null : STANDING_LENSES[i].split(' —')[0])) + .filter(Boolean) + const reviews = reviewSlots.filter(Boolean) + const verify = claimsGreen ? (checks[LENSES.length] || null) : null + if (!claimsGreen) log(`${l.key}: round ${round} reported "${fix.status}" — verifier skipped, nothing green to falsify`) + if (deadLenses.length) { + log(`${l.key}: round ${round} — ${reviews.length}/${LENSES.length} reviewers returned; DIED: ${deadLenses.join(', ')}`) + } + if (deadStanding.length) { + log(`${l.key}: round ${round} CANNOT CONVERGE — standing lens(es) never reported: ${deadStanding.join(', ')}`) + } + + const blockers = reviews.flatMap((v) => (v.findings || []).filter(isBlocking)) + const weakened = reviews.some((v) => v.oracleWeakened) + // scopeCreep blocks for the same reason oracleWeakened does: the review schema carries a + // dedicated flag, and the reviewer prompt defines an edit outside `l.owned` as a contract + // breach. A reviewer that sets the flag without ALSO restating it as a blocking finding was + // being ignored, so a lane could edit an unowned root, converge, and be handed to the lander. + // The flag is the reviewer's verdict; requiring them to say it twice is how it gets lost. + const creep = reviews.some((v) => v.scopeCreep) + recordDefects(l.key, blockers, weakened) + TELEMETRY.checkers.push({ dispatched: LENSES.length + (claimsGreen ? 1 : 0), returned: reviews.length + (verify ? 1 : 0), deadLenses }) + + // A lane is not green because it says so. The verifier must have reproduced it. + // The verifier judges whether its own findings contradict the claim; contradictsClaim is a + // required field, so there is no prose to parse and nothing to fall back to. + // + // A DEAD VERIFIER IS NOT A PASSED VERIFICATION. This used to read `!verify || (...)`, so a + // verifier killed by a session limit made verifierOk TRUE and the lane converged while the log + // said "independently re-verified" -- the exact false-green class this harness exists to + // remove, and worse than the rest because the log asserts the opposite of what happened. + // A dead CUSTOM lens is tolerated because four other eyes still read the diff; the verifier has + // no such substitute. It is the ONLY thing that re-runs a claimed green, so its death leaves + // the claim untested and the lane must rebuild. + // The deliberate exception stands: when the build claims nothing (`claimsGreen` false) no + // verifier is dispatched, there is nothing to falsify, and its absence is not a death. + // + // AND A GREEN NOBODY ANSWERED FOR IS NOT A GREEN. The schema now REQUIRES the command list, the + // false-green answer and an oracle-integrity verdict, but a schema is a request to an agent and + // this is the enforcement: a verifier that reproduced the result while leaving any of the three + // unanswered has told us it agreed without saying what it ran or whether the suite still proves + // anything. Absence is a NO here, never a yes. + const verifierDied = claimsGreen && !verify + // COUNT THE COMMANDS AGAINST WHAT WAS CLAIMED, not against zero. `length > 0` let a verifier + // that re-ran ONE of the implementer's five commands satisfy the check, and with reproduced=true + // and contradictsClaim=false the lane converged while four suites were never independently run. + // "Some of it was re-run" is not independent verification of a green; it is a sample of one. + const claimedCommands = Array.isArray(fix.commands) ? fix.commands.filter((c) => typeof c === 'string' && c.trim()) : [] + // Every commandsRun entry must be a non-empty string. Filtering blanks used to turn + // `commandsRun: [""]` (or `["cargo test", ""]`) into a quieter shape and let a hollow list + // look like "no commands named" rather than "the verifier answered with nothing". Fail closed: + // any blank or non-string entry means the verifier did not answer for what it ran. + const rawRan = verify && Array.isArray(verify.commandsRun) ? verify.commandsRun : [] + const commandsRunWellFormed = rawRan.length > 0 + && rawRan.every((c) => typeof c === 'string' && c.trim() !== '') + const ranCommands = commandsRunWellFormed ? rawRan.map((c) => c.trim()) : [] + // Compared as a SET over normalised text rather than by count, because a verifier that ran the + // same command five times would satisfy a count and prove nothing. Coverage is against EVERY + // claimed command, not merely "ran at least one". + const norm = (c) => c.replace(/\s+/g, ' ').trim() + const ranSet = new Set(ranCommands.map(norm)) + const unrun = claimedCommands.filter((c) => !ranSet.has(norm(c))) + // A done implementer that omits `commands` or returns only blanks used to make + // `unrun.length === 0` vacuously true: the verifier could name any command of its own and the + // lane converged without independently re-running anything the build claimed. Status=done + // requires a nonempty claimed-command set before coverage can be judged. + const verifierAnswered = !!verify + && claimedCommands.length > 0 + && commandsRunWellFormed + && ranCommands.length > 0 + && unrun.length === 0 + && typeof verify.falseGreenRisk === 'string' && verify.falseGreenRisk.trim() !== '' + && verify.oracleIntact === true + const verifierOk = !claimsGreen + || (!!verify && verify.reproduced === true && verify.contradictsClaim === false && verifierAnswered) + const verifierSaid = verifierOk + ? null + : (verifierDied + ? 'the INDEPENDENT VERIFIER died before reporting, so nothing re-ran your claimed green. Re-run your own commands and report the exact output.' + : (claimedCommands.length === 0 + ? `the build claimed status=done but named no well-formed commands (${JSON.stringify(fix.commands)}); a green with nothing claimed cannot be independently verified. List the commands you ran.` + : (unrun.length + ? `the verifier re-ran ${ranCommands.length} command(s) but the build claimed ${claimedCommands.length}; these were NEVER independently run: ${unrun.join(' | ')}. A green re-run of part of the evidence is a sample, not a verification.` + : (verify && verify.reproduced === true && verify.contradictsClaim === false + ? `the INDEPENDENT VERIFIER reproduced your result but could not answer for it: commandsRun=${JSON.stringify(verify.commandsRun)}, falseGreenRisk=${JSON.stringify(verify.falseGreenRisk)}, oracleIntact=${verify.oracleIntact}. Either it ran nothing it could name, or it judged the oracle no longer intact. Make the commands reproducible and show that the suite still proves what it did before.` + : `reproduced=${verify && verify.reproduced}; contradictsClaim=${verify && verify.contradictsClaim}; discrepancies=${verify && verify.discrepancies}; falseGreenRisk=${verify && verify.falseGreenRisk}`)))) + if (verifierDied) log(`${l.key}: round ${round} CANNOT CONVERGE — VERIFIER DIED; the claimed green was never re-run`) + + last = { lane: l, fix, reviews, verify, blockers, rounds: round, converged: false } + + if (fix.status === 'done' && blockers.length === 0 && !weakened && !creep && verifierOk && deadStanding.length === 0) { + last.converged = true + log(`${l.key}: CONVERGED round ${round} (independently re-verified by ${reviews.length}/${LENSES.length} reviewers)`) + break + } + + const cause = classifyRejection(blockers, weakened, verifierOk, fix.status, verifierDied || deadStanding.length > 0) + TELEMETRY.rounds.push({ lane: l.key, round, cause, blockers: blockers.length, weakened, verifierOk }) + log(`${l.key}: round ${round} -> status=${fix.status}, ${blockers.length} blocker(s)${weakened ? ', ORACLE WEAKENED' : ''}${verifierOk ? '' : ', VERIFIER DISAGREED'} [cause: ${cause}]`) + if (round === MAX_ROUNDS) { + log(`${l.key}: ESCALATION — hit MAX_ROUNDS=${MAX_ROUNDS} unconverged; ${blockers.length} blocker(s) survive`) + break + } + // Carry lower-severity findings and the status so the next round always has something concrete + // even when no blocker was raised. + const lesser = reviews.flatMap((v) => (v.findings || []).filter((f) => !isBlocking(f))).slice(0, 6) + fb = { + round: round + 1, + blockers, + lesser, + weakened, + verifierSaid, + notDone: fix.status === 'done' ? null : fix.status, + } + } + return last +} + +phase('Build') + +let results +if (ARGS.trial) { + const first = LANES.find((l) => l.key === ARGS.trial) + if (!first) { + throw new Error(`lane-fanout: trial names lane "${ARGS.trial}", which is not in lanes: ${LANES.map((l) => l.key).join(', ')}`) + } + if (LANES.length < 2) throw new Error('lane-fanout: trial with fewer than two lanes serialises for nothing') + log(`TRIAL: running lane "${first.key}" alone before committing the other ${LANES.length - 1}`) + const trial = await runLane(first) + const converged = trial && trial.converged + if (!converged) { + // The whole point: a brief that is wrong is usually wrong the SAME way for every lane, so + // dispatching the rest would multiply one defect by N rather than discover N defects. + log(`TRIAL FAILED — not dispatching the remaining ${LANES.length - 1} lane(s).`) + log('The lock, the accept criteria or the review lenses did not hold against this tree. Fix the') + log('brief, not the lane, then re-run: the other lanes would have failed the same way.') + return { + headline: [ + `TRIAL LANE "${first.key}" DID NOT CONVERGE — fleet not dispatched`, + `${LANES.length - 1} lane(s) held back deliberately, not dropped`, + ], + trial, + heldBack: LANES.filter((l) => l.key !== first.key).map((l) => l.key), + } + } + log(`TRIAL CONVERGED — dispatching the remaining ${LANES.length - 1} lane(s)`) + const rest = await parallel(LANES.filter((l) => l.key !== first.key).map((l) => () => runLane(l))) + results = [trial, ...rest] +} else { + results = await parallel(LANES.map((l) => () => runLane(l))) +} + +const out = results.filter(Boolean).map((r) => ({ + lane: r.lane.key, + bead: r.lane.bead || null, + converged: r.converged, + rounds: r.rounds, + status: r.fix ? r.fix.status : 'agent_failed', + summary: r.fix ? r.fix.summary : null, + redBaseline: r.fix ? r.fix.redBaseline : null, + filesChanged: r.fix ? r.fix.filesChanged : [], + claimedVerification: r.fix ? r.fix.verification : null, + independentlyReproduced: r.verify ? r.verify.reproduced : null, + verifierObserved: r.verify ? r.verify.actualResults : null, + verifierDiscrepancies: r.verify ? r.verify.discrepancies : null, + falseGreenRisk: r.verify ? r.verify.falseGreenRisk : null, + contractBreaches: r.fix ? r.fix.contractBreaches : null, + followUps: r.fix ? r.fix.followUps : null, + verdicts: (r.reviews || []).map((v) => v.verdict), + oracleWeakened: (r.reviews || []).some((v) => v.oracleWeakened), + remainingBlockers: r.blockers || [], +})) + +const conv = out.filter((o) => o.converged).map((o) => o.lane) +const stuck = out.filter((o) => !o.converged).map((o) => `${o.lane}(${o.rounds}r,${o.remainingBlockers.length}b)`) +log(`CONVERGED: ${conv.join(', ') || 'none'} | UNCONVERGED: ${stuck.join(', ') || 'none'}`) +if (DEFECT_LEDGER.length) log(`defect classes seen this run: ${DEFECT_LEDGER.length}`) + +// --- terminal phase: LAND --------------------------------------------------- +// This phase exists because the harness did not have one, and its absence was a process defect +// rather than an oversight. A lane used to converge and stop, leaving its commits on a detached +// HEAD in a worktree nobody landed. Accumulation was therefore guaranteed by construction: measured +// 2026-08-08, ZERO open PRs against a main that twelve-plus worktrees sat above -- one at +99, one +// at +95, one at +61 -- which is hours of correct work nobody could review. +// +// The fix is NOT a consolidation phase. Consolidation-as-an-institution is the bureaucracy you get +// from refusing to fix the thing that generates the debt. The fix is that landing is part of the +// pipeline, because the cost curve is superlinear: one lane's diff onto an integration branch is a +// minutes-long rebase and STAYS minutes; twelve lanes at once is not twelve times worse, because +// conflicts multiply and the authority train rebinds on every fix. +// +// So: neither N PRs nor one heroic merge. ONE long-lived integration branch that every converged +// lane lands on immediately, kept continuously mergeable, PR'd on a cadence the human chooses. +// +// ONE WRITER. A single agent lands every lane in sequence. Landing is the one place where parallel +// agents would share a write target, and two writers on one branch is the failure that has already +// cost this program a round. +const LAND = ARGS.land !== false && conv.length > 0 +const INTEGRATION_BRANCH = ARGS.integrationBranch || 'integration/lane-fanout' +let landed = null +if (LAND) { + phase('Land') + // Built from the ORIGINAL result objects, never from `out`. `out` flattens `lane` to a STRING + // (`lane: r.lane.key`), so the prompt's `o.lane.key` / `o.lane.wt` / `o.fix` were all undefined + // and the integration owner was handed "undefined — worktree undefined", no files, no follow-ups. + // The landing phase ran and told the owner nothing; a phase that runs on garbage has not run. + const landable = results.filter(Boolean).filter((r) => r.converged) + landed = await agent( + `You are the integration owner. ${landable.length} lane(s) converged and must be LANDED NOW, in one sequence, by you alone. + +INTEGRATION BRANCH: ${INTEGRATION_BRANCH} BASE: ${TIP} + +LANES TO LAND, in this order: +${landable.map((r, i) => `${i + 1}. ${r.lane.key} — worktree ${r.lane.wt} + REVIEWED HEAD: ${(r.verify && r.verify.headSha) || 'NOT CAPTURED — refuse this lane and report it'} + files: ${(r.fix && r.fix.filesChanged ? r.fix.filesChanged : []).join(', ') || '(see the worktree)'} + leased edits it reported instead of making: ${(r.fix && r.fix.followUps) || 'none'}`).join('\n')} + +WHY THIS RUNS AT ALL: work that converges but does not land accumulates, and the cost of landing it +grows faster than linearly. Landing one lane now is cheap; landing twelve later is not. + +BEFORE YOU TOUCH ANYTHING: + For each lane's worktree run 'git status --porcelain', 'git rev-parse HEAD' and 'git log --oneline -3'. + A worktree with uncommitted changes has a LIVE WRITER -- do not land it, report it as skipped and + say so. A finished workflow id is NOT evidence that nothing is writing; the working tree is. + + *** LAND ONLY THE REVIEWED HEAD. If 'git rev-parse HEAD' does not equal the REVIEWED HEAD listed + for that lane above, do NOT land it: skip it and report both SHAs. A clean worktree is NOT a + binding -- a commit added after the reviewers finished is clean too, and cherry-picking it + would land unreviewed code under a reviewed lane's name. The same applies to a lane whose + reviewed head was NOT CAPTURED: refuse it. Never resolve a mismatch by re-reviewing it + yourself; you are the lander, and a lane whose head moved goes back through the harness. *** + +HOW TO LAND: + Create or fast-forward ${INTEGRATION_BRANCH} from ${TIP} in the PRIMARY repo. For each lane in + order, apply that lane's commits (cherry-pick its range, or format-patch/am). After EACH lane: + build and test the packages that lane touched, and stop at the first failure rather than piling + the next lane on top of a broken tree. + PERMITTED: branch create, checkout of the integration branch, cherry-pick, am, add, commit, and + read-only git everywhere. + FORBIDDEN: push, force-push, PR creation, reset --hard, clean, stash, rebase of anything already + landed, and ANY write inside a lane worktree. Pushing and PR-opening are the human's call; your + job is to make the branch exist, be correct, and be continuously mergeable. + NEVER use a merge that would create an unsigned two-parent head -- that breaks the authority train + and the error will blame the signature. + +CONFLICTS: if a lane conflicts, resolve ONLY if the resolution is mechanical and obvious; otherwise +stop, leave the branch at the last good lane, and report the conflicting hunks exactly. A wrong +conflict resolution is far more expensive than a skipped lane. + +REPORT: the branch and its head SHA, which lanes landed and which were skipped and why, the exact +verification you ran after each lane with its real output, every leased edit still outstanding across +all lanes (deduplicated -- these are what the human must apply before the PR is green), and the +exact 'gh pr create' command you did NOT run.`, + { label: 'land', phase: 'Land', schema: { + type: 'object', + required: ['branch', 'headSha', 'landedLanes', 'skippedLanes', 'verification', 'outstandingLeasedEdits'], + properties: { + branch: { type: 'string' }, + headSha: { type: 'string' }, + landedLanes: { type: 'array', items: { type: 'string' } }, + skippedLanes: { type: 'array', items: { type: 'string' }, description: 'lane key + the reason, e.g. "p4-tai: live writer, 4 files dirty"' }, + verification: { type: 'string', description: 'exact commands and real output, per lane' }, + conflicts: { type: 'string' }, + outstandingLeasedEdits: { type: 'string', description: 'deduplicated across lanes; what the human must apply before a PR can be green' }, + prCommand: { type: 'string', description: 'the gh pr create command, NOT run' }, + }, + } }, + ) + if (landed) { + log(`LANDED on ${landed.branch} @ ${landed.headSha}: ${(landed.landedLanes || []).join(', ') || 'none'}`) + if ((landed.skippedLanes || []).length) log(`NOT LANDED: ${landed.skippedLanes.join(' | ')}`) + } else { + log('LAND phase returned nothing — converged work is still sitting in worktrees; land it by hand') + } +} else if (conv.length === 0) { + log('nothing converged, so nothing to land') +} else { + log(`land disabled by args; ${conv.length} converged lane(s) left in their worktrees — this is how the +99 backlog happened`) +} + +// Where did the rounds go? Rounds are the scarce resource, so this is the number that should drive +// the next improvement. A run dominated by scope-brief-defect means fix the BRIEF; by code-defect +// means the lanes are genuinely hard; by owner-lease means the lease boundary is drawn wrong. +const byCause = {} +for (const r of TELEMETRY.rounds) byCause[r.cause] = (byCause[r.cause] || 0) + 1 +const wasted = (byCause['scope-brief-defect'] || 0) + (byCause['owner-lease'] || 0) +const buildRounds = out.reduce((n, o) => n + (o.rounds || 0), 0) + +// SERIAL DEPTH is what wall-clock actually tracks — measured across this harness's runs, per-step +// latency sits at 9-14 min and total time follows depth almost exactly while ignoring width: +// 12 agents at depth 6 took 74 min; 36 agents at depth 6 took 63 min. Three times the width, no +// slower. So adding reviewers is nearly free and adding a serial step is not. +// This harness is already flat WITHIN a round -- build, then one parallel block of reviewers plus +// the verifier -- so depth is 2 per round and nothing here can be unstacked further. All remaining +// depth is ROUNDS, which makes brief quality the only real lever on wall-clock. +const maxRounds = out.reduce((n, o) => Math.max(n, o.rounds || 0), 0) +const depth = maxRounds * 2 +log(`telemetry: ${buildRounds} build round(s); serial depth ~${depth} (${maxRounds} round(s) x 2: build then one parallel check block)`) +log(`telemetry: rejection causes ${JSON.stringify(byCause)}`) +if (wasted) log(`telemetry: ${wasted}/${TELEMETRY.rounds.length} rejected round(s) were BRIEF or LEASE defects, not code — fix those in the brief, not the lane`) + +// Headline FIRST and compact, because the result file gets truncated on long runs and the journal +// stores content-hash labels rather than the lane keys passed in — so a truncated tail leaves +// verdicts unattributable to lanes. One line per lane, before anything verbose. +const headline = out.map((o) => + `${o.lane}: ${o.converged ? 'CONVERGED' : 'UNCONVERGED'} r${o.rounds} ${o.status} blockers=${(o.remainingBlockers || []).length} weakened=${o.oracleWeakened} verified=${o.independentlyReproduced}`) +log(`HEADLINE | ${headline.join(' | ')}`) + +return { + headline, + lanes: out, + // The landing result must be RETURNED, not merely logged. Caught by the offline preflight on its + // first run: the Land phase executed and its branch and head SHA went nowhere, so the caller could + // not tell where the work went -- a phase that runs and reports nothing is barely better than the + // missing phase it replaced. + landed, + defectClasses: DEFECT_LEDGER.map((d) => ({ lane: d.laneKey, claim: d.claim })), + telemetry: { + buildRounds, + serialDepth: depth, + // Report what ACTUALLY reported, not what was dispatched. The previous expression was + // ((LENSES.length + 1) * buildRounds) / buildRounds -- algebraically just LENSES.length + 1, + // a constant that could never observe a dead agent. Telemetry that cannot be wrong is not + // telemetry, and this harness lost checkers to session limits in most of its measured runs. + checkersDispatched: TELEMETRY.checkers.reduce((n, c) => n + c.dispatched, 0), + checkersReturned: TELEMETRY.checkers.reduce((n, c) => n + c.returned, 0), + checkersPerBuild: TELEMETRY.checkers.length + ? +(TELEMETRY.checkers.reduce((n, c) => n + c.returned, 0) / TELEMETRY.checkers.length).toFixed(2) + : 0, + deadCheckers: TELEMETRY.checkers.flatMap((c) => c.deadLenses), + rejectionCauses: byCause, + briefOrLeaseRounds: wasted, + rounds: TELEMETRY.rounds, + }, +} diff --git a/scripts/console/workflows/lane-fanout.test.mjs b/scripts/console/workflows/lane-fanout.test.mjs new file mode 100644 index 000000000..2913dcbf3 --- /dev/null +++ b/scripts/console/workflows/lane-fanout.test.mjs @@ -0,0 +1,1358 @@ +// Offline preflight for lane-fanout.js. Run it BEFORE every dispatch: +// +// node scripts/console/workflows/lane-fanout.test.mjs +// +// It exists because two classes of defect are invisible to reading, and both shipped here: +// +// 1. SYNTAX. An unescaped backtick inside a prompt template literal ends the literal. It +// silently truncated BASE_LOCK once; the run looked normal and the lock was half gone. +// `node --check` cannot see this — it rejects the top-level await and tells you nothing. +// 2. WIRING. A rule can be written, documented, believed, and reachable by nothing: +// - `LENSES = ARGS.lenses || [...]` meant the standing lenses NEVER ran, because every +// invocation passed `lenses`. Oracle integrity, the most common rejection cause in this +// program, had never once been reviewed for. +// - `verifierOk` matched the literal string "none" in the verifier's prose, so a verifier +// writing "four, all minor; none contradicts the verdict" failed the check. Convergence +// was unreachable whenever the verifier ran, and the harness manufactured rebuild rounds. +// - convergence keyed on `severity === 'blocker'`, so a run reported CONVERGED while both +// reviewers held six separately PROVEN fail-opens, every one filed "major". +// +// The harness is driven with STUB agents, so this proves the ENFORCEMENT LOGIC, not any agent's +// judgement. It is offline, free, and takes under a second. + +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const HERE = path.dirname(fileURLToPath(import.meta.url)) +// The RED baseline for any change to these dispatchers is THIS file run against the PRE-FIX +// sources (`git show :scripts/console/workflows/.js` into a scratch dir, then point this at it). +// Without the override the preflight can only ever measure the tree it ships with, so "it would +// have gone red on the old code" is a claim nobody can re-run. CI passes nothing and gets HERE. +const SRCDIR = process.env.LANE_FANOUT_SRCDIR || HERE +const SRC = fs.readFileSync(path.join(SRCDIR, 'lane-fanout.js'), 'utf8') + +// Compile exactly as the harness evaluates it: an async function body with these globals, so +// top-level await and return are legal. This is the only honest syntax check. +const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor +let run +try { + run = new AsyncFunction( + 'args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', + SRC.replace(/^export const meta = /m, 'const meta = '), + ) +} catch (e) { + console.error('FAIL compile —', e.message) + console.error(' An unescaped ` inside a prompt template literal is the usual cause.') + process.exit(1) +} +console.log('PASS compile — body parses as the harness evaluates it') + +let failures = 0 // eslint-disable-line prefer-const +const check = (name, ok, detail) => { + // JSON.stringify(undefined) is undefined, not "undefined", so a FAILING assertion called without a + // detail threw TypeError here and killed the whole preflight mid-run — losing every assertion after + // it AND the failure count, so the harness reported nothing rather than a red. The reporter's own + // failure path was the one path no assertion exercised. It is exercised at the bottom of this file. + const shown = detail === undefined ? '(no detail)' : String(JSON.stringify(detail)).slice(0, 500) + console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${ok ? '' : ' :: ' + shown}`) + if (!ok) failures++ +} + +// Sequential is fine for a logic test; parallel() only needs to run every thunk and collect. +const parallel = async (thunks) => { + const out = [] + for (const t of thunks) { try { out.push(await t()) } catch { out.push(null) } } + return out +} +const pipeline = async (items, ...stages) => { + const out = [] + for (const [i, it] of items.entries()) { + let v = it + for (const s of stages) v = await s(v, it, i) + out.push(v) + } + return out +} + +const BUILD = (over = {}) => ({ + status: 'done', summary: 's', filesChanged: ['x.rs'], redBaseline: 'RED', verification: 'ok', + contractBreaches: 'none', enforcementPlacement: 'n/a - adds no enforcement', + // Default must name a real command that VERIFY re-runs. `commands: []` used to converge + // vacuously against any verifier command — that is the fail-open under test below. + peripheralsUpdated: 'n/a - nothing described this behaviour', followUps: '', + commands: ['cargo test -p x'], ...over, +}) +const FINDING = (over = {}) => ({ + severity: 'major', claim: 'c', failureScenario: 'f', location: 'l', + provenByExecution: false, ownerLease: false, ...over, +}) +const REVIEW = (over = {}) => ({ verdict: 'accept', findings: [], oracleWeakened: false, scopeCreep: false, ...over }) +const VERIFY = (over = {}) => ({ + reproduced: true, actualResults: 'a', discrepancies: 'none', contradictsClaim: false, + falseGreenRisk: 'none', commandsRun: ['cargo test -p x'], oracleIntact: true, headSha: 'cafe1234', ...over, +}) + +// Records every label the harness actually dispatched, which is how we prove a rule is REACHABLE +// rather than merely written. +const mkAgent = (opts = {}) => { + const seen = [] + const fn = async (prompt, o = {}) => { + const label = o.label || '' + seen.push({ label, prompt }) + if (label.startsWith('build:')) return opts.build ? opts.build(seen) : BUILD() + if (label.startsWith('review:')) return opts.review ? opts.review(prompt, seen) : REVIEW() + if (label.startsWith('verify:')) return opts.verify ? opts.verify() : VERIFY() + if (label === 'land') return opts.land ? opts.land() : { branch: 'integration/x', headSha: 'deadbee', landedLanes: ['a'], skippedLanes: [], verification: 'v', outstandingLeasedEdits: 'none', prCommand: 'gh pr create ...' } + return 'REPORT' + } + fn.seen = seen + return fn +} + +const LANE = (over = {}) => ({ key: 'a', bead: 'b', wt: '/w', owned: 'x/**', brief: 't', accept: 'a', ...over }) +const ARGS = (over = {}) => ({ tip: 'abc1234', lanes: [LANE()], maxRounds: 1, land: false, ...over }) + +const go = (args, agent = mkAgent(), logs = []) => + run(args, agent, parallel, pipeline, (m) => logs.push(m), () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => {}) + +const threw = async (args) => { + try { await go(args); return null } catch (e) { return e.message } +} + +// --- 1. An option the harness does not read must ABORT, never be silently dropped. ---------- +{ + const m = await threw(ARGS({ prose_hardening: false })) + check('unknown top-level arg aborts', !!m && /prose_hardening/.test(m), m) + + const m2 = await threw(ARGS({ lanes: [LANE({ scopes: ['x/'] })] })) + check('unknown per-lane key aborts', !!m2 && /scopes/.test(m2), m2) + + const m3 = await threw(ARGS({ lens: ['typo'] })) + check('a typo in `lenses` aborts rather than silently using defaults', !!m3 && /lens\b/.test(m3), m3) + + const ok = await go(ARGS()) + check('every documented arg is accepted', !!ok && Array.isArray(ok.headline), ok && Object.keys(ok)) +} + +// --- 2. Standing lenses must survive custom lenses (the dead-default defect). --------------- +{ + const agent = mkAgent() + await go(ARGS({ lenses: ['CUSTOM ONE', 'CUSTOM TWO'] }), agent) + const reviews = agent.seen.filter((s) => s.label.startsWith('review:')) + const hasOracle = reviews.some((r) => /ORACLE INTEGRITY/.test(r.prompt)) + const hasPlacement = reviews.some((r) => /ENFORCEMENT PLACEMENT/.test(r.prompt)) + const hasDrift = reviews.some((r) => /PERIPHERAL DRIFT/.test(r.prompt)) + const hasCustom = reviews.some((r) => /CUSTOM ONE/.test(r.prompt)) + const hasMaintainability = reviews.some((r) => /COST OF CARRY/.test(r.prompt)) + // Sized against the standing set rather than pinned to a literal. The count was hard-coded to 5, + // so ADDING a standing lens -- the intended way to strengthen review -- read as a regression and + // blocked dispatch. An assertion that a legitimate improvement breaks is an assertion that will + // eventually be "fixed" by deleting the improvement. Two lanes have already learned this shape: + // a rule stated as a list of members instead of a relationship over the set. + const standing = SRC.match(/const STANDING_LENSES = \[([\s\S]*?)\n\]/) + const standingCount = standing ? (standing[1].match(/^\s{2}'/gm) || []).length : -1 + check('custom lenses ADD to standing lenses, never replace them', + standingCount > 0 && reviews.length === standingCount + 2 && + hasOracle && hasPlacement && hasDrift && hasMaintainability && hasCustom, + { count: reviews.length, standingCount, hasOracle, hasPlacement, hasDrift, hasMaintainability, hasCustom }) +} + +// --- 3. Convergence keys on PROOF, not on the severity label. ------------------------------- +{ + const provenMajor = mkAgent({ review: () => REVIEW({ verdict: 'accept_with_findings', findings: [FINDING({ severity: 'major', provenByExecution: true })] }) }) + const r1 = await go(ARGS(), provenMajor) + check('a PROVEN major blocks convergence', r1.lanes[0].converged === false, r1.headline) + + const arguedMajor = mkAgent({ review: () => REVIEW({ verdict: 'accept_with_findings', findings: [FINDING({ severity: 'major', provenByExecution: false })] }) }) + const r2 = await go(ARGS(), arguedMajor) + check('an ARGUED major does not block convergence', r2.lanes[0].converged === true, r2.headline) + + const leasedBlocker = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker', provenByExecution: true, ownerLease: true })] }) }) + const r3 = await go(ARGS(), leasedBlocker) + check('an owner lease releases at ANY severity', r3.lanes[0].converged === true, r3.headline) + + const realBlocker = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker' })] }) }) + const r4 = await go(ARGS(), realBlocker) + check('a real blocker blocks', r4.lanes[0].converged === false, r4.headline) +} + +// --- 4. The oracle may never be weakened, whatever the findings say. ------------------------ +{ + const weakened = mkAgent({ review: () => REVIEW({ oracleWeakened: true }) }) + const r = await go(ARGS(), weakened) + check('oracleWeakened blocks convergence with zero findings', r.lanes[0].converged === false, r.headline) +} + +// --- 5. The verifier decides via a BOOLEAN, not a regex over its prose. --------------------- +{ + const conscientious = mkAgent({ verify: () => VERIFY({ discrepancies: 'four, all minor; none contradicts the verdict', contradictsClaim: false }) }) + const r = await go(ARGS(), conscientious) + check('a wordy but non-contradicting verifier still converges', r.lanes[0].converged === true, r.headline) + + const contradicts = mkAgent({ verify: () => VERIFY({ contradictsClaim: true }) }) + const r2 = await go(ARGS(), contradicts) + check('contradictsClaim=true blocks convergence', r2.lanes[0].converged === false, r2.headline) + + const notReproduced = mkAgent({ verify: () => VERIFY({ reproduced: false }) }) + const r3 = await go(ARGS(), notReproduced) + check('reproduced=false blocks convergence', r3.lanes[0].converged === false, r3.headline) +} + +// --- 6. Nothing green to falsify => no verifier. Rounds are the scarce resource. ------------ +{ + const partial = mkAgent({ build: () => BUILD({ status: 'partial' }) }) + await go(ARGS(), partial) + check('a non-done build skips the verifier', partial.seen.filter((s) => s.label.startsWith('verify:')).length === 0, + partial.seen.map((s) => s.label)) + + const done = mkAgent() + await go(ARGS(), done) + check('a done build runs the verifier', done.seen.filter((s) => s.label.startsWith('verify:')).length === 1, + done.seen.map((s) => s.label)) +} + +// --- 7. Agent death is routine at this scale; retry once, then abandon. --------------------- +{ + let n = 0 + const flaky = mkAgent({ build: () => { n++; return n === 1 ? null : BUILD() } }) + const r = await go(ARGS(), flaky) + check('a dead implementer is retried once and the round proceeds', n === 2 && r.lanes[0].status === 'done', { n, r: r.headline }) + + const dead = mkAgent({ build: () => null }) + const r2 = await go(ARGS(), dead) + check('two deaths abandon the lane without throwing', r2.lanes[0].converged === false, r2.headline) +} + +// --- 8. Rejected rounds must feed the next build, or findings are collected and discarded. -- +{ + const seenPrompts = [] + const agent = mkAgent({ + build: (seen) => { seenPrompts.push(seen[seen.length - 1].prompt); return BUILD() }, + review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker', claim: 'THE-DISTINCTIVE-BLOCKER' })] }), + }) + await go(ARGS({ maxRounds: 2 }), agent) + check('round 2 receives round 1 blockers as feedback', + seenPrompts.length === 2 && /THE-DISTINCTIVE-BLOCKER/.test(seenPrompts[1]), seenPrompts.length) +} + +// --- 9. Reviewers diff the TIP, never HEAD~1 (which once diffed the base commit). ----------- +{ + const agent = mkAgent() + await go(ARGS(), agent) + const reviews = agent.seen.filter((s) => s.label.startsWith('review:')) + check('reviewers are told to diff the tip, and HEAD~1 appears only as a warning', + reviews.every((r) => r.prompt.includes('abc1234')), reviews.length) +} + +// --- 10. Landing is part of the pipeline. Its absence is what produced a +99 backlog. ------- +{ + const agent = mkAgent() + // Distinctive key and worktree: 'a' and '/w' occur in English prose, so they cannot tell a + // populated prompt from a broken one. + const r = await go(ARGS({ land: true, lanes: [LANE({ key: 'LANE-KEY-Z', wt: '/wt/lane-z' })] }), agent) + check('a converged lane LANDS by default', agent.seen.some((s) => s.label === 'land') && !!r.landed, r.headline) + + // THE PROMPT MUST CONTAIN THE LANE, NOT undefined. Asserting only that landing was INVOKED is + // what let this ship: the prompt read o.lane.key / o.lane.wt / o.fix off the flattened summary, + // whose `lane` is a STRING, so the integration owner was told "undefined — worktree undefined" + // with no files and no follow-ups. A phase that runs on garbage is not a phase that runs. + const landPrompt = (agent.seen.find((s) => s.label === 'land') || {}).prompt || '' + check('the landing prompt names the real lane key and its worktree', + landPrompt.includes('LANE-KEY-Z') && landPrompt.includes('/wt/lane-z'), landPrompt.slice(0, 400)) + check('the landing prompt carries the lane\'s changed files', landPrompt.includes('x.rs'), landPrompt.slice(0, 400)) + check('the landing prompt interpolates no undefined field', !/undefined/.test(landPrompt), + (landPrompt.split('\n').filter((x) => /undefined/.test(x)) || []).slice(0, 4)) + + // LANDING MUST BE BOUND TO THE REVIEWED HEAD. Clean-worktree + recent-log is not a binding: a + // commit added after the reviewers finished is clean and would be cherry-picked as reviewed. + check('the landing prompt carries the head SHA captured at review time', + landPrompt.includes('cafe1234'), landPrompt.slice(0, 400)) + check('and orders a refusal when the worktree no longer matches it', + /rev-parse HEAD/.test(landPrompt) && /do NOT land|REFUSE/.test(landPrompt), landPrompt.slice(0, 400)) + + const noConverge = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker' })] }) }) + await go(ARGS({ land: true }), noConverge) + check('nothing converged => nothing lands', !noConverge.seen.some((s) => s.label === 'land'), + noConverge.seen.map((s) => s.label)) + + const off = mkAgent() + await go(ARGS({ land: false }), off) + check('land:false is honoured (and warns)', !off.seen.some((s) => s.label === 'land'), off.seen.map((s) => s.label)) +} + +// --- 10b. A DEAD REVIEWER IS NOT AN ABSENT FINDING. ----------------------------------------- +// Measured: session-limit kills took 4 of 7 agents from one run and 3 of 3 from another, and +// `.filter(Boolean)` made them vanish — a lane could converge on one surviving reviewer in silence. +{ + const standingDied = mkAgent({ review: (prompt) => (/ORACLE INTEGRITY/.test(prompt) ? null : REVIEW()) }) + const logs = [] + const r = await go(ARGS(), standingDied, logs) + check('a dead STANDING lens blocks convergence', r.lanes[0].converged === false, r.headline) + check('and says which one died', logs.some((m) => /CANNOT CONVERGE.*ORACLE INTEGRITY/.test(m)), + logs.filter((m) => /CONVERGE|DIED/.test(m))) + + const customDied = mkAgent({ review: (prompt) => (/CUSTOM A/.test(prompt) ? null : REVIEW()) }) + const logs2 = [] + const r2 = await go(ARGS({ lenses: ['CUSTOM A', 'CUSTOM B'] }), customDied, logs2) + check('a dead CUSTOM lens is tolerated (a rebuild round costs more than it saves)', + r2.lanes[0].converged === true, r2.headline) + check('but the death is still logged', logs2.some((m) => /DIED/.test(m)), + logs2.filter((m) => /DIED|reviewers returned/.test(m))) +} + +// --- 10d. A DEAD VERIFIER IS NOT A PASSED VERIFICATION. ------------------------------------- +// The sibling of 10b, and worse: `!verify` read a died verifier as "no verifier was needed", so the +// lane converged and the log asserted "independently re-verified" having verified NOTHING. A +// standing lens is one voice among several; the verifier is the ONLY thing that reproduces a +// claimed green, so a dead one has no substitute and cannot be tolerated the way a custom lens is. +{ + const verifierDied = mkAgent({ verify: () => null }) + const logs = [] + const r = await go(ARGS(), verifierDied, logs) + check('a dead VERIFIER blocks convergence', r.lanes[0].converged === false, r.headline) + check('and never claims the lane was independently re-verified', + !logs.some((m) => /independently re-verified/.test(m)), logs.filter((m) => /re-verified|VERIFIER/.test(m))) + check('and says the verification never happened', + logs.some((m) => /VERIFIER DIED/.test(m)), logs.filter((m) => /VERIFIER/.test(m))) + + // The deliberate exception must survive: a lane claiming nothing has nothing to falsify, so the + // verifier is SKIPPED, and a skip is not a death. + const partial = mkAgent({ build: () => BUILD({ status: 'partial' }) }) + const logs2 = [] + await go(ARGS(), partial, logs2) + check('a build claiming nothing is skipped, not accused of a dead verifier', + logs2.some((m) => /verifier skipped/.test(m)) && !logs2.some((m) => /VERIFIER DIED/.test(m)), logs2) +} + +// --- 10c. Telemetry must be able to be WRONG, i.e. must observe reality. -------------------- +// The old expression was ((LENSES.length + 1) * buildRounds) / buildRounds — algebraically a +// constant. It could not detect a dead agent no matter how many died. +{ + const oneDied = mkAgent({ review: (prompt) => (/BLAST RADIUS/.test(prompt) ? null : REVIEW()) }) + const r = await go(ARGS(), oneDied) + const t = r.telemetry + check('telemetry reports dispatched > returned when a checker dies', + t.checkersDispatched > t.checkersReturned, { d: t.checkersDispatched, r: t.checkersReturned }) + check('telemetry names the dead checker', (t.deadCheckers || []).length === 1, t.deadCheckers) + + const allLived = mkAgent() + const r2 = await go(ARGS(), allLived) + check('telemetry reports equality when none die', + r2.telemetry.checkersDispatched === r2.telemetry.checkersReturned, + { d: r2.telemetry.checkersDispatched, r: r2.telemetry.checkersReturned }) +} + +// --- 10d. Review-round findings: three defects the reviewer caught that the preflight did not. --- +{ + // Two lanes in ONE worktree passed validation and were dispatched concurrently, each told by the + // lock to build on whatever it found — so the second treats the first's half-finished edits as + // its baseline. Silent by design. + const m = await threw(ARGS({ lanes: [LANE({ key: 'a', wt: '/same' }), LANE({ key: 'b', wt: '/same' })] })) + check('two lanes sharing a worktree abort', !!m && /both declare worktree/.test(m), m) + + const m2 = await threw(ARGS({ lanes: [LANE({ key: 'dup' }), LANE({ key: 'dup', wt: '/other' })] })) + check('duplicate lane keys abort', !!m2 && /duplicate lane key/.test(m2), m2) + + // Documented and accepted must be the same set: the args comment advertised blockedTargets while + // the allowlist rejected it, so a caller following the docs aborted. + const ok = await go(ARGS({ lanes: [LANE({ blockedTargets: ['x'] })] })) + check('a documented per-lane key is accepted', !!ok && Array.isArray(ok.headline), ok && Object.keys(ok || {})) + + // scopeCreep is a first-class verdict, not a hint. A reviewer setting it without ALSO restating it + // as a blocking finding was ignored, so a lane could edit an unowned root and still converge. + const creeper = mkAgent({ review: () => REVIEW({ verdict: 'accept', scopeCreep: true }) }) + const r = await go(ARGS(), creeper) + check('scopeCreep alone blocks convergence', r.lanes[0].converged === false, r.headline) + + const clean = await go(ARGS(), mkAgent()) + check('and does not block when unset', clean.lanes[0].converged === true, clean.headline) +} + +// --- 11. The required-field trio must stay required, or the clause is skimmable again. ------ +{ + const req = (SRC.match(/required: \[[^\]]*'enforcementPlacement'[^\]]*\]/) || [''])[0] + check('enforcementPlacement is a REQUIRED build field', /enforcementPlacement/.test(req), req.slice(0, 200)) + check('peripheralsUpdated is a REQUIRED build field', /peripheralsUpdated/.test(req), req.slice(0, 200)) + check('redBaseline is a REQUIRED build field', /redBaseline/.test(req), req.slice(0, 200)) + const rf = (SRC.match(/required: \[[^\]]*'provenByExecution'[^\]]*\]/) || [''])[0] + check('provenByExecution and ownerLease are REQUIRED per finding', + /provenByExecution/.test(rf) && /ownerLease/.test(rf), rf.slice(0, 200)) +} + +// --- 12. The lock must not be silently truncated by a nested backtick. ---------------------- +{ + const lock = SRC.slice(SRC.indexOf('const BASE_LOCK = `') + 19, SRC.indexOf('\n`\n\nconst LOCK')) + check('BASE_LOCK contains no nested backtick', (lock.match(/`/g) || []).length === 0, (lock.match(/`/g) || []).length) + for (const clause of ['NEVER WEAKEN THE ORACLE', 'CONTRACT TESTS ARE PART OF THE CHANGE', + 'AN ENFORCEMENT MUST BE ABLE TO SEE ITS SUBJECT', 'PERIPHERALS ARE PART OF THE CHANGE', + 'THE THIRD SPELLING MEANS THE MECHANISM IS WRONG', 'NEVER pass --workflow-only']) { + check(`lock clause survives: ${clause}`, lock.includes(clause)) + } +} + +// --- 13. The caller that dispatches this harness must not bake in one machine's paths. ------ +// program-tick.js chains into lane-fanout and used to send every selected lane to a hard-coded +// /Users//... worktree, ignoring both the workspace it was given and the worktree inventory +// it had just collected. On CI, on Linux, on any other machine, every implementer was pointed at a +// path that does not exist — and nothing here could see it, because the preflight only ever +// compiled lane-fanout.js. A dispatcher is part of the harness. +{ + const TICK = fs.readFileSync(path.join(SRCDIR, 'program-tick.js'), 'utf8') + try { + new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', + TICK.replace(/^export const meta = /m, 'const meta = ')) + check('program-tick compiles as the harness evaluates it', true) + } catch (e) { + check('program-tick compiles as the harness evaluates it', false, e.message) + } + const homePaths = TICK.split('\n').filter((l) => /(\/Users\/|\/home\/)[A-Za-z0-9_.-]+\//.test(l)) + check('program-tick hard-codes no machine-specific worktree path', homePaths.length === 0, homePaths) +} + +// The unknown-option guard was written for lane-fanout, repeated in backlog-audit, and skipped in +// program-tick. A rule present in two of three sibling harnesses is not a rule, it is a coincidence, +// so the preflight now asserts it across ALL of them rather than for each file someone remembers. +{ + // ENUMERATE THE DIRECTORY, DO NOT LIST IT. This was a hardcoded array of the harnesses someone + // remembered, which is how program-tick went without the guard while its two siblings had it, and + // how review-gate.js and slice.js sat in this directory referenced-but-never-compiled. A new + // harness must be covered by existing here, not by being added to a list a future author edits. + const dispatchers = fs.readdirSync(HERE) + .filter((f) => f.endsWith('.js')) + .map((f) => f.replace(/\.js$/, '')) + .sort() + check('every harness in the directory is swept, not a hardcoded subset', + dispatchers.length >= 4 && dispatchers.includes('scout'), dispatchers) + for (const name of dispatchers) { + const src = fs.readFileSync(path.join(SRCDIR, `${name}.js`), 'utf8') + .replace(/^export const meta = /m, 'const meta = ') + const fn = new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', src) + const stub = async () => ({}) + // Every required field is supplied; ONLY the bogus key should be able to fail this. + const base = { tip: 'a'.repeat(40), lanes: [LANE()], candidateWt: '/w', candidateTip: 'b'.repeat(40), + base: 'main', repo: '/r', ghRepo: 'o/n', maxLanes: 2 } + let threw = null + try { + await fn({ ...base, thisOptionDoesNotExist: true }, stub, async (t) => Promise.all(t.map((f) => f())), + async (i) => i, () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, stub) + } catch (e) { threw = e.message } + check(`${name} refuses an option it does not read`, + !!threw && /unknown option/i.test(threw), threw) + } +} + +// A batch size of -1 produces no batches and 1.5 produces overlapping slices; both exit cleanly. +{ + const src = fs.readFileSync(path.join(SRCDIR, 'backlog-audit.js'), 'utf8') + .replace(/^export const meta = /m, 'const meta = ') + const fn = new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', src) + for (const bad of [-1, 0, 1.5, 'eight']) { + let threw = null + try { + await fn({ repo: '/r', ghRepo: 'o/n', issueBatch: bad }, async () => ({}), + async (t) => Promise.all(t.map((f) => f())), async (i) => i, () => {}, () => {}, + { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) + } catch (e) { threw = e.message } + check(`backlog-audit refuses issueBatch=${JSON.stringify(bad)}`, + !!threw && /issueBatch must be a positive integer/.test(threw), threw) + } +} + +// The reconciler was handed `JSON.stringify(findings).slice(0, 24000)`. With ~32 read lanes the cap +// binds routinely, so the single writer filed beads for a prefix of the audit and reported success. +{ + const src = fs.readFileSync(path.join(SRCDIR, 'backlog-audit.js'), 'utf8') + check('backlog-audit no longer truncates the findings payload blindly', + !/JSON\.stringify\(findingsAll\)\.slice\(/.test(src)) + const body = src.match(/function renderFindings\(all\) \{[\s\S]*?\n\}/) + check('backlog-audit exposes renderFindings to the preflight', !!body) + if (body) { + const renderFindings = new Function(`${body[0]}; return renderFindings`)() + const many = Array.from({ length: 400 }, (_, i) => ({ + title: `finding ${i} ${'x'.repeat(200)}`, severity: i ? 'minor' : 'blocker', + provenByExecution: i === 399, evidence: 'f.rs:1', + })) + const out = renderFindings(many) + check('an over-budget findings set says how many it dropped', /DID NOT FIT/.test(out), out.slice(-200)) + check('the proven finding survives truncation regardless of its position', + out.includes('finding 399'), 'the last-listed proven finding was cut') + const few = [{ title: 'only one', severity: 'blocker', provenByExecution: true }] + check('a set that fits carries no truncation notice', !/DID NOT FIT/.test(renderFindings(few))) + } +} + +// The reporter must survive its own failure path. Proven by capturing stdout rather than by reading +// it: a detail-less FAIL used to throw TypeError and abort the run, which is worse than a red because +// it looks like a crash in the harness instead of a defect in the code under test. +{ + const realLog = console.log + const lines = [] + console.log = (l) => lines.push(l) + let crashed = null + const before = failures + try { check('self-test: a failing assertion carries no detail', false) } catch (e) { crashed = e.message } + console.log = realLog + failures = before // this deliberate FAIL must not colour the real result + check('a detail-less failure reports instead of crashing the preflight', + crashed === null && lines.length === 1 && lines[0].startsWith('FAIL'), crashed || lines) +} + +// A doc edit that fails CI on a stale generated checksum is the cheapest possible review round: the +// fix is one command, and the lane that made the edit could have run it. This assertion exists +// because a correct change was turned red by exactly that, twice. +{ + const need = ['REGENERATE, THEN ASK GIT', 'git status --porcelain', 'POSTFLIGHT', 'tools/buck/preflight.sh', 'tools/lanes/pgtest.sh'] + for (const fragment of need) { + check(`the lock names a TOTAL generated-face check, not a list: ${fragment}`, SRC.includes(fragment)) + } +} + +// --- 14. OVERLAPPING OWNED ROOTS: the duplicate-worktree collision, deferred to LAND. -------- +// Separate worktrees mean two lanes cannot corrupt each other's files, so every reviewer and every +// verifier passes. The collision arrives on the integration branch afterwards, as two independent +// rewrites of the same files from the same base — the most expensive possible moment. Refuse it +// where the duplicate `wt` is refused: at dispatch. +{ + const nested = await threw(ARGS({ lanes: [ + LANE({ key: 'outer', wt: '/w1', owned: 'backend/crates/x/**' }), + LANE({ key: 'inner', wt: '/w2', owned: 'backend/crates/x/sub/**' }), + ] })) + check('a lane owning a subtree of another lane aborts', + !!nested && /overlapping owned root/i.test(nested) && /outer/.test(nested) && /inner/.test(nested), nested) + + const same = await threw(ARGS({ lanes: [ + LANE({ key: 'a1', wt: '/w1', owned: 'docs/x/**' }), + LANE({ key: 'a2', wt: '/w2', owned: 'docs/x/**' }), + ] })) + check('two lanes owning the SAME root abort', !!same && /overlapping owned root/i.test(same), same) + + // An owned root is routinely a LIST. One colliding member is enough, and checking only the first + // would be the same defect with a smaller blast radius. + const multi = await threw(ARGS({ lanes: [ + LANE({ key: 'm1', wt: '/w1', owned: 'a/**, b/**' }), + LANE({ key: 'm2', wt: '/w2', owned: 'c/**\nb/deep/**' }), + ] })) + check('one colliding path inside a multi-path owned root is enough', !!multi && /overlapping owned root/i.test(multi), multi) + + // A guard that examines nothing must FAIL, not pass: an owned root naming no path cannot be + // compared, and it is also unusable as the reviewer's IN-SCOPE PATHS list. + const prose = await threw(ARGS({ lanes: [LANE({ key: 'p1', owned: 'everything in the crate' })] })) + check('an owned root with no path in it aborts rather than being silently unguarded', + !!prose && /no path/i.test(prose), prose) + + // `./backend/crates/foo` and `backend/crates/foo` are the same root; dispatch must refuse. + const dotted = await threw(ARGS({ lanes: [ + LANE({ key: 'bare', wt: '/w1', owned: 'backend/crates/foo/**' }), + LANE({ key: 'dot', wt: '/w2', owned: './backend/crates/foo/**' }), + ] })) + check('./ and bare owned roots collide at dispatch', + !!dotted && /overlapping owned root/i.test(dotted), dotted) + + const parentDots = await threw(ARGS({ lanes: [ + LANE({ key: 'up', wt: '/w1', owned: 'backend/crates/foo/../bar/**' }), + LANE({ key: 'other', wt: '/w2', owned: 'docs/x/**' }), + ] })) + check('owned roots containing .. are refused', + !!parentDots && /\.\./.test(parentDots), parentDots) + + // ...and it must not OVER-refuse, or it becomes a thing people work around. + const sibling = await go(ARGS({ lanes: [ + LANE({ key: 'd1', wt: '/w1', owned: 'backend/crates/ab/**' }), + LANE({ key: 'd2', wt: '/w2', owned: 'backend/crates/a/**' }), + ] })) + check('a shared string prefix that is not a PATH prefix is not an overlap', + !!sibling && Array.isArray(sibling.headline), sibling && sibling.headline) + + const disjoint = await go(ARGS({ lanes: [ + LANE({ key: 'e1', wt: '/w1', owned: 'backend/crates/a/**' }), + LANE({ key: 'e2', wt: '/w2', owned: 'docs/**' }), + ] })) + check('disjoint owned roots dispatch normally', !!disjoint && Array.isArray(disjoint.headline), disjoint && disjoint.headline) +} + +// --- 15. A GREEN THE VERIFIER DID NOT ANSWER FOR IS NOT A GREEN. ----------------------------- +// reproduced+contradictsClaim say the verifier ran something and agreed. They do not say WHAT it +// ran, whether any command selected zero tests and exited 0, or whether the suite still proves as +// much as it did. Oracle integrity is the most common rejection cause in this programme and a +// standing review lens, yet the schema let a verifier certify a green without ever answering it. +{ + const req = (SRC.match(/const VERIFY_SCHEMA = \{[\s\S]*?required: \[([^\]]*)\]/) || ['', ''])[1] + for (const f of ['falseGreenRisk', 'commandsRun', 'oracleIntact']) { + check(`${f} is a REQUIRED verifier field`, req.includes(f), req.slice(0, 300)) + } + + // The schema is a request, not an enforcement — the harness must refuse the green itself. + const noCommands = mkAgent({ verify: () => VERIFY({ commandsRun: [] }) }) + check('a verifier that lists no command it RAN cannot certify a green', + (await go(ARGS(), noCommands)).lanes[0].converged === false) + + const noRisk = mkAgent({ verify: () => VERIFY({ falseGreenRisk: '' }) }) + check('a verifier that leaves the false-green risk blank cannot certify a green', + (await go(ARGS(), noRisk)).lanes[0].converged === false) + + const silent = mkAgent({ verify: () => VERIFY({ oracleIntact: undefined }) }) + check('a verifier that never answers oracle integrity cannot certify a green', + (await go(ARGS(), silent)).lanes[0].converged === false) + + const weakened = mkAgent({ verify: () => VERIFY({ oracleIntact: false }) }) + check('a verifier that OBSERVED a weakened oracle blocks convergence', + (await go(ARGS(), weakened)).lanes[0].converged === false) + + // A rejection with no actionable text manufactures a wasted round, so the next build must be told. + const seenPrompts = [] + const partialAnswer = mkAgent({ + build: (seen) => { seenPrompts.push(seen[seen.length - 1].prompt); return BUILD() }, + verify: () => VERIFY({ commandsRun: [] }), + }) + await go(ARGS({ maxRounds: 2 }), partialAnswer) + check('and the next round is told the verification was unanswered, not merely "disagreed"', + seenPrompts.length === 2 + && (/commandsRun/.test(seenPrompts[1]) + || /NEVER independently run/.test(seenPrompts[1]) + || /named no well-formed commands/.test(seenPrompts[1])), + { n: seenPrompts.length, round2: (seenPrompts[1] || '').slice(0, 400) }) + + // A field nobody is asked for is a field nobody fills in. + const asked = mkAgent() + await go(ARGS(), asked) + const vp = (asked.seen.find((s) => s.label.startsWith('verify:')) || {}).prompt || '' + check('the verifier is ASKED for the commands it ran and for an oracle verdict', + /commandsRun/.test(vp) && /oracleIntact/.test(vp), vp.slice(0, 300)) + + const full = await go(ARGS(), mkAgent()) + check('a fully answered verification still converges', full.lanes[0].converged === true, full.headline) +} + +// --- 16. program-tick is the CALLER, so a collision must be refused where the set is BUILT. --- +// Refusing downstream in lane-fanout is necessary and late: by then the agents are already chosen. +// These assertions drive the REAL program-tick body, with a genuinely concurrent parallel(), and +// observe what it dispatches. +{ + const compileWorkflow = (name) => new AsyncFunction( + 'args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', + fs.readFileSync(path.join(SRCDIR, `${name}.js`), 'utf8').replace(/^export const meta = /m, 'const meta = ')) + const tick = compileWorkflow('program-tick') + + const WT = (over = {}) => ({ path: '/ws/x', head: 'abc', dirtyCount: 0, prunable: false, filesVsBase: [], commitsAheadOfCandidate: 0, ...over }) + const RAW = (over = {}) => ({ candidateFiles: ['f.rs'], worktrees: [], prs: [], beads: [], ...over }) + const JUDGED = (over = {}) => ({ startNow: [], holdBack: [], alreadyDone: [], coverageRisks: [], ...over }) + const PR = (n, over = {}) => ({ number: n, title: `pr${n}`, checkConclusion: 'FAILURE', reviewDecision: '', mergeable: 'MERGEABLE', mergeStateStatus: 'CLEAN', headSha: `sha${n}`, failingChecks: ['t'], isDraft: false, ...over }) + const SEL = (n) => Array.from({ length: n }, (_, i) => ({ key: `l${i + 1}`, bead: `b${i + 1}`, owned: `p${i + 1}/**`, brief: 'concrete', accept: 'a', briefConfidence: 'grounded' })) + + const runTick = async (over = {}, raw = RAW(), judged = JUDGED()) => { + const inflight = new Set() + const overlaps = [] + const dispatched = [] + const logs = [] + const workflows = [] + const agentFn = async (prompt, o = {}) => { + const label = o.label || '' + dispatched.push(label) + if (label === 'collect') return raw + if (label === 'judge') return judged + // A PR disposition agent. `fix-then-merge` is WORK: it edits files in the candidate worktree. + inflight.add(label) + if (inflight.size > 1) overlaps.push([...inflight]) + await new Promise((r) => setTimeout(r, 5)) + inflight.delete(label) + return { pr: 1, done: true, outcome: 'x' } + } + // REAL concurrency. A sequential stub would make serialisation indistinguishable from its + // absence, which is how a test measures the fixture instead of the code. + const par = async (thunks) => Promise.all(thunks.map((t) => t())) + const plan = await tick( + { candidateWt: '/ws/cand', candidateTip: 'c'.repeat(40), base: 'main', ...over }, + agentFn, par, async (i) => i, (m) => logs.push(m), () => {}, + { total: null, spent: () => 0, remaining: () => Infinity }, + async (name, a) => { workflows.push({ name, args: a }); return { headline: [] } }, + ) + return { plan, overlaps, dispatched, logs, workflows } + } + + const twoFixes = await runTick({}, RAW({ prs: [PR(1), PR(2)] })) + // Both halves matter: a guard that dispatched ZERO PR lanes would also report zero overlaps. + check('two PR-fix lanes are both dispatched', + twoFixes.dispatched.filter((l) => l.startsWith('pr:')).length === 2, twoFixes.dispatched) + check('...and never run concurrently in the one candidate worktree they both edit', + twoFixes.overlaps.length === 0, twoFixes.overlaps) + + const overCap = await runTick({ fanout: true, maxLanes: 4 }, + RAW({ worktrees: [1, 2, 3, 4, 5].map((i) => WT({ path: `/ws/l${i}` })) }), JUDGED({ startNow: SEL(5) })) + check('more selected lanes than maxLanes refuses the fanout instead of ignoring the cap', + overCap.workflows.length === 0 && !!overCap.plan.fanoutBlocked, + { workflows: overCap.workflows.length, blocked: overCap.plan.fanoutBlocked }) + check('and says the cap is why', /maxLanes/.test(JSON.stringify(overCap.plan.fanoutBlocked || '')), overCap.plan.fanoutBlocked) + + const withinCap = await runTick({ fanout: true, maxLanes: 4 }, + RAW({ worktrees: [1, 2, 3, 4].map((i) => WT({ path: `/ws/l${i}` })) }), JUDGED({ startNow: SEL(4) })) + check('a lane set within the cap still fans out', + withinCap.workflows.length === 1 && (withinCap.workflows[0].args.lanes || []).length === 4, + withinCap.workflows.map((w) => w.name)) + + const wts = await runTick({}, RAW({ worktrees: [ + WT({ path: '/ws/cand' }), + WT({ path: '/ws/idle' }), + WT({ path: '/ws/unreadable', commitsAheadOfCandidate: -1 }), + WT({ path: '/ws/capped', filesVsBase: [], filesVsBaseCount: 900 }), + ] })) + const safe = wts.plan.worktrees.safeToRemove + check('the ACTIVE candidate worktree is never offered as safe to remove', !safe.includes('/ws/cand'), safe) + check('an unreadable worktree is not "unused"', !safe.includes('/ws/unreadable'), safe) + check('a worktree whose file list was capped is not "empty"', !safe.includes('/ws/capped'), safe) + // ...and the list is not simply emptied, which would pass all three above and help nobody. + check('a genuinely empty worktree is still removable', safe.includes('/ws/idle'), safe) +} + +// --- 17. backlog-audit: evidence nobody landed is not evidence, and coverage must be real. ---- +{ + const audit = new AsyncFunction( + 'args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', + fs.readFileSync(path.join(SRCDIR, 'backlog-audit.js'), 'utf8').replace(/^export const meta = /m, 'const meta = ')) + + const CENSUS = (over = {}) => { + const base = { + openIssueNumbers: [1], openIssueCount: 1, issues: [], beads: [], + crates: [{ name: 'identity' }, { name: 'policy' }], + cargoTomlPaths: ['backend/crates/identity/Cargo.toml', 'backend/crates/policy/Cargo.toml'], + mergedPrs: [], excludedRoots: [], + } + const merged = { ...base, ...over } + // Keep cargoTomlPaths consistent with crates unless the caller overrides either explicitly. + if (!('cargoTomlPaths' in over) && 'crates' in over) { + merged.cargoTomlPaths = (merged.crates || []).map((c) => `backend/crates/${c.name}/Cargo.toml`) + } + return merged + } + const VERDICT = (over = {}) => ({ + number: 1, title: 't', verdict: 'CLOSE-FIXED', + evidence: 'implemented in backend/crates/identity/src/lib.rs:12 by commit deadbeefcafe1234 — verified by reading it', + ...over, + }) + + const runAudit = async (census, verdicts, over = {}) => { + const dispatched = [] + const logs = [] + const agentFn = async (prompt, o = {}) => { + const label = o.label || '' + dispatched.push({ label, prompt }) + if (label === 'collect') return census + // Independent disk oracle — must not reuse Collect's crates as its only source. + if (label === 'crate-disk-census') { + return { cargoTomlPaths: census.cargoTomlPaths || [] } + } + if (label.startsWith('triage:')) return { verdicts } + if (label === 'reconcile') return { ok: true } + return { domain: label, findings: [], coverage: 'read it all' } + } + let res = null + let err = null + try { + res = await audit({ repo: '/r', ghRepo: 'o/n', ...over }, agentFn, + async (t) => Promise.all(t.map((f) => f())), async (i) => i, (m) => logs.push(m), () => {}, + { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) + } catch (e) { err = e.message } + return { res, err, dispatched, logs } + } + + const unmerged = await runAudit(CENSUS(), [VERDICT({ reachableFromDefault: false })]) + check('a CLOSE-FIXED whose evidence is not on the default branch is WITHHELD', + !!unmerged.res && (unmerged.res.withheld || []).includes(1), unmerged.res && unmerged.res.withheld) + + const unanswered = await runAudit(CENSUS(), [VERDICT()]) + check('a CLOSE-FIXED that never answered reachability is WITHHELD too — absence is a NO', + !!unanswered.res && (unanswered.res.withheld || []).includes(1), unanswered.res && unanswered.res.withheld) + + const landedEv = await runAudit(CENSUS(), [VERDICT({ reachableFromDefault: true })]) + check('a CLOSE-FIXED reachable from the default branch is still closable', + !!landedEv.res && !(landedEv.res.withheld || []).includes(1), landedEv.res && landedEv.res.withheld) + + const keep = await runAudit(CENSUS(), [VERDICT({ verdict: 'KEEP', evidence: 'this is still broken, here is the file and line that shows it' })]) + check('a KEEP verdict is not withheld — the rule is about CLOSING', + !!keep.res && (keep.res.withheld || []).length === 0, keep.res && keep.res.withheld) + + check('triage is told to PROVE reachability by running merge-base --is-ancestor', + landedEv.dispatched.some((d) => d.label.startsWith('triage:') && /merge-base --is-ancestor/.test(d.prompt)), + landedEv.dispatched.map((d) => d.label)) + + const extraCrate = await runAudit(CENSUS({ crates: [{ name: 'identity' }, { name: 'brand-new-crate' }] }), + [VERDICT({ reachableFromDefault: true })]) + // Match the lane's OWN crate list, not the census echoed into every audit prompt — the echo made + // this assertion pass against the unfixed source, which is a test measuring its own fixture. + check('a discovered crate that no named domain claims is still audited', + extraCrate.dispatched.some((d) => /^audit:/.test(d.label) && /CRATES:[^\n]*brand-new-crate/.test(d.prompt)), + extraCrate.dispatched.map((d) => d.label)) + + const allCovered = await runAudit(CENSUS({ crates: [{ name: 'identity' }] }), [VERDICT({ reachableFromDefault: true })]) + check('and no lane is invented when every discovered crate is claimed', + !allCovered.dispatched.some((d) => d.label === 'audit:uncovered'), allCovered.dispatched.map((d) => d.label)) + + const blind = await runAudit(CENSUS({ crates: [] }), [VERDICT({ reachableFromDefault: true })]) + check('a census with no crate inventory cannot claim coverage and must abort', + !!blind.err && /crate inventory/i.test(blind.err), blind.err) + + // crate-disk-census is the independent oracle (workflow sandbox has no Node fs). Omitting a + // path that find would have returned must abort — same fail-closed class as a partial crates list. + // A co-emitted Collect.cargoTomlPaths is NOT enough: Collect can omit from both fields together. + const omittedDisk = await runAudit(CENSUS({ + crates: [{ name: 'identity' }], + cargoTomlPaths: ['backend/crates/identity/Cargo.toml', 'backend/crates/brand-new/Cargo.toml'], + }), [VERDICT({ reachableFromDefault: true })]) + check('a census that omits a crate-disk-census crate aborts', + !!omittedDisk.err && /omitted/i.test(omittedDisk.err), omittedDisk.err) + check('coverage uses a dedicated crate-disk-census agent, not Collect alone', + omittedDisk.dispatched.some((d) => d.label === 'crate-disk-census')) + + const emptyToml = await runAudit(CENSUS({ + crates: [{ name: 'identity' }], + cargoTomlPaths: [], + }), [VERDICT({ reachableFromDefault: true })]) + check('an empty crate-disk-census list cannot cross-check coverage and must abort', + !!emptyToml.err && /cargoTomlPaths|crate-disk-census/i.test(emptyToml.err), emptyToml.err) + + // Hostile: Collect's crates list is internally consistent and would have matched a co-emitted + // cargoTomlPaths — the old self-validation false green. The independent disk census still sees + // the omitted crate and must abort. + const coordinatedPartial = await runAudit(CENSUS({ + crates: [{ name: 'identity' }, { name: 'policy' }], + cargoTomlPaths: [ + 'backend/crates/identity/Cargo.toml', + 'backend/crates/policy/Cargo.toml', + 'backend/crates/brand-new/Cargo.toml', + ], + }), [VERDICT({ reachableFromDefault: true })]) + check('a Collect list that omits an on-disk crate aborts even when well-formed', + !!coordinatedPartial.err && /omitted/i.test(coordinatedPartial.err) + && /brand-new/.test(coordinatedPartial.err), coordinatedPartial.err) +} + +// Six green tests over a self-built registry coexisted with a production root that wired none of it. +// The lock must name the distinction, because "all the tests pass" is exactly how it presented. +{ + for (const fragment of ['BUILDS ITS OWN SUBJECT', 'MECHANISM:', 'WIRING:', 'composition root']) { + check(`the lock separates mechanism evidence from wiring evidence: ${fragment}`, SRC.includes(fragment)) + } +} + +// Sprawl and CI-caught-it-first are the two costs the harness never charged for. +{ + for (const fragment of ['MAINTAINABILITY / COST OF CARRY', 'COMMENT BLOBBING', 'RUN WHAT CI RUNS']) { + check(`the lock charges for cost of carry: ${fragment}`, SRC.includes(fragment)) + } + // A lens that is defined but unreachable is the defect this file exists to catch. + const standing = SRC.match(/const STANDING_LENSES = \[([\s\S]*?)\n\]/) + check('the maintainability lens is a STANDING lens, not an opt-in', + !!standing && standing[1].includes('MAINTAINABILITY')) +} + +// scout.js was added to the unknown-option sweep and NOTHING ELSE, so the harness that decides what +// every other lane works on was the least tested one in the directory. Both of its judgement calls +// shipped defective and both were caught by a real run rather than here: the packing produced two +// lanes owning the same territory, and agent-authored paths reached the emitted plan unvalidated. +// These assertions extract the two pure functions and drive them with the EXACT strings that run +// produced, so neither can regress silently. +{ + const SCOUT = fs.readFileSync(path.join(HERE, 'scout.js'), 'utf8') + + const normSrc = SCOUT.match(/function normaliseRoot\(raw\) \{[\s\S]*?\n\}/) + check('scout exposes normaliseRoot to the preflight', !!normSrc) + if (normSrc) { + const REPO = '/Users/x/wt' + const normaliseRoot = new Function('REPO', 'MIN_ROOT_SEGMENTS', `${normSrc[0]}; return normaliseRoot`)(REPO, 2) + const cases = [ + [`${REPO}/backend/crates/platform/audit-chain/src/`, 'backend/crates/platform/audit-chain/src/', 'absolute path made repo-relative'], + [' i) + const find = (i) => (parent[i] === i ? i : (parent[i] = find(parent[i]))) + for (let i = 0; i < items.length; i++) { + for (let j = i + 1; j < items.length; j++) { + if (overlaps(items[i].roots, items[j].roots)) parent[find(i)] = find(j) + } + } + check('scout packing: transitively-linked territory collapses to ONE group', + new Set(items.map((_, i) => find(i))).size === 1, + items.map((_, i) => find(i))) + } + + // A guard that only exists in scout.js text is a guard nobody proved runs. + check('scout still refuses to emit a plan whose lanes overlap', + /own overlapping roots/.test(SCOUT)) + check('scout defers a bead whose paths are all unusable rather than inventing a root', + /every reported path was unusable as an owned root/.test(SCOUT)) +} + +// TRIAL GATE. Bun proved its port method on three files before scaling to 64 agents; this harness +// always dispatched every lane cold, so a brief that is wrong the same way for every lane wastes +// the whole wave instead of one lane. +{ + const TWO = (over = {}) => ARGS({ + // Distinct worktrees AND distinct owned roots: the overlap guard is a sibling rule and this + // fixture must not trip it while testing something else. + lanes: [LANE({ key: 'a', wt: '/w1', owned: 'aa/**' }), LANE({ key: 'b', wt: '/w2', owned: 'bb/**' })], + ...over, + }) + + // A failing trial must hold the fleet back, and must NOT dispatch lane b at all. + { + const agent = mkAgent({ review: () => REVIEW({ verdict: 'reject', findings: [FINDING({ severity: 'blocker' })] }) }) + const out = await go(TWO({ trial: 'a', maxRounds: 1 }), agent) + const bDispatched = agent.seen.some((x) => /:b\b/.test(x.label)) + check('a failed trial holds the fleet back', !!out && /DID NOT CONVERGE/.test(out.headline[0]), out && out.headline) + check('a failed trial dispatches NO other lane', !bDispatched, + agent.seen.map((x) => x.label)) + check('the held-back lanes are named, not silently dropped', + !!out && Array.isArray(out.heldBack) && out.heldBack.includes('b'), out && out.heldBack) + } + + // A converged trial must go on to dispatch the rest. + { + const agent = mkAgent() + const out = await go(TWO({ trial: 'a', maxRounds: 1 }), agent) + const bDispatched = agent.seen.some((x) => /:b\b/.test(x.label)) + check('a converged trial dispatches the remaining lanes', bDispatched, + agent.seen.map((x) => x.label)) + } + + // Naming a lane that does not exist is a typo, not a silent no-trial run. + { + const m = await threw(TWO({ trial: 'nope' })) + check('trial naming an unknown lane aborts', !!m && /nope/.test(m), m) + } + { + const m = await threw(ARGS({ trial: 'a' })) + check('trial with a single lane aborts rather than serialising for nothing', + !!m && /serialises for nothing/.test(m), m) + } +} + +// The tier rule must stay a RULE, not a preference someone reverses on a slow day. +{ + check('the lock states when a cheaper tier is allowed', + SRC.includes('A CHEAPER TIER IS ALLOWED ONLY WHERE AN INDEPENDENT STRONGER PASS AUDITS THE RESULT')) + const judgePhases = SRC.match(/label: `verify:[\s\S]{0,160}/g) || [] + check('the independent verifier does NOT run on a cheaper tier', + judgePhases.every((frag) => !/model:/.test(frag)), judgePhases.length) +} + +// KNOWN_ARGS IS A CLAIM ABOUT THE SOURCE, SO CHECK IT AGAINST THE SOURCE. +// The guard's whole purpose is "an option this harness does not read must abort". Two of these +// lists were hand-written and both were wrong in BOTH directions at once: slice.js omitted five +// options it genuinely reads (so the guard rejected every real invocation) while review-gate.js +// listed two it never reads (so the guard fell open on exactly what it exists to catch). A +// hand-maintained list of what the code reads is a second copy of the code. +{ + for (const name of fs.readdirSync(HERE).filter((f) => f.endsWith('.js')).map((f) => f.replace(/\.js$/, ''))) { + const src = fs.readFileSync(path.join(HERE, `${name}.js`), 'utf8') + const declared = src.match(/const KNOWN_ARGS = \[([^\]]*)\]/) + if (!declared) { check(`${name} declares KNOWN_ARGS`, false); continue } + const listed = new Set([...declared[1].matchAll(/'([^']+)'/g)].map((m) => m[1])) + + // Whichever accessor this harness uses for its parsed args. + const holder = /const KNOWN_ARGS[\s\S]{0,400}?\b(ARGS|A)\b\s*\)/.exec(src)?.[1] + || (src.includes('const ARGS') || src.includes('let ARGS') ? 'ARGS' : 'A') + const read = new Set( + [...src.matchAll(new RegExp(`\\b${holder}\\.([a-zA-Z_][a-zA-Z0-9_]*)`, 'g'))] + .map((m) => m[1]) + .filter((k) => !['length', 'lanes'].includes(k) || k === 'lanes'), + ) + // Object.keys(ARGS) inside the guard itself is not an option read. + read.delete('keys') + + const unread = [...listed].filter((k) => !read.has(k)) + const undeclared = [...read].filter((k) => !listed.has(k)) + check(`${name}: KNOWN_ARGS lists nothing the harness never reads`, unread.length === 0, unread) + check(`${name}: every option the harness reads is declared`, undeclared.length === 0, undeclared) + } +} + +// stale-take-audit.js had NO logic coverage here — only the generic KNOWN_ARGS sweep — and its +// Confirm phase failed open exactly the way the Audit phase does not. A dead agent yielded +// `refuted: null`, which is neither `=== false` nor truthy, so the suspicion fell out of BOTH result +// lists and the headline still printed "full coverage". This is the step that decides whether a +// reported reversion is REAL, and the reversion it exists to catch is a file whose un-wiring means +// the check that would have caught it does not run. +{ + const STA = fs.readFileSync(path.join(HERE, 'stale-take-audit.js'), 'utf8') + .replace(/^export const meta = /m, 'const meta = ') + const fn = new AsyncFunction('args', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'budget', 'workflow', STA) + + const drive = async (confirmReturns) => { + const agent = async (prompt, o = {}) => { + if ((o.label || '').startsWith('audit:')) { + return { results: [{ file: '.github/workflows/ci.yml', verdict: 'STALE', evidence: 'main has a step HEAD lacks', missingFromHead: 'the step', wouldBreak: 'the suite un-wires' }] } + } + return confirmReturns() + } + return fn({ repo: '/r', main: 'origin/main', files: ['.github/workflows/ci.yml'] }, + agent, async (t) => Promise.all(t.map((f) => f().catch(() => null))), async (i) => i, + () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) + } + + const dead = await drive(() => null) + check('a dead confirmation does not erase the suspicion', + !!dead && Array.isArray(dead.unconfirmed) && dead.unconfirmed.length === 1, + dead && { stale: dead.stale, refuted: dead.refuted, unconfirmed: dead.unconfirmed }) + check('a dead confirmation stops the report claiming full coverage', + !!dead && !dead.headline.some((h) => /full coverage/.test(h)), dead && dead.headline) + + // A live agent that omits the field despite the schema must land in the same bucket: the schema is + // a request to the model, not an enforcement. + const fieldless = await drive(() => ({ file: '.github/workflows/ci.yml', reasoning: 'no verdict' })) + check('a confirmation without a verdict is unresolved, not clean', + !!fieldless && fieldless.unconfirmed.length === 1, fieldless && fieldless.unconfirmed) + + // Controls: the live paths must still work, or the fix is an over-block. + // Upholding STALE also requires this pass to attest the graft payload — publishing the + // first agent's missingFromHead unseen is how a wrong quote becomes the "confirmed" patch. + const kept = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: false, reasoning: 'real', missingFromHead: 'the step (attested)' })) + check('a live confirmation that fails to refute still reports STALE', + !!kept && kept.stale.length === 1 && kept.unconfirmed.length === 0 + && kept.stale[0].missingFromHead === 'the step (attested)', + kept && { s: kept.stale, u: kept.unconfirmed }) + const noPayload = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: false, reasoning: 'real but no graft' })) + check('an upheld STALE without an attested graft payload is unresolved', + !!noPayload && noPayload.stale.length === 0 && noPayload.unconfirmed.length === 1, + noPayload && { s: noPayload.stale, u: noPayload.unconfirmed }) + // console-zd7: a confirmed-stale verdict that never attested content must not publish the + // first agent's text under the graft-shaped key. Unconfirmed may retain the claim under a + // distinctly-named field so operators see the accusation without a ready-to-apply payload. + check('an upheld STALE without attestation does not publish first-pass text as missingFromHead', + !!noPayload && noPayload.stale.length === 0 + && !JSON.stringify(noPayload.stale).includes('the step') + && noPayload.unconfirmed.length === 1 + && !Object.prototype.hasOwnProperty.call(noPayload.unconfirmed[0], 'missingFromHead') + && noPayload.unconfirmed[0].claimedMissingFromHead === 'the step', + noPayload && { s: noPayload.stale, u: noPayload.unconfirmed }) + const blankPayload = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: false, reasoning: 'real', missingFromHead: ' ' })) + check('an upheld STALE with a blank graft payload is unresolved', + !!blankPayload && blankPayload.stale.length === 0 && blankPayload.unconfirmed.length === 1, + blankPayload && { s: blankPayload.stale, u: blankPayload.unconfirmed }) + check('a blank confirmer payload does not expose a graft-shaped missingFromHead either', + !!blankPayload && blankPayload.unconfirmed.length === 1 + && !Object.prototype.hasOwnProperty.call(blankPayload.unconfirmed[0], 'missingFromHead') + && blankPayload.unconfirmed[0].claimedMissingFromHead === 'the step', + blankPayload && blankPayload.unconfirmed) + // Confirmer attests a DIFFERENT payload: publish only that. Publishing the first-pass quote + // when the confirmation returned something else is the "mismatched" fail-open. + check('confirmed stale publishes the confirmer payload, never the first-pass quote', + !!kept && kept.stale[0].missingFromHead === 'the step (attested)' + && kept.stale[0].missingFromHead !== 'the step', + kept && kept.stale[0]) + // Confirm must re-derive the graft from diffs. Handing the first-pass quote in the prompt + // invites rubber-stamping an unread payload (claim, not evidence). + { + let confirmPrompt = null + const agent = async (prompt, o = {}) => { + if ((o.label || '').startsWith('audit:')) { + return { results: [{ file: '.github/workflows/ci.yml', verdict: 'STALE', evidence: 'main has a step HEAD lacks', missingFromHead: 'FIRST_PASS_SECRET_GRAFT', wouldBreak: 'the suite un-wires' }] } + } + confirmPrompt = prompt + return { file: '.github/workflows/ci.yml', refuted: true, reasoning: 'deliberate', missingFromHead: '' } + } + await fn({ repo: '/r', main: 'origin/main', files: ['.github/workflows/ci.yml'] }, + agent, async (t) => Promise.all(t.map((f) => f().catch(() => null))), async (i) => i, + () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) + check('confirm prompt does not offer the first-pass graft payload for rubber-stamping', + typeof confirmPrompt === 'string' + && !/FIRST_PASS_SECRET_GRAFT/.test(confirmPrompt) + && !/PAYLOAD OFFERED/.test(confirmPrompt) + && /EVIDENCE OFFERED/.test(confirmPrompt), + confirmPrompt && confirmPrompt.slice(0, 400)) + } + // Oracle integrity: the pre-zd7 publish path (`missingFromHead: r.missingFromHead` from the + // audit spread) would leak the first-pass quote under a confirmed-stale verdict. Mutating the + // control back to that mapping must go red against the attestation pin. + { + const leaked = { file: 'ci.yml', missingFromHead: 'FIRST_PASS_WRONG', confirmedMissing: null, refuted: false } + const oldPublish = [leaked].filter((c) => c.refuted === false) + .map((r) => ({ file: r.file, missingFromHead: r.missingFromHead })) + const newPublish = [leaked].filter((c) => + c.refuted === false + && typeof c.confirmedMissing === 'string' + && c.confirmedMissing.trim() !== '') + .map((r) => ({ file: r.file, missingFromHead: r.confirmedMissing })) + check('mutate→red: old confirmed-stale publish path leaks the first-pass graft', + oldPublish.length === 1 && oldPublish[0].missingFromHead === 'FIRST_PASS_WRONG' + && newPublish.length === 0, + { oldPublish, newPublish }) + } + const dropped = await drive(() => ({ file: '.github/workflows/ci.yml', refuted: true, reasoning: 'deliberate', missingFromHead: '' })) + check('a live refutation still drops the suspicion', + !!dropped && dropped.stale.length === 0 && dropped.refuted.length === 1 && dropped.unconfirmed.length === 0, + dropped && { s: dropped.stale, r: dropped.refuted, u: dropped.unconfirmed }) + check('a fully-answered run still claims full coverage', + !!dropped && dropped.headline.some((h) => /full coverage/.test(h)), dropped && dropped.headline) + + // Partial result lists must not report full coverage: five verdicts for six files is incomplete. + const partialAudit = async () => { + const agent = async (prompt, o = {}) => { + if ((o.label || '').startsWith('audit:')) { + return { + results: [ + { file: 'a.yml', verdict: 'CLEAN', evidence: 'ok' }, + { file: 'b.yml', verdict: 'CLEAN', evidence: 'ok' }, + { file: 'c.yml', verdict: 'CLEAN', evidence: 'ok' }, + { file: 'd.yml', verdict: 'CLEAN', evidence: 'ok' }, + { file: 'e.yml', verdict: 'CLEAN', evidence: 'ok' }, + // f.yml omitted + ], + } + } + return null + } + return fn({ repo: '/r', main: 'origin/main', files: ['a.yml', 'b.yml', 'c.yml', 'd.yml', 'e.yml', 'f.yml'] }, + agent, async (t) => Promise.all(t.map((f) => f().catch(() => null))), async (i) => i, + () => {}, () => {}, { total: null, spent: () => 0, remaining: () => Infinity }, async () => ({})) + } + const partial = await partialAudit() + check('a live audit that omits a requested file does not claim full coverage', + !!partial && !partial.headline.some((h) => /full coverage/.test(h)) + && Array.isArray(partial.missingAuditFiles) && partial.missingAuditFiles.includes('f.yml'), + partial && { headline: partial.headline, missing: partial.missingAuditFiles }) + + check('stale-take RULES pin diffs to args.repo via git -C', + /git -C \$\{REPO\} diff HEAD/.test(STA) && /git -C \$\{REPO\} diff \$\{MAIN\} HEAD/.test(STA)) +} + +// A verifier that re-ran ONE of five claimed commands satisfied `commandsRun.length > 0`, and with +// reproduced=true the lane converged while four suites were never independently run. Some of the +// evidence re-run is a sample, not a verification. +{ + const build = (cmds) => () => BUILD({ commands: cmds }) + const CMDS = ['cargo test -p a', 'cargo test -p b', 'npm run check:x'] + + const partial = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: [CMDS[0]], falseGreenRisk: 'none', oracleIntact: true }) }) + const r1 = await go(ARGS({ maxRounds: 1 }), partial) + check('a verifier that re-ran only some claimed commands does not converge', + !!r1 && r1.lanes[0].converged === false, r1 && r1.lanes[0].converged) + + const full = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: [...CMDS], falseGreenRisk: 'none', oracleIntact: true }) }) + const r2 = await go(ARGS({ maxRounds: 1 }), full) + check('a verifier that re-ran every claimed command still converges', + !!r2 && r2.lanes[0].converged === true, r2 && r2.lanes[0].converged) + + // Whitespace must not decide it, and repetition must not substitute for coverage. + const spaced = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: CMDS.map((c) => ` ${c.replace(/ /g, ' ')} `), falseGreenRisk: 'none', oracleIntact: true }) }) + const r3 = await go(ARGS({ maxRounds: 1 }), spaced) + check('command matching is not defeated by whitespace', !!r3 && r3.lanes[0].converged === true, r3 && r3.lanes[0].converged) + + const repeated = mkAgent({ build: build(CMDS), verify: () => VERIFY({ commandsRun: [CMDS[0], CMDS[0], CMDS[0]], falseGreenRisk: 'none', oracleIntact: true }) }) + const r4 = await go(ARGS({ maxRounds: 1 }), repeated) + check('re-running one command three times is not three commands', + !!r4 && r4.lanes[0].converged === false, r4 && r4.lanes[0].converged) + + // commandsRun: [""] must not converge — every entry has to be a non-empty string. + const blankOnly = mkAgent({ + build: build(['cargo test -p a']), + verify: () => VERIFY({ commandsRun: [''], falseGreenRisk: 'none', oracleIntact: true }), + }) + const r5 = await go(ARGS({ maxRounds: 1 }), blankOnly) + check('commandsRun of a single empty string does not converge', + !!r5 && r5.lanes[0].converged === false, r5 && r5.lanes[0].converged) + + const blankAmong = mkAgent({ + build: build(['cargo test -p a']), + verify: () => VERIFY({ commandsRun: ['cargo test -p a', ''], falseGreenRisk: 'none', oracleIntact: true }), + }) + const r6 = await go(ARGS({ maxRounds: 1 }), blankAmong) + check('a blank entry among commandsRun fails closed even if a real command is present', + !!r6 && r6.lanes[0].converged === false, r6 && r6.lanes[0].converged) + + // Omit/invalid claimed commands must not converge: empty coverage against the verifier's own + // commands is a vacuous pass, not independent verification of a done build. + const omitted = mkAgent({ + build: () => BUILD({ commands: [] }), + verify: () => VERIFY({ commandsRun: ['cargo test -p x'], falseGreenRisk: 'none', oracleIntact: true }), + }) + const r7 = await go(ARGS({ maxRounds: 1 }), omitted) + check('a done build that omits commands does not converge', + !!r7 && r7.lanes[0].converged === false, r7 && r7.lanes[0].converged) + + const blanksOnly = mkAgent({ + build: () => BUILD({ commands: ['', ' '] }), + verify: () => VERIFY({ commandsRun: ['cargo test -p x'], falseGreenRisk: 'none', oracleIntact: true }), + }) + const r8 = await go(ARGS({ maxRounds: 1 }), blanksOnly) + check('a done build whose commands are only blanks does not converge', + !!r8 && r8.lanes[0].converged === false, r8 && r8.lanes[0].converged) + + const missingField = mkAgent({ + build: () => { + const b = BUILD() + delete b.commands + return b + }, + verify: () => VERIFY({ commandsRun: ['cargo test -p x'], falseGreenRisk: 'none', oracleIntact: true }), + }) + const r9 = await go(ARGS({ maxRounds: 1 }), missingField) + check('a done build that omits the commands field does not converge', + !!r9 && r9.lanes[0].converged === false, r9 && r9.lanes[0].converged) +} + +// scout deferred a bead whose paths were ALL unusable and merely LOGGED the partial case, emitting a +// lane authorised for the work but forbidden from part of it — a failure that arrives after dispatch. +{ + const SCOUT = fs.readFileSync(path.join(HERE, 'scout.js'), 'utf8') + check('scout defers a bead when only SOME of its paths are unusable', + /are unusable as owned roots, so any lane would be authorised/.test(SCOUT)) + // The partial branch must CONTINUE, not fall through to placeable.push. + const partial = SCOUT.match(/if \(rejected\) \{[\s\S]*?\n \}/) + check('the partial-rejection branch stops the bead being placed', + !!partial && /continue/.test(partial[0]), partial && partial[0].slice(0, 120)) + + // Unverified dependency edges must be dropped, not kept via the stored fallback. + check('scout drops edges with no verification verdict (fail closed)', + /if \(!v\) \{ unverifiedEdges\.push\(e\); continue \}/.test(SCOUT) + && /UNVERIFIED EDGES ARE NOT KEPT/.test(SCOUT)) + check('scout no longer advertises fanoutArgs as feed-straight-into lane-fanout', + /fanoutPlan:/.test(SCOUT) && /status: 'incomplete'/.test(SCOUT) && !/Feed straight into lane-fanout/.test(SCOUT)) + check('scout fanoutPlan is explicitly incomplete for lane-fanout', + /missing tip and per-lane wt\/brief\/accept/.test(SCOUT)) + check('scout measures depth by downstream dependents (reverse edges)', + /corrected\.filter\(\(e\) => e\.to === id\)/.test(SCOUT)) + check('scout counts rejected paths before deduplicating roots', + /const normalised = \(item\.paths/.test(SCOUT) && /rejected = normalised\.filter/.test(SCOUT)) + check('scout rejects absolute paths outside REPO with a segment boundary', + /r\.startsWith\(`\$\{repo\}\/`\)/.test(SCOUT) && /else return null/.test(SCOUT)) +} + +// backlog-audit must not treat a partial crate census as complete coverage. +{ + const AUDIT = fs.readFileSync(path.join(HERE, 'backlog-audit.js'), 'utf8') + check('backlog-audit does not import Node fs for the crate census', !/import\(['"]node:fs['"]\)/.test(AUDIT)) + check('backlog-audit measures crates via a dedicated crate-disk-census agent', + /label: 'crate-disk-census'/.test(AUDIT) && /find backend\/crates -name Cargo\.toml/.test(AUDIT)) + check('backlog-audit does not treat Collect.cargoTomlPaths as the disk oracle', + /required: \['openIssueNumbers', 'openIssueCount', 'issues', 'beads', 'crates'\]/.test(AUDIT) + && !/required: \['openIssueNumbers', 'openIssueCount', 'issues', 'beads', 'crates', 'cargoTomlPaths'\]/.test(AUDIT) + && /diskCensus\.cargoTomlPaths/.test(AUDIT)) + const omitSrc = AUDIT.match(/function cratesOmittedFromCensus\([\s\S]*?\n\}/) + check('backlog-audit exposes cratesOmittedFromCensus to the preflight', !!omitSrc) + if (omitSrc) { + const cratesOmittedFromCensus = new Function(`${omitSrc[0]}; return cratesOmittedFromCensus`)() + check('a census that omits an on-disk crate is incomplete', + cratesOmittedFromCensus(['identity', 'brand-new'], ['identity']).join(',') === 'brand-new') + check('a census parent prefix still covers nested crates', + cratesOmittedFromCensus(['identity/domain', 'identity/rest'], ['identity']).length === 0) + } + const deriveSrc = AUDIT.match(/function crateNamesFromCargoTomlPaths\([\s\S]*?\n\}/) + check('backlog-audit exposes crateNamesFromCargoTomlPaths to the preflight', !!deriveSrc) + if (deriveSrc) { + const crateNamesFromCargoTomlPaths = new Function(`${deriveSrc[0]}; return crateNamesFromCargoTomlPaths`)() + check('cargoTomlPaths strip to crate names under backend/crates', + crateNamesFromCargoTomlPaths(['backend/crates/identity/Cargo.toml', './backend/crates/policy/Cargo.toml']).join(',') === 'identity,policy') + } +} + +// Contract-drift: string literals must not invent HTTP methods; OpenAPI path keys may contain ':'. +{ + const driftPath = path.join(HERE, '..', '..', '..', 'scripts', 'check-platform-contract-drift.mjs') + const driftSrc = fs.readFileSync(driftPath, 'utf8') + check('contract-drift masks string literals before method discovery', + /function maskStringLiterals/.test(driftSrc) && /maskStringLiterals\(methodExpression\)/.test(driftSrc)) + check('contract-drift OpenAPI path keys allow colons inside the path', + driftSrc.includes('trimmedRight.match(/^ {2}(\\/.+):$/)') + || driftSrc.includes('trimmedRight.match(/^ {2}(/.+):$/)') + || /\\\/\.+\):\$/.test(driftSrc)) + check('contract-drift refuses repo-wide same-name fallback for undeclared PATH consts', + /refusing repo-wide same-name fallback/.test(driftSrc)) + check('contract-drift discovers route sources after stripping comments/literals', + /stripRustCommentsAndLiterals\(readFileSync\(file, "utf8"\)\)\.includes\(\s*"\.route\("\s*\)/.test(driftSrc) + || /stripRustCommentsAndLiterals\(readFileSync\(file, "utf8"\)\)\.includes\("\.route\("\)/.test(driftSrc)) + + const maskSrc = driftSrc.match(/function maskStringLiterals\([\s\S]*?\n\}/) + const stripSrc = driftSrc.match(/function stripRustCommentsAndLiterals\([\s\S]*?\n\}/) + if (stripSrc) { + const stripRustCommentsAndLiterals = new Function(`${stripSrc[0]}; return stripRustCommentsAndLiterals`)() + const docOnly = '//! example\n/// `.route("/api/x", get(h))` in docs only\nfn unused() {}\n' + const after = stripRustCommentsAndLiterals(docOnly) + check('doc-comment .route( does not survive strip discovery', + !after.includes('.route('), after) + const real = 'fn router() { axum::Router::new().route("/api/x", get(h)) }\n' + check('real .route( survives strip discovery', + stripRustCommentsAndLiterals(real).includes('.route(')) + } + if (maskSrc) { + const maskStringLiterals = new Function(`${maskSrc[0]}; return maskStringLiterals`)() + const methodConstructor = /\b(get|put|post|delete|options|head|patch|trace)\s*\(/g + const expr = 'get(handler).layer(/* "documentation says get() here" */)' + // Simulate a string that would false-positive without masking: + const withProse = 'get(handler_with_doc("documentation says get() here"))' + const rawHits = [...withProse.matchAll(methodConstructor)].map((m) => m[1]) + const maskedHits = [...maskStringLiterals(withProse).matchAll(methodConstructor)].map((m) => m[1]) + check('prose get() inside a string is a raw false-positive before masking', + rawHits.includes('get') && rawHits.length >= 2, rawHits) + check('masking string literals drops the prose get() false-positive', + maskedHits.length === 1 && maskedHits[0] === 'get', maskedHits) + + const openApiSrc = driftSrc.match(/function openApiApiOperations\([\s\S]*?\n\}/) + check('openApiApiOperations is extractable', !!openApiSrc) + if (openApiSrc) { + const helpers = driftSrc.match(/function operationKey\([\s\S]*?\n\}\n\nfunction normalizePathParameters\([\s\S]*?\n\}/) + const openApiApiOperations = new Function( + `const httpMethodSet = new Set(['get','put','post','delete','options','head','patch','trace']);\n` + + `${helpers ? helpers[0] : 'function operationKey(m,p){return m.toUpperCase()+\" \"+p} function normalizePathParameters(p){return p}'};\n` + + `${openApiSrc[0]}; return openApiApiOperations`, + )() + const ops = openApiApiOperations([ + 'paths:', + ' /api/jobs:run:', + ' post:', + ' summary: run', + ' /api/plain:', + ' get:', + ].join('\n')) + check('OpenAPI path keys with a colon are accepted', + ops.has('POST /api/jobs:run') && ops.has('GET /api/plain'), [...ops]) + } + } +} + +console.log(failures ? `\n${failures} FAILURE(S) — do not dispatch` : '\nALL PASS — safe to dispatch') +process.exit(failures ? 1 : 0) diff --git a/scripts/console/workflows/program-tick.js b/scripts/console/workflows/program-tick.js new file mode 100644 index 000000000..936d8da49 --- /dev/null +++ b/scripts/console/workflows/program-tick.js @@ -0,0 +1,435 @@ +export const meta = { + name: 'program-tick', + description: 'Survey the program before doing any work: collect live state mechanically, classify it deterministically in-script, and use agents only where judgement is genuinely required — then dispose of finished PRs and hand the chosen lanes to lane-fanout', + whenToUse: 'At the start of any working session or phase, INSTEAD of hand-picking lanes. Answers "what should be worked on right now, and what is already sitting somewhere unfinished" before a single implementer is spawned.', + phases: [ + { title: 'Collect', detail: 'one mechanical agent: raw git/gh/bd output, verbatim, no interpretation' }, + { title: 'Judge', detail: 'agents only for what cannot be computed: is the work still needed, is the brief real' }, + { title: 'Disposition', detail: 'act on finished PRs by their CURRENT state; never poll' }, + ], +} + +// --------------------------------------------------------------------------- +// DESIGN NOTE — why the split is where it is. +// +// Workflow scripts have no filesystem and no shell: only agent(), parallel(), phase(), log(). So +// running `git`/`gh`/`bd` MUST go through an agent. What must NOT go through an agent is the +// reasoning over that output. Set differences, graph reachability, path-collision detection and +// lane selection are total functions of the collected facts — computing them in an LLM adds a +// hallucination surface to arithmetic that cannot be wrong in JS. +// +// agent -> collection (forced: no fs access) and judgement (genuinely interpretive) +// script -> every classification that is a function of the collected facts +// +// args = { +// candidateWt, candidateTip, base, authority?, maxLanes?=4, fanout?=false, workspace? +// +// maxLanes is a HARD CAP, not advice to the judge: a selection larger than it refuses the whole +// fan-out rather than dispatching past it or silently truncating. +// +// workspace defaults to the directory candidateWt itself lives in. Lane worktrees are RESOLVED +// against the worktree inventory collected below, never constructed from a literal — see the +// fanout chain at the bottom of this file. +// } +// --------------------------------------------------------------------------- + +let ARGS = args +if (typeof ARGS === 'string') { + try { ARGS = JSON.parse(ARGS) } catch (e) { throw new Error(`program-tick: args is not valid JSON: ${e.message}`) } +} +ARGS = ARGS || {} + +// An option this workflow does not read must abort rather than be silently dropped. This guard was +// written for lane-fanout, repeated in backlog-audit, and never applied here — and a rule living in +// two of three sibling files is the signal that it belongs to the shape, not to the file. In a +// sibling runner the same defect (an option accepted, ignored, and the run looking entirely normal) +// cost six lanes. `fanout` and `workspace` are read at the bottom of this file; both are listed. +const KNOWN_ARGS = ['candidateWt', 'candidateTip', 'base', 'authority', 'maxLanes', 'fanout', 'workspace'] +{ + const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) + if (unknown.length) { + throw new Error(`program-tick: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) + } +} + +const CAND_WT = ARGS.candidateWt +const CAND_TIP = ARGS.candidateTip +const BASE = ARGS.base +const MAX_LANES = ARGS.maxLanes || 4 +const AUTHORITY = ARGS.authority || '' + +for (const [k, v] of [['candidateWt', CAND_WT], ['candidateTip', CAND_TIP], ['base', BASE]]) { + if (!v) throw new Error(`program-tick: args.${k} is required`) +} + +const READ_ONLY = ` +=== READ-ONLY. NON-NEGOTIABLE. === +Run ONLY: git status/log/diff/rev-parse/ls-files/worktree list, gh pr list/view/checks, bd list/show/ready/blocked/dep. +NEVER: stash, reset, checkout , rebase, merge, clean, push, worktree add|remove, bd close/update, +gh pr merge/close/edit. Do not edit a single file. You are reading, not deciding and not acting. +DO NOT POLL: read each PR's current check conclusion ONCE. Never sleep, never wait for a run. +` + +// --- COLLECT: raw facts only. No judgement, no classification, no opinion. ------------------- +const RAW_SCHEMA = { + type: 'object', + required: ['candidateFiles', 'worktrees', 'prs', 'beads'], + properties: { + candidateFiles: { type: 'array', items: { type: 'string' }, description: `verbatim output of: git -C ${CAND_WT} diff --name-only ${BASE}..${CAND_TIP}` }, + worktrees: { + type: 'array', + items: { + type: 'object', + required: ['path', 'head', 'dirtyCount', 'prunable', 'filesVsBase', 'filesVsBaseCount', 'commitsAheadOfCandidate'], + properties: { + path: { type: 'string' }, + head: { type: 'string' }, + branch: { type: 'string' }, + dirtyCount: { type: 'number', description: 'lines of git status --porcelain' }, + prunable: { type: 'boolean', description: 'worktree list --porcelain marked it prunable' }, + filesVsBase: { type: 'array', items: { type: 'string' }, description: `git -C diff --name-only ${BASE} — empty array if none or unreadable` }, + // Without the count, an empty filesVsBase cannot be told apart from a capped one, and + // "we did not look" reads exactly like "there is nothing there". + filesVsBaseCount: { type: 'number', description: 'the @@N value: how many files differ from base IN TOTAL, reported even when filesVsBase was capped or unreadable; -1 if the command failed' }, + commitsAheadOfCandidate: { type: 'number', description: `git -C rev-list --count ${CAND_TIP}..HEAD, or -1 if unreadable` }, + }, + }, + }, + prs: { + type: 'array', + items: { + type: 'object', + required: ['number', 'title', 'checkConclusion', 'reviewDecision', 'mergeable', 'headSha'], + properties: { + number: { type: 'number' }, + title: { type: 'string' }, + checkConclusion: { type: 'string', description: 'SUCCESS / FAILURE / PENDING / NONE — the rollup, read once' }, + reviewDecision: { type: 'string' }, + mergeable: { type: 'string' }, + mergeStateStatus: { type: 'string' }, + headSha: { type: 'string' }, + failingChecks: { type: 'array', items: { type: 'string' } }, + isDraft: { type: 'boolean' }, + }, + }, + }, + beads: { + type: 'array', + items: { + type: 'object', + required: ['id', 'title', 'status', 'blockedByOpen'], + properties: { + id: { type: 'string' }, + title: { type: 'string' }, + status: { type: 'string' }, + priority: { type: 'number' }, + blockedByOpen: { type: 'array', items: { type: 'string' }, description: 'ids of OPEN blockers only, from bd show; empty array if none' }, + blocksCount: { type: 'number', description: 'how many issues this one blocks — 0 if none' }, + }, + }, + }, + }, +} + +phase('Collect') + +const raw = await agent(`Collect raw program state. You are a COLLECTOR: report exactly what the commands +print. Do not classify, do not interpret, do not decide anything, do not summarise. Another stage +does all of that; your only failure mode that matters is reporting something you did not observe. + + cd ${CAND_WT} + +1. CANDIDATE FILE SET + git --no-pager diff --name-only ${BASE}..${CAND_TIP} + +2. WORKTREES — there may be a HUNDRED of them. Do NOT run four separate commands per worktree: + that is hundreds of shell round-trips and it is the slowest thing in this survey by far. Run ONE + batched loop and parse its output: + + git worktree list --porcelain | awk '/^worktree /{print $2}' | while read -r w; do + printf '@@WT\\t%s\\t%s\\t%s\\t%s\\n' \\ + "$w" \\ + "$(git -C "$w" rev-parse --short HEAD 2>/dev/null || echo MISSING)" \\ + "$(git -C "$w" status --porcelain 2>/dev/null | wc -l | tr -d ' ')" \\ + "$(git -C "$w" rev-list --count ${CAND_TIP}..HEAD 2>/dev/null || echo -1)" + n=$(git -C "$w" --no-pager diff --name-only ${BASE} 2>/dev/null | wc -l | tr -d ' ') + printf '@@N\\t%s\\n' "$n" + [ "$n" -le 60 ] && git -C "$w" --no-pager diff --name-only ${BASE} 2>/dev/null | sed 's/^/@@F\\t/' + done + + One pass, one round-trip per worktree instead of four. Measured on this repository: 89 worktrees + in ~5 seconds, against many minutes for the per-worktree form. + + The 60-file cap is deliberate. A worktree differing from base by thousands of files is an old + branch, not a lane: its file list is worthless to the classification and would swamp your + output (unfiltered, this repository emits 25,000+ lines). Report its @@N count with an empty + filesVsBase and let the count speak. A LANE worktree is small by construction. + + ALWAYS report @@N as filesVsBaseCount, capped or not. An empty filesVsBase with no count cannot + be told apart from a worktree that genuinely holds nothing, and the classification below would + then offer a capped worktree for removal. + + A worktree whose HEAD prints MISSING has no gitdir: set prunable true, filesVsBase [], + filesVsBaseCount -1 and commitsAheadOfCandidate -1. Do NOT skip it and do NOT guess its contents. + +3. PULL REQUESTS + gh pr list --state open --json number,title,mergeable,reviewDecision,isDraft,headRefOid + For each, ONCE: + gh pr view --json statusCheckRollup,mergeStateStatus + Reduce statusCheckRollup to one word — SUCCESS / FAILURE / PENDING / NONE — and list the names + of any failing checks. If there are zero open PRs, return an empty array. That is a complete + answer, not a failure. + +4. BEADS + bd list --status=open --json (fall back to plain output if --json is unsupported) + bd blocked + For each open bead record its id, title, status, priority, the ids of its OPEN blockers only, + and how many issues it blocks. + +Report numbers you actually saw. If a command fails, report the failure in that record rather than +inventing a plausible value. +${READ_ONLY}`, { label: 'collect', phase: 'Collect', schema: RAW_SCHEMA }) + +// --- CLASSIFY: pure functions of the collected facts. No agent involved. ---------------------- +const candSet = new Set(raw.candidateFiles || []) +const wts = raw.worktrees || [] + +const classifyWorktree = (w) => { + if (w.prunable) return 'prunable' + const files = w.filesVsBase || [] + const ahead = typeof w.commitsAheadOfCandidate === 'number' ? w.commitsAheadOfCandidate : -1 + // UNREADABLE IS NOT UNUSED, and it used to be sorted as `empty` — i.e. offered for removal. Two + // ways the inventory fails to describe a worktree, and both land here: `git rev-list` failed and + // the collector reported -1, and the deliberate 60-file cap, which reports the COUNT and an empty + // file list. In both cases the evidence that the worktree holds nothing is exactly what is + // missing, and removal is the one irreversible action in this plan. + const count = typeof w.filesVsBaseCount === 'number' ? w.filesVsBaseCount : files.length + if (ahead < 0 || count > files.length) return 'unreadable' + const unmerged = files.filter((f) => !candSet.has(f)) + // At risk if it carries commits the candidate lacks, or contributes files the candidate lacks, + // or has uncommitted edits. Anything else contributes nothing that is not already captured. + if (ahead > 0 || unmerged.length > 0) return 'atRisk' + if ((w.dirtyCount || 0) > 0) return 'dirty' + if (files.length === 0) return 'empty' + return 'integrated' +} + +const buckets = { integrated: [], atRisk: [], prunable: [], dirty: [], empty: [], unreadable: [] } +for (const w of wts) buckets[classifyWorktree(w)].push(w) + +// Duplicate work: the same non-candidate file contributed by more than one worktree. +const contributors = new Map() +for (const w of wts) { + for (const f of (w.filesVsBase || [])) { + if (candSet.has(f)) continue + if (!contributors.has(f)) contributors.set(f, []) + contributors.get(f).push(w.path) + } +} +const duplicated = [...contributors.entries()].filter(([, ps]) => ps.length > 1) + .map(([file, paths]) => ({ file, paths })) + +// Bead readiness is a graph fact, not a judgement call. +const beads = raw.beads || [] +const openIds = new Set(beads.filter((b) => b.status === 'open' || b.status === 'in_progress').map((b) => b.id)) +const unblocked = beads.filter((b) => b.status === 'open' && !(b.blockedByOpen || []).some((d) => openIds.has(d))) +const blocked = beads.filter((b) => b.status === 'open' && (b.blockedByOpen || []).some((d) => openIds.has(d))) +// Rank by what unblocks the most, then by priority. +const ranked = [...unblocked].sort((a, b) => (b.blocksCount || 0) - (a.blocksCount || 0) || (a.priority ?? 9) - (b.priority ?? 9)) + +// PR disposition is a decision table over the rollup, not an opinion. +const prAction = (p) => { + if (p.isDraft) return 'report-only' + if (p.checkConclusion === 'PENDING') return 'in-flight' + if (p.checkConclusion === 'FAILURE') return 'fix-then-merge' + if (p.mergeStateStatus === 'BEHIND') return 'rebase-then-merge' + if (p.checkConclusion === 'SUCCESS' && p.reviewDecision !== 'APPROVED') return 'needs-review' + if (p.checkConclusion === 'SUCCESS' && p.mergeable === 'MERGEABLE') return 'merge' + return 'report-only' +} +const prPlan = (raw.prs || []).map((p) => ({ + pr: p.number, title: p.title, action: prAction(p), + checkConclusion: p.checkConclusion, reviewDecision: p.reviewDecision, + failingChecks: p.failingChecks || [], +})) + +log(`collected: ${wts.length} worktrees, ${(raw.prs || []).length} open PRs, ${beads.length} open beads`) +log(`worktrees -> integrated ${buckets.integrated.length}, atRisk ${buckets.atRisk.length}, prunable ${buckets.prunable.length}, dirty ${buckets.dirty.length}, empty ${buckets.empty.length}, unreadable ${buckets.unreadable.length}`) +log(`beads -> ${unblocked.length} unblocked, ${blocked.length} blocked${duplicated.length ? `; ${duplicated.length} file(s) contributed by more than one worktree` : ''}`) + +// --- JUDGE: only what cannot be computed. ----------------------------------------------------- +const JUDGE_SCHEMA = { + type: 'object', + required: ['startNow', 'holdBack', 'alreadyDone', 'coverageRisks'], + properties: { + startNow: { + type: 'array', + items: { + type: 'object', + required: ['key', 'bead', 'owned', 'brief', 'accept', 'briefConfidence'], + properties: { + key: { type: 'string' }, bead: { type: 'string' }, owned: { type: 'string' }, + brief: { type: 'string' }, accept: { type: 'string' }, + briefConfidence: { type: 'string', enum: ['grounded', 'thin'] }, + structurallyNeedsLeasedFile: { type: 'string', description: 'a leased path the deliverable cannot avoid (workspace manifest for a new crate, CI wiring for a new test binary), or "none"' }, + }, + }, + }, + holdBack: { type: 'array', items: { type: 'object', required: ['bead', 'why'], properties: { bead: { type: 'string' }, why: { type: 'string' } } } }, + alreadyDone: { type: 'array', items: { type: 'object', required: ['bead', 'proof'], properties: { bead: { type: 'string' }, proof: { type: 'string' } } } }, + coverageRisks: { type: 'array', items: { type: 'string' } }, + }, +} + +phase('Judge') + +const judged = await agent(`Decide what to actually work on. The mechanical classification is DONE and is not +yours to redo — trust the numbers below and spend your effort only on what arithmetic cannot answer. + +ALREADY COMPUTED (do not recompute): + unblocked beads, ranked by how much they unblock: +${ranked.map((b) => ` ${b.id} [P${b.priority ?? '?'}] blocks:${b.blocksCount ?? 0} — ${b.title}`).join('\n') || ' (none)'} + blocked beads: +${blocked.map((b) => ` ${b.id} — waiting on ${(b.blockedByOpen || []).join(', ')}`).join('\n') || ' (none)'} + worktrees: integrated ${buckets.integrated.length}, atRisk ${buckets.atRisk.length}, prunable ${buckets.prunable.length}, dirty ${buckets.dirty.length}, unreadable ${buckets.unreadable.length} +${buckets.atRisk.length ? ` AT RISK (hold work nowhere else — never propose removing these):\n${buckets.atRisk.map((w) => ` ${w.path} @ ${w.head} (+${w.commitsAheadOfCandidate} commits)`).join('\n')}` : ''} +${duplicated.length ? ` DUPLICATED across worktrees:\n${duplicated.slice(0, 10).map((d) => ` ${d.file} <- ${d.paths.join(', ')}`).join('\n')}` : ''} + + cd ${CAND_WT} +${AUTHORITY ? ` Authority for scope and non-goals: ${AUTHORITY}` : ''} + +YOUR JOB — three judgements, each needing evidence a computation cannot supply: + +1. IS THE WORK STILL NEEDED? For each unblocked bead, CHECK THE CODE before calling it runnable: + grep for the artefact it would create, read the gate it would satisfy, run the check it would + fix. A bead tracking work that already landed is a recurring failure here — list it under + alreadyDone WITH THE PROOF, not under startNow. + +2. WHAT SHOULD ACTUALLY START, at most ${MAX_LANES} — a hard cap: returning more refuses the whole + fan-out and nothing starts. Choose for PATH DISJOINTNESS FIRST: two lanes sharing a writable + path must never both start however ready they look, and the fan-out refuses them anyway. + Prefer lanes that unblock the most. For each, write a brief citing CONCRETE files, line numbers + and the governing authority clause — a brief that restates the bead title is useless, mark it briefConfidence + "thin". Name any leased file the deliverable structurally cannot avoid (a new crate needs the + workspace manifest; a new test binary needs CI wiring) under structurallyNeedsLeasedFile, and + instruct the lane to REPORT it rather than stall or fake it. Omitting that is how a lane gets + authorised to do something while forbidden the only way to do it. + +3. COVERAGE RISKS. Anything landed or about to land WITHOUT a test that can fail, or a claimed + invariant with no oracle. This program has repeatedly shipped guards that could not detect their + own violation: a non-exhaustive matches!, a route check reading a hand-maintained list, a $ref + validator that truncated its input, a preflight test that never called preflight. Hunt that shape. + +Be honest when little is runnable. A thin frontier stated plainly beats lanes invented to look busy. +${READ_ONLY}`, { label: 'judge', phase: 'Judge', schema: JUDGE_SCHEMA }) + +const thin = (judged.startNow || []).filter((l) => l.briefConfidence === 'thin').map((l) => l.key) +if (thin.length) log(`briefs too thin to run unreviewed: ${thin.join(', ')}`) +if ((judged.alreadyDone || []).length) log(`beads tracking work that already landed: ${judged.alreadyDone.length}`) + +// --- DISPOSITION: act on PRs by current state. ------------------------------------------------ +phase('Disposition') + +const actionable = prPlan.filter((p) => ['merge', 'fix-then-merge', 'rebase-then-merge'].includes(p.action)) + +// EVERY DISPOSITION RUNS IN THE SAME CANDIDATE WORKTREE, AND fix-then-merge IS WORK. Two of +// them through parallel() is two writers in one root: exactly the collision lane-fanout refuses at +// dispatch for lanes, arriving here by a different door because PR dispositions are not lanes. +// program-tick is the CALLER that builds this set, so refusing downstream would be late — the +// agents are already chosen. Serialised, not partitioned by action: any of these agents may touch +// the tree, and a rule that has to guess which ones is a rule with a next spelling. Depth costs +// wall-clock and a tick disposes of a handful of PRs, which is the cheap side of the trade. +const disposePr = (p) => agent(`Dispose of PR #${p.pr} ("${p.title}") in ${CAND_WT}. + +DECIDED ACTION: ${p.action} +Current rollup: ${p.checkConclusion}; review: ${p.reviewDecision}${p.failingChecks.length ? `; failing: ${p.failingChecks.join(', ')}` : ''} + + - merge: verify ONCE that every required context is green, review is satisfied, and the head is + still the reviewed head. Then squash merge. If ANY required context is not green, or the head + moved since review, STOP and report — do not merge. + - fix-then-merge: this is WORK, not a watchlist item. Diagnose the failing checks and fix the + CAUSE. Do not re-run hoping for a different answer. Do not disable, skip or weaken the failing + check. If the fix lies outside this PR's scope, report exactly what is needed and stop. + - rebase-then-merge: you may NOT rebase or force-push. Report precisely what the owner must do. + +*** You may not relax branch protection, bypass a required check, skip a test, or merge anything +whose signed authority train is not intact. If the right action needs any of that, report it. *** +Do not poll: act on the state above, once.`, + { label: `pr:${p.pr}`, phase: 'Disposition', schema: { type: 'object', required: ['pr', 'done', 'outcome'], properties: { pr: { type: 'number' }, done: { type: 'boolean' }, outcome: { type: 'string' }, blockedBy: { type: 'string' } } } }) + +const prResults = [] +for (const p of actionable) prResults.push(await disposePr(p)) + +if (!actionable.length) log(`no PR needed action this tick (${prPlan.length} open)`) + +const plan = { + summary: `${ranked.length} unblocked, ${(judged.startNow || []).length} to start, ${buckets.atRisk.length} worktrees at risk, ${prPlan.length} open PRs`, + startNow: judged.startNow || [], + holdBack: judged.holdBack || [], + alreadyDone: judged.alreadyDone || [], + coverageRisks: judged.coverageRisks || [], + worktrees: { + counts: Object.fromEntries(Object.entries(buckets).map(([k, v]) => [k, v.length])), + // NEVER the worktree this tick is operating IN. It classifies as integrated by construction — + // its diff against base IS the candidate file set — so it sat at the head of a list titled + // "safe to remove", one copy-paste away from deleting the checkout doing the work. + safeToRemove: buckets.integrated.concat(buckets.empty) + .map((w) => w.path).filter((p) => String(p).replace(/\/+$/, '') !== CAND_WT.replace(/\/+$/, '')), + mustNotRemove: buckets.atRisk.map((w) => ({ path: w.path, head: w.head, aheadBy: w.commitsAheadOfCandidate })), + // Not removable and not at risk either: we simply cannot see what they hold. Reported so the + // gap is visible rather than resolved by a silent default in either direction. + unreadable: buckets.unreadable.map((w) => w.path), + prunableRegistrations: buckets.prunable.map((w) => w.path), + duplicated, + }, + prPlan, + prResults, +} + +// A lane worktree must be one this tick OBSERVED, never one it composed. This used to send every +// selected lane to a literal absolute path under one developer's home directory: a path that exists +// on exactly one machine, that nothing in this workflow creates, and that on CI, on Linux, or in any +// other checkout pointed every implementer at nothing at all. (The preflight now forbids the +// spelling as well as the instance.) Workflow scripts have no filesystem, so the +// only evidence a path exists is the inventory the collector just read — resolve against that, and +// refuse to spawn anything if a lane has no single unambiguous worktree. +const WORKSPACE = (ARGS.workspace || CAND_WT.replace(/\/+$/, '').replace(/\/[^/]*$/, '')).replace(/\/+$/, '') +const resolveLaneWt = (key) => { + const hits = wts.filter((w) => !w.prunable && (w.path === `${WORKSPACE}/${key}` || w.path.endsWith(`-${key}`))) + return { hits: hits.map((w) => w.path) } +} + +// Chaining is opt-in and refuses to spawn implementers off a brief nobody has read. +if (ARGS.fanout && (judged.startNow || []).length && !thin.length) { + const resolved = judged.startNow.map((l) => ({ lane: l, ...resolveLaneWt(l.key) })) + const unusable = resolved.filter((r) => r.hits.length !== 1).map((r) => ({ + key: r.lane.key, + why: r.hits.length ? `ambiguous: ${r.hits.join(', ')}` : `no worktree under ${WORKSPACE} matches lane "${r.lane.key}"`, + })) + // maxLanes lived ONLY in the judge's prompt, and a prompt is a request. The judge is free to + // return more, and everything it returned was then fanned out — the cap was read, printed, and + // enforced by nobody. Enforced HERE because here is the last point before implementers exist. + const overCap = judged.startNow.length > MAX_LANES + ? [{ key: '(all)', why: `${judged.startNow.length} lanes selected but maxLanes=${MAX_LANES}. Raise maxLanes deliberately, or narrow the selection — do not dispatch past a cap the caller set.` }] + : [] + const blocked = [...overCap, ...unusable] + if (blocked.length) { + // Refuse the WHOLE fanout, not the offending lanes only: chaining the remaining subset + // would silently drop selected work, which is the same defect in a different spelling. + plan.fanoutBlocked = blocked + log(`FANOUT REFUSED — no implementer dispatched. ${plan.fanoutBlocked.map((b) => `${b.key}: ${b.why}`).join(' | ')}`) + if (unusable.length) log(`create the missing worktree(s) from ${CAND_TIP} under ${WORKSPACE}, or pass args.workspace, then re-run with fanout`) + } else { + log(`chaining into lane-fanout with ${resolved.length} lane(s): ${resolved.map((r) => r.hits[0]).join(', ')}`) + plan.fanoutResult = await workflow('lane-fanout', { + tip: CAND_TIP, + lanes: resolved.map(({ lane: l, hits }) => ({ + key: l.key, bead: l.bead, owned: l.owned, brief: l.brief, accept: l.accept, wt: hits[0], + })), + }) + } +} else if (ARGS.fanout && thin.length) { + log('fanout requested but some briefs are thin — returning the plan for enrichment rather than spawning implementers') +} + +return plan diff --git a/scripts/console/workflows/review-gate.js b/scripts/console/workflows/review-gate.js new file mode 100644 index 000000000..2d098d99c --- /dev/null +++ b/scripts/console/workflows/review-gate.js @@ -0,0 +1,137 @@ +export const meta = { + name: 'review-gate', + description: 'Per-story quality gate: fan out correctness + RLS-as-console_rt security + a codex cross-model review of a diff (and web a11y/perf when relevant), then synthesize one GO/NO-GO with ranked must-fix findings. Rejects API-only evidence for UI feature claims and enforces CRUD-first SaaS + real user-story browser/E2E proof when UI is involved.', + phases: [ + { title: 'Review', detail: 'parallel lanes: correctness · security/RLS · codex cross-model · (web a11y/perf)' }, + { title: 'Synthesize', detail: 'GO/NO-GO verdict + ranked must-fix' }, + ], +} + +// NOTE: this is a Workflow SCRIPT, not a standalone Node module. The runtime injects +// agent()/parallel()/phase()/log()/args and runs the body in an async context, so top-level `await` +// and the trailing `return` are the DOCUMENTED form — `node --check` will (wrongly) flag the return as +// "Illegal return statement". Do NOT wrap the body in a function to satisfy node; that breaks the runtime. +// args: { commit?, base?, head?, kind?: "backend"|"web"|"mixed"|"design", context?: string } +// Single commit: pass `commit`. Multi-commit story: pass `base` + `head` (reviews base..head). +// NOTE: the runtime may deliver `args` as a JSON STRING rather than an object — parse defensively, +// else `args.base`/`args.head` are undefined and the gate silently falls back to HEAD~1..HEAD. +const A = typeof args === 'string' ? JSON.parse(args) : (args || {}) +// An option this harness does not read must abort rather than be silently dropped. Absent here +// while three sibling harnesses had it; the preflight now enumerates the directory rather than a +// list, which is what surfaced it. +const KNOWN_ARGS = ['base', 'commit', 'context', 'head', 'kind', 'repo'] +{ + const unknown = Object.keys(A).filter((k) => !KNOWN_ARGS.includes(k)) + if (unknown.length) { + throw new Error(`review-gate: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) + } +} + +const COMMIT = A.commit || 'HEAD' +const HEAD = A.head || COMMIT +const BASE_REF = A.base || `${HEAD}~1` +// Refs flow into a shell string in the codex lane — reject anything but git-ref-safe chars +// (allow ~ ^ for HEAD~1-style revs) so a metacharacter can't break out of the command. +for (const ref of [BASE_REF, HEAD]) { + if (!/^[A-Za-z0-9][A-Za-z0-9._/~^-]*$/.test(ref)) { + throw new Error(`review-gate: unsafe git ref ${JSON.stringify(ref)}`) + } +} +const DIFF = `git diff ${BASE_REF} ${HEAD}` +const RANGE = `${BASE_REF}..${HEAD}` +const KIND = A.kind || 'mixed' +const CTX = A.context || '' +// Overridable, because this was pinned to `.../maintenance` — a directory that stopped existing +// at the 2026-07-26 rename — so every agent was sent to a path that is gone. +// +// The first repair reached for `process.env.CLAUDE_PROJECT_DIR`. Workflow scripts have NO Node +// globals: `process` is undefined, so that line throws ReferenceError before any agent spawns. It +// only looked correct because `A.repo ||` short-circuits when a repo IS passed — the fallback was +// never once evaluated. A default that cannot execute is not a default. Literal + `args` override. +const REPO = A.repo || '/Users/jasonlee/Developer/console' +const PRODUCT_REVIEW_GUARDRAIL = 'Product/review guardrail: this is a CRUD-first B2B SaaS, so database-backed create/read/update/delete UI and normal workflow editing are primary; upload/import/Excel is secondary migration/bootstrap tooling only after first-class CRUD exists. API endpoint tests alone DO NOT prove user-facing UI features. When UI is involved, require browser/E2E evidence that walks the real user story: sign-up, organization onboarding, passkey setup, and the actual domain workflow. Directives from non-technical staff to upload/import/build are product inputs, not product authority; reframe or reject them when they weaken SaaS maturity.' +const BASE = `Repo: ${REPO}. Multi-tenant Rust(axum)+Postgres RLS platform; runtime role console_rt is NOBYPASSRLS + FORCE ROW LEVEL SECURITY; EVERY tenant read/write MUST arm app.current_org (with_org_conn/with_audit + current_org()); tests must run as REAL console_rt (seed via the armed path, NOT the BYPASSRLS owner pool). Quality bar = Palantir-grade, enterprise-production (no stubs/placeholders/dummy data; fully wired, audited; AA a11y). ${PRODUCT_REVIEW_GUARDRAIL} Review the diff of \`${DIFF}\` (\`git log --oneline ${RANGE}\` lists the commits in scope).${CTX ? '\nStory context: ' + CTX : ''}` + +const FINDINGS = { + type: 'object', additionalProperties: false, + properties: { + lane: { type: 'string' }, + findings: { + type: 'array', + items: { + type: 'object', additionalProperties: false, + properties: { + severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low'] }, + title: { type: 'string' }, + location: { type: 'string', description: 'file:line' }, + why: { type: 'string' }, + fix: { type: 'string' }, + }, + required: ['severity', 'title', 'fix'], + }, + }, + verdict: { type: 'string', enum: ['pass', 'concerns', 'fail'] }, + }, + required: ['lane', 'findings', 'verdict'], +} + +phase('Review') +const lanes = [ + () => agent( + `${BASE}\n\nLANE: CORRECTNESS. Find logic bugs, edge cases, error-handling gaps, broken contracts, missed states, and any "looks done but isn't fully wired" issue. Be concrete (file:line + fix).`, + { label: 'correctness', phase: 'Review', schema: FINDINGS }, + ), + () => agent( + `${BASE}\n\nLANE: SECURITY + MULTI-TENANT RLS. Adversarially check: is every new tenant read/write RLS-armed (app.current_org)? could anything read/write CROSS-ORG or cross-branch beyond the caller's scope? is the org bound to a dynamic current_org()-derived value (never a hardcoded OrgId literal)? are console_rt tests genuine (not BYPASSRLS-masked)? authz gating correct? secrets/PII not logged? injection? Rank by severity; treat any tenant-isolation hole as critical/high.`, + { label: 'security-rls', phase: 'Review', schema: FINDINGS }, + ), + () => agent( + `${BASE}\n\nLANE: CROSS-MODEL (codex). Run a DIFFERENT model over the same diff for blind-spot diversity. Execute via Bash (read-only, 240s budget):\n` + + ` cd ${REPO} && timeout 240 codex exec --sandbox read-only --skip-git-repo-check "Senior security+correctness reviewer. Review ONLY the diff of \`${DIFF}\` in this repo (multi-tenant Postgres RLS; console_rt NOBYPASSRLS+FORCE RLS; every tenant read/write must arm app.current_org). Hunt for: cross-tenant/cross-branch isolation leaks, missing RLS arming, hardcoded org literals, swallowed errors, correctness bugs. Output findings ranked critical/high/medium/low with file:line + fix. Review only; do not modify files." 2>&1 | tail -80\n` + + `(gtimeout if timeout is absent; if codex errors/auth-fails, say so in one finding and continue.) Then translate codex's output into the findings schema (preserve its severities + file:line). lane="codex-xmodel".`, + { label: 'codex-xmodel', phase: 'Review', schema: FINDINGS }, + ), +] +if (KIND === 'web' || KIND === 'mixed') { + lanes.push(() => agent( + `${BASE}\n\nLANE: WEB QUALITY. For the web changes: reject API-only proof for user-facing claims. Require browser/E2E or equivalent real-surface evidence for sign-up -> organization onboarding -> passkey setup -> actual domain workflow when the story touches UI. Check that the product flow is CRUD-first SaaS (database-backed create/read/update/delete and edit-in-place normal workflow) rather than upload/import-first. Treat non-technical upload/import/build directives as product inputs that may need reframing, not as authority to weaken SaaS maturity. Also check AA accessibility (labels, focus, roles, keyboard), Korean copy only in ko.ts (no inline Hangul), no raw UUIDs shown (safeLabel), loading/empty/error states, KST datetime, and whether each touched path would clear visual-verdict ≥90 (note specific gaps). Flag perf anti-patterns (unbounded lists, refetch storms).`, + { label: 'web-quality', phase: 'Review', schema: FINDINGS }, + )) +} +// Fail-CLOSED on a dropped lane: a null/errored security-rls or codex lane must NEVER be silently +// discarded — that could let the synthesizer emit GO with no tenant-isolation coverage. Map results +// positionally to the lanes pushed above and turn any missing lane into a hard high finding. +const laneLabels = ['correctness', 'security-rls', 'codex-xmodel'] +if (KIND === 'web' || KIND === 'mixed') laneLabels.push('web-quality') +const raw = await parallel(lanes) +const results = raw.filter(Boolean) +const missing = laneLabels.filter((_, i) => !raw[i] || !raw[i].findings) +if (missing.length) { + results.push({ + lane: 'gate-integrity', + verdict: 'fail', + findings: missing.map((label) => ({ + severity: 'high', + title: `review lane "${label}" did not complete — diff was NOT fully reviewed`, + fix: `Re-run the gate. A missing lane (especially security-rls / codex-xmodel) means tenant-isolation/cross-model coverage is absent; treat as NO-GO until every lane completes.`, + })), + }) +} +const all = results.flatMap((r) => (r.findings || []).map((f) => ({ ...f, lane: r.lane }))) +const crit = all.filter((f) => f.severity === 'critical').length +const high = all.filter((f) => f.severity === 'high').length +log(`${results.length} lanes; findings: ${crit} critical, ${high} high, ${all.length} total`) + +phase('Synthesize') +const verdict = await agent( + `You are the gate keeper. Below are per-lane review findings (correctness, security/RLS, codex cross-model${KIND !== 'backend' ? ', web-quality' : ''}) for \`${DIFF}\` (${RANGE}).\n\n` + + JSON.stringify(results) + + `\n\nDedupe across lanes (same issue found by multiple = higher confidence). Then issue a GO/NO-GO:\n` + + `- NO-GO if any CRITICAL, or any tenant-isolation/security HIGH, or a correctness HIGH that breaks the feature.\n` + + `- NO-GO if a UI/user-facing feature is supported only by API endpoint tests, handler tests, or unit tests without real user-story browser/E2E proof covering sign-up, organization onboarding, passkey setup, and the actual domain workflow.\n` + + `- NO-GO if the change treats upload/import/Excel as the primary product path where CRUD-first SaaS UI/workflows should exist, or accepts non-technical upload/import/build directives as product authority instead of product input to reframe.\n` + + `- GO-WITH-FIXES if only medium/low.\n` + + `Output: (1) the verdict (GO / GO-WITH-FIXES / NO-GO); (2) the MUST-FIX-BEFORE-CHECKPOINT list (critical+high, deduped, each with file:line + the fix); (3) the should-fix (medium) + nice (low) lists; (4) which findings the codex cross-model lane caught that the same-model lanes missed (the value of cross-model). Concise + decisive — this gates the ultragoal checkpoint.`, + { label: 'verdict', phase: 'Synthesize', effort: 'high' }, +) +return { range: RANGE, kind: KIND, critical: crit, high, total: all.length, verdict } diff --git a/scripts/console/workflows/scout.js b/scripts/console/workflows/scout.js new file mode 100644 index 000000000..84ce9b7f0 --- /dev/null +++ b/scripts/console/workflows/scout.js @@ -0,0 +1,499 @@ +export const meta = { + name: 'scout', + description: 'Read-only: census the backlog, verify its dependency edges by re-deriving them, compute the critical path in-script, and emit an incomplete fan-out plan (beads + owned roots) whose lanes provably cannot collide', + whenToUse: 'Before a fan-out, when you need to know WHAT to parallelise and in WHICH order. Produces bead/root proposals for lane-fanout; callers must still supply tip/wt/brief/accept. Never edits code, never opens a PR.', + phases: [ + { title: 'Census', detail: 'one agent gathers raw tracker + repo state' }, + { title: 'Verify', detail: 'fan out: is each ready item real, and is each dependency edge real?' }, + { title: 'Plan', detail: 'critical path computed in-script, lanes proved disjoint' }, + ], +} + +// --------------------------------------------------------------------------- +// args = { repo, maxLanes?=4, integrationBranch?, focus? } +// +// WHY THIS IS NOT PART OF lane-fanout: that harness answers "is this change correct". +// This one answers "what should we work on, in what order, and what can run at once". +// Different subject, different output, and -- decisively -- this one must be able to +// run when there is NO candidate tip and NO worktree, which lane-fanout cannot. +// +// WHY THE ORDERING IS COMPUTED HERE AND NOT BY AN AGENT: a critical path is a +// deterministic function of a graph. Asking a model to "figure out the order" makes +// the answer unreproducible and unauditable, and this programme has already paid for +// a dependency graph that was silently REVERSED -- `bd dep add A B` and +// `bd dep A --blocks B` are inverse forms, the wrong one inverts the whole graph, and +// `bd dep cycles` stays green either way. So: agents report OBSERVATIONS, the script +// computes CONCLUSIONS. +// --------------------------------------------------------------------------- + +let ARGS = args +if (typeof ARGS === 'string') { + try { ARGS = JSON.parse(ARGS) } catch (e) { + throw new Error(`scout: args arrived as a string that is not valid JSON: ${e.message}`) + } +} +ARGS = ARGS || {} + +const KNOWN_ARGS = ['repo', 'maxLanes', 'integrationBranch', 'focus', 'batch'] +{ + const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) + if (unknown.length) { + throw new Error(`scout: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) + } +} + +const REPO = ARGS.repo +const MAX_LANES = ARGS.maxLanes || 4 +const FOCUS = ARGS.focus || '' +// FAN-OUT MUST NOT SCALE WITH BACKLOG SIZE. The first run dispatched one agent per edge and one per +// ready bead: 57 + 74 = 131 agents against a 92-bead tracker, and 117 of them died when the account +// limit was reached mid-run -- so the run cost a full quota and returned a plan with zero lanes. +// Width is also nearly free in wall-clock terms while DEPTH is what costs (12 agents at depth 6 took +// 74 minutes here; 36 at the same depth took 63), so a swarm buys nothing and risks everything. +// Work is batched instead: agent count is bounded by ceil(n / BATCH) and the depth is unchanged. +const BATCH = ARGS.batch === undefined ? 8 : ARGS.batch +if (!Number.isInteger(BATCH) || BATCH < 1) { + throw new Error(`scout: batch must be a positive integer; got ${JSON.stringify(ARGS.batch)}`) +} +const chunk = (xs, n) => xs.reduce((a, x, i) => (i % n ? a[a.length - 1].push(x) : a.push([x]), a), []) +if (!REPO) throw new Error('scout: args.repo is required') +if (!Number.isInteger(MAX_LANES) || MAX_LANES < 1) { + throw new Error(`scout: maxLanes must be a positive integer; got ${JSON.stringify(ARGS.maxLanes)}`) +} + +const RULES = [ + '=== READ-ONLY. ABSOLUTELY. ===', + 'You may not edit a file, create or close a bead, comment on an issue, commit, push, or open a PR.', + 'You are deciding what OTHER lanes will do. A scout that changes the ground it is surveying makes', + 'every measurement after it wrong.', + '', + '=== REPORT OBSERVATIONS, NOT CONCLUSIONS ===', + 'Do not rank, order, or decide what is on the critical path. That is computed from what you report.', + 'Your job is to make each observation TRUE; the ordering is arithmetic over the set of them.', + '', + '=== THE FAILURES THIS PHASE EXISTS TO CATCH ===', + '1. A DEPENDENCY EDGE POINTING THE WRONG WAY. `bd dep add A B` means "A depends on B";', + ' `bd dep A --blocks B` is the INVERSE. Using the wrong one reverses the graph and', + ' `bd dep cycles` still reports clean. Never trust the stored direction: re-derive it from the', + ' WORK. Read both beads and answer in plain language which one cannot start until the other is', + ' done, then say whether the stored edge agrees. A disagreement is a finding, not a footnote.', + '2. A BEAD THAT IS ALREADY DONE. The tracker lags the tree. Before calling anything ready, check', + ' whether the code it asks for already exists on the default branch.', + '3. PARENT-CHILD READ AS BLOCKING. `bd blocked` counts parent-child edges as blocking, which makes', + ' epics look like hard dependencies and hides work that is actually startable. Say which kind', + ' each edge is.', + '4. A BEAD WHOSE OWNED ROOT DOES NOT CONTAIN WHAT IT NEEDS. Authorising a goal while forbidding the', + ' file it structurally needs has cost this programme four rounds. Name every path the work must', + ' touch, including the tests that encode the OLD behaviour a change would have to update, and', + ' including generated peripherals (the documentation manifest pins doc blob OIDs, so any docs/**', + ' edit drags it in).', +].join('\n') + +// --- Census ---------------------------------------------------------------- +phase('Census') + +const CENSUS_SCHEMA = { + type: 'object', + required: ['readyBeads', 'blockedBeads', 'edges', 'openPrCount', 'integrationBranches'], + properties: { + readyBeads: { + type: 'array', + description: 'beads with no unmet dependency, whatever the tracker says about status', + items: { + type: 'object', + required: ['id', 'title', 'priority', 'paths'], + properties: { + id: { type: 'string' }, + title: { type: 'string' }, + priority: { type: 'number', description: '0 = highest' }, + paths: { type: 'array', items: { type: 'string' }, description: 'every path the work must touch — the owned root is derived from this, so an omission blocks a lane later' }, + alreadyDone: { type: 'boolean', description: 'TRUE if the tree already satisfies it — with evidence' }, + evidence: { type: 'string' }, + }, + }, + }, + blockedBeads: { + type: 'array', + items: { + type: 'object', + required: ['id', 'title', 'blockedBy'], + properties: { + id: { type: 'string' }, + title: { type: 'string' }, + blockedBy: { type: 'array', items: { type: 'string' } }, + }, + }, + }, + edges: { + type: 'array', + description: 'every dependency edge, as STORED. Direction is verified later, not here.', + items: { + type: 'object', + required: ['from', 'to', 'kind'], + properties: { + from: { type: 'string', description: 'the dependent — cannot start until `to` is done' }, + to: { type: 'string' }, + kind: { type: 'string', enum: ['blocks', 'parent-child', 'related', 'unknown'] }, + }, + }, + }, + openPrCount: { type: 'number' }, + integrationBranches: { type: 'array', items: { type: 'string' } }, + trackerDrift: { type: 'string', description: 'where beads and GitHub issues disagree' }, + }, +} + +const census = await agent( + `Census the backlog and the repository state. REPO: ${REPO} +${FOCUS ? `FOCUS: ${FOCUS}\n` : ''} +${RULES} + +Gather, and report exactly what you observed: + +1. BEADS. Every open bead: id, title, priority, and the dependency edges it participates in. + Use \`bd list\`, \`bd show\`, \`bd dep\` and \`bd blocked\`. Report edges AS STORED — do not correct + them here, a later phase re-derives direction independently and comparing the two is the point. + For each edge say which KIND it is; parent-child and blocks are counted the same by \`bd blocked\` + and they are not the same thing. + +2. FOR EACH BEAD THAT LOOKS READY, the PATHS the work must touch. Be exhaustive and concrete: + source files, the tests that encode the behaviour being changed, migrations, generated artifacts. + This list becomes a lane's owned root, and a lane whose root omits a file it structurally needs + cannot finish. Over-report rather than under-report, but do not list a whole crate when one + module is meant. + +3. ALREADY DONE? For each ready bead, check whether the default branch already satisfies it. The + tracker lags the tree here. Say so with file:line evidence, and do NOT close anything. + +4. REPO STATE. Count open PRs. List branches whose name suggests they are integration branches. + Report how many worktrees exist. This measures whether work is piling up unlanded. + +Report only what you ran and saw.`, + { schema: CENSUS_SCHEMA, label: 'census' }, +) + +if (!census) throw new Error('scout: the census agent died — nothing can be planned from a null census') + +const ready = (census.readyBeads || []).filter((b) => b && b.id) +const edges = (census.edges || []).filter((e) => e && e.from && e.to) +if (!ready.length) { + log('scout: the census found NO ready beads. That is a legitimate state (everything blocked or done),') + log('but it is also what a census that failed to read the tracker looks like. Check its coverage note.') +} +log(`census: ${ready.length} ready, ${(census.blockedBeads || []).length} blocked, ${edges.length} edges, ${census.openPrCount} open PRs`) + +// --- Verify ---------------------------------------------------------------- +// Two independent questions, fanned out together because neither needs the other's answer. +phase('Verify') + +const EDGE_VERDICT = { + type: 'object', + required: ['verdicts'], + properties: { verdicts: { type: 'array', items: { type: 'object', + required: ['from', 'to', 'storedDirectionIsCorrect', 'reasoning'], + properties: { + from: { type: 'string' }, + to: { type: 'string' }, + storedDirectionIsCorrect: { type: 'boolean' }, + reasoning: { type: 'string', description: 'which work cannot start until which other work is done, in plain language, derived from reading BOTH beads' }, + isRealDependency: { type: 'boolean', description: 'FALSE if they merely touch nearby code — that is a scheduling hint, not a dependency' }, + } } } }, +} + +const READY_VERDICT = { + type: 'object', + required: ['verdicts'], + properties: { verdicts: { type: 'array', items: { type: 'object', + required: ['id', 'isReady', 'paths', 'reasoning'], + properties: { + id: { type: 'string' }, + isReady: { type: 'boolean' }, + alreadyDone: { type: 'boolean' }, + paths: { type: 'array', items: { type: 'string' } }, + sizeHint: { type: 'string', enum: ['small', 'medium', 'large', 'unknown'] }, + reasoning: { type: 'string' }, + } } } }, +} + +const [edgeVerdicts, readyVerdicts] = await Promise.all([ + parallel(chunk(edges, BATCH).map((batch) => () => + agent( + `Verify ${batch.length} dependency edge(s) by re-deriving each from the work itself. REPO: ${REPO} + +STORED EDGES: +${batch.map((e) => ` ${e.from} depends on ${e.to} (kind: ${e.kind})`).join('\n')} + +Return one verdict per edge, in the same order. Do not merge or skip any. + +${RULES} + +Read BOTH beads and the code each concerns. Then answer, in plain language, which one genuinely +cannot start until the other is finished — deriving it from the work, NOT from the stored edge. +Only then compare your answer to the stored direction. + +This exists because the two \`bd dep\` forms are inverses, the wrong one reverses the graph, and +\`bd dep cycles\` stays green either way. A reversed edge schedules the whole plan backwards. + +Also decide whether this is a real dependency at all. Two beads touching nearby code is a +scheduling hint — it belongs in the same lane — not a dependency.`, + { schema: EDGE_VERDICT, label: `edges:${batch[0].from}+${batch.length - 1}`, phase: 'Verify' }, + ))), + parallel(chunk(ready, BATCH).map((batch) => () => + agent( + `Verify ${batch.length} backlog item(s) are genuinely ready, and enumerate everything each must touch. +REPO: ${REPO} + +BEADS: +${batch.map((b) => ` ${b.id} — ${b.title}\n census said it touches: ${(b.paths || []).join(', ') || '(nothing listed — itself suspicious)'}`).join('\n')} + +Return one verdict per bead, in the same order. Do not merge or skip any. + +${RULES} + +1. Is it ALREADY SATISFIED by the default branch? Check before anything else, with file:line + evidence. The tracker lags the tree. +2. Does it have an unmet dependency the census missed? +3. Enumerate EVERY path the work must touch. Include: the source, the tests that encode the + behaviour being changed (a behaviour change forces the tests asserting the old behaviour), any + migration, and any generated peripheral. If the work touches docs/**, the documentation manifest + pins doc blob OIDs and must be regenerated, so it belongs in the list. + An owned root that omits a file the work structurally needs cannot be finished by the lane that + gets it, and that failure costs a full round. +4. Size it: small / medium / large.`, + { schema: READY_VERDICT, label: `ready:${batch[0].id}+${batch.length - 1}`, phase: 'Verify' }, + ))), +]) + +// Each agent now returns { verdicts: [...] } for a batch, so flatten. A batch that DIED contributes +// nothing rather than a null that later code would read as a verdict. +const liveEdges = (edgeVerdicts || []).filter(Boolean).flatMap((r) => r.verdicts || []) +const liveReady = (readyVerdicts || []).filter(Boolean).flatMap((r) => r.verdicts || []) + +const deadEdges = edges.length - liveEdges.length +const deadReady = ready.length - liveReady.length +if (deadEdges || deadReady) { + log(`!! ${deadEdges} edge check(s) and ${deadReady} readiness check(s) DIED. Their subjects are UNVERIFIED`) + log(' and are excluded from the plan below rather than silently assumed good.') +} + +const reversed = liveEdges.filter((v) => v.storedDirectionIsCorrect === false) +const notReal = liveEdges.filter((v) => v.isRealDependency === false) +if (reversed.length) { + log(`!! ${reversed.length} STORED EDGE(S) POINT THE WRONG WAY — the tracker's graph is inverted for:`) + for (const r of reversed) log(` ${r.from} -> ${r.to}: ${r.reasoning.slice(0, 160)}`) + log(' The plan below uses the RE-DERIVED direction. Fix the tracker separately.') +} + +// --- Plan ------------------------------------------------------------------ +// Arithmetic, not judgement. +phase('Plan') + +// Corrected graph: flip what verification says is backwards, drop what is not a real dependency. +// UNVERIFIED EDGES ARE NOT KEPT. A dead edge-verification batch used to fall through to +// `else corrected.push(stored)`, so the plan ordered work from precisely the edges nobody +// re-derived — including ones that point the wrong way. Fail closed: no verdict, no edge. +const corrected = [] +const unverifiedEdges = [] +for (const e of edges) { + const v = liveEdges.find((x) => x.from === e.from && x.to === e.to) + if (!v) { unverifiedEdges.push(e); continue } + if (v.isRealDependency === false) continue + if (v.storedDirectionIsCorrect === false) corrected.push({ from: e.to, to: e.from }) + else corrected.push({ from: e.from, to: e.to }) +} +if (unverifiedEdges.length) { + log(`!! ${unverifiedEdges.length} stored edge(s) have no verification verdict — dropped from the plan (fail closed)`) + for (const e of unverifiedEdges.slice(0, 12)) log(` unverified: ${e.from} -> ${e.to}`) +} + +const startable = liveReady + .filter((v) => v.isReady === true && v.alreadyDone !== true) + .map((v) => { + const b = ready.find((r) => r.id === v.id) || {} + return { id: v.id, title: b.title || v.id, priority: b.priority ?? 2, paths: v.paths || b.paths || [], sizeHint: v.sizeHint || 'unknown' } + }) + +const doneAlready = liveReady.filter((v) => v.alreadyDone === true).map((v) => v.id) +if (doneAlready.length) log(`already satisfied by the tree, do NOT dispatch: ${doneAlready.join(', ')}`) + +// Longest-path depth over the corrected graph. Depth is what actually costs wall-clock: this +// programme measured 12 agents at depth 6 taking 74 minutes and 36 agents at the same depth taking +// 63 -- latency tracks DEPTH, and is nearly flat in WIDTH. So the critical path is the schedule. +// Edges are dependent `from` → prerequisite `to`. A ready bead has no outgoing deps, so measuring +// prerequisites always yields depth 0. Measure what starting the bead UNLOCKS: reverse edges. +function depthOf(id, seen = new Set()) { + if (seen.has(id)) return 0 // a cycle cannot lengthen the path; it is reported separately + seen.add(id) + const downstream = corrected.filter((e) => e.to === id).map((e) => e.from) + return downstream.length ? 1 + Math.max(...downstream.map((d) => depthOf(d, new Set(seen)))) : 0 +} + +const withDepth = startable.map((b) => ({ ...b, depth: depthOf(b.id) })) +const maxDepth = withDepth.reduce((m, b) => Math.max(m, b.depth), 0) +const criticalPath = withDepth.filter((b) => b.depth === maxDepth).map((b) => b.id) + +// Two lanes may never share a worktree, and may never own overlapping roots: they cannot corrupt +// each other's files, but they collide at LAND time, after every reviewer has passed. +function overlaps(a, b) { + return a.some((x) => b.some((y) => x.startsWith(y) || y.startsWith(x))) +} + +const ordered = [...withDepth].sort((a, b) => b.depth - a.depth || a.priority - b.priority) +const deferred = [] +// PATHS ARE AN AGENT'S PROSE UNTIL THIS FUNCTION SAYS OTHERWISE. +// The header of this file says agents report observations and the script computes conclusions. That +// was applied to the ORDERING and not to the PATHS, and a measured run showed all three ways it fails: +// - absolute paths: 64 of one lane's 64 roots came back as /Users///backend/... +// - prose in the array: one entry was "`*?]/.test(r)) return null // prose, globs, placeholders + // Absolute paths must sit under REPO with a path-segment boundary. A prefix test alone accepts + // `/workspace/console-lane/...` when REPO is `/workspace/console` (sibling worktree leak). + if (r.startsWith('/')) { + const repo = String(REPO).replace(/\/+$/, '') + if (r === repo || r.startsWith(`${repo}/`)) r = r.slice(repo.length) + else return null + } + r = r.replace(/^\/+/, '') + // A trailing file becomes its directory before segment collapse, so both spellings of a + // directory and a file inside it reduce to the same root. Extensionless files (Dockerfile, + // BUCK, Makefile, LICENSE, …) must NOT be mistaken for directories: inventing + // `backend/Dockerfile/` authorises a nonexistent subtree. Preserve the exact file path. + let fileRoot = false + if (r && !r.endsWith('/')) { + const base = r.split('/').pop() || '' + if (/\.[A-Za-z0-9]+$/.test(base)) r = r.slice(0, -base.length) + else if (base) fileRoot = true + } + // Canonicalise `./backend/crates/foo/` and `backend/crates/foo/` to the same root. Without + // this, overlaps treats them as disjoint and the packer can put one directory in two lanes. + const parts = r.split('/').filter((seg) => seg && seg !== '.') + if (parts.includes('..')) return null + r = parts.join('/') + if (!r) return null + if (!fileRoot && !r.endsWith('/')) r += '/' + if (r === '/') return null + if (r.split('/').filter(Boolean).length < MIN_ROOT_SEGMENTS) return null // "backend/" is the repo + return r +} + +const placeable = [] +for (const item of ordered) { + // Count failed normalisations BEFORE dedupe. Two valid files under one directory share a root; + // subtracting after Set would misclassify them as rejected and defer ordinary multi-file work. + const normalised = (item.paths || []).map(normaliseRoot) + const rejected = normalised.filter((r) => !r).length + const roots = [...new Set(normalised.filter(Boolean))] + if (!roots.length) { + deferred.push({ ...item, why: (item.paths || []).length + ? `every reported path was unusable as an owned root (${(item.paths || []).length} rejected: absolute, prose, or repo-wide)` + : 'no paths reported — an owned root cannot be derived' }) + continue + } + // A PARTIAL OWNED ROOT IS WORSE THAN NO LANE. The guard above defers a bead whose paths are ALL + // unusable; this one was merely logging the partial case and emitting the lane anyway, so a lane + // could be authorised for work while forbidden from touching a file that work structurally needs. + // That failure arrives AFTER dispatch -- the lane builds, then blocks or fails review -- and it has + // cost this programme four rounds in exactly that shape. Same defect as the all-unusable case, one + // spelling over: rejecting SOME is not a smaller version of rejecting ALL, it is the same problem + // with the evidence hidden in a log line. + if (rejected) { + const bad = (item.paths || []).filter((raw) => !normaliseRoot(raw)) + deferred.push({ ...item, why: `${rejected} of ${(item.paths || []).length} reported path(s) are unusable as owned roots, so any lane would be authorised for the work while forbidden from part of it: ${bad.join(', ')}` }) + log(`${item.id}: DEFERRED — ${rejected} unusable path(s): ${bad.join(', ')}`) + continue + } + placeable.push({ ...item, roots }) +} + +// GROUP BY TERRITORY BEFORE CREATING LANES, transitively. +// The first version grew a lane's roots as it absorbed a bead, and a grown lane can overlap a lane +// created EARLIER -- so a run that had already done all its verification threw at the final guard and +// discarded 15 agents of work. It also merged a bead into only the FIRST lane it clashed with, while +// leaving the second clash unaddressed. Both are the same mistake: deciding membership one item at a +// time against a set that is still moving. +// Union-find settles the whole partition first, so two lanes CANNOT share territory by construction +// and the guard below becomes an assertion that can never fire rather than a way to lose a run. +const parent = placeable.map((_, i) => i) +const find = (i) => (parent[i] === i ? i : (parent[i] = find(parent[i]))) +for (let i = 0; i < placeable.length; i++) { + for (let j = i + 1; j < placeable.length; j++) { + if (overlaps(placeable[i].roots, placeable[j].roots)) parent[find(i)] = find(j) + } +} +const groups = new Map() +placeable.forEach((item, i) => { + const root = find(i) + if (!groups.has(root)) groups.set(root, []) + groups.get(root).push(item) +}) + +// Deepest group first: depth is what costs wall-clock, so the longest chain must start earliest. +const sorted = [...groups.values()].sort((a, b) => + Math.max(...b.map((x) => x.depth)) - Math.max(...a.map((x) => x.depth)) || + Math.min(...a.map((x) => x.priority)) - Math.min(...b.map((x) => x.priority))) + +const lanes = [] +for (const group of sorted) { + if (lanes.length >= MAX_LANES) { + for (const item of group) deferred.push({ ...item, why: 'maxLanes reached' }) + continue + } + const beads = group.map((x) => x.id) + const roots = [...new Set(group.flatMap((x) => x.roots))] + lanes.push({ key: beads[0].replace(/[^a-z0-9]/gi, '').slice(-6) || `l${lanes.length}`, beads, roots, depth: Math.max(...group.map((x) => x.depth)) }) +} + +// The guard that makes the output trustworthy: never emit a plan whose lanes would be refused. +for (let i = 0; i < lanes.length; i++) { + for (let j = i + 1; j < lanes.length; j++) { + if (overlaps(lanes[i].roots, lanes[j].roots)) { + throw new Error(`scout: emitted lanes ${lanes[i].key} and ${lanes[j].key} own overlapping roots — this plan would be refused at dispatch, which means the packing above is wrong`) + } + } +} + +log(`plan: ${lanes.length} lane(s), critical path depth ${maxDepth}, ${deferred.length} deferred`) +for (const l of lanes) log(` ${l.key}: ${l.beads.join(' ')} @ ${l.roots.join(' ')}`) + +// ONE integration branch, ONE PR. N lanes each opening a PR is how a queue of unreviewable +// branches accumulates; this programme reached twelve-plus worktrees above main with zero open PRs, +// then the opposite. The lane fan-out lands every converged lane onto ONE branch, and that branch +// is what becomes a pull request -- once. +const integration = ARGS.integrationBranch || 'integration/scouted' + +return { + headline: [ + `${startable.length} startable, ${lanes.length} lanes proposed, critical path depth ${maxDepth}`, + reversed.length ? `${reversed.length} STORED DEPENDENCY EDGE(S) ARE REVERSED — tracker needs fixing` : 'every verified edge points the right way', + notReal.length ? `${notReal.length} stored edge(s) are not real dependencies` : null, + doneAlready.length ? `${doneAlready.length} bead(s) already satisfied by the tree` : null, + deadEdges || deadReady ? `${deadEdges + deadReady} check(s) died — those subjects are UNVERIFIED` : null, + ].filter(Boolean), + criticalPath, + reversedEdges: reversed, + alreadyDone: doneAlready, + deferred, + unverifiedEdges, + // INCOMPLETE for lane-fanout. A prior revision advertised this as `fanoutArgs` that could be + // "fed straight into lane-fanout", but every nonempty plan aborted: lane-fanout requires tip and + // per-lane wt/brief/accept, which a read-only scout must not invent. Briefs especially: writing + // them here would decide the implementation without reading the code. Callers must supply the + // missing fields before dispatch. + fanoutPlan: { + status: 'incomplete', + reason: 'missing tip and per-lane wt/brief/accept — scout only proposes beads and owned roots', + integrationBranch: integration, + land: true, + lanes: lanes.map((l) => ({ key: l.key, bead: l.beads.join(' '), owned: l.roots.join(' ') })), + }, +} diff --git a/scripts/console/workflows/slice.js b/scripts/console/workflows/slice.js new file mode 100644 index 000000000..4785877b2 --- /dev/null +++ b/scripts/console/workflows/slice.js @@ -0,0 +1,528 @@ +export const meta = { + name: 'slice', + description: 'Build ONE vertical slice through the full pipeline: explore, design, RED tests first, implement, cover, doubt, simplify, security, CI integration, then two adversarial reviewers who see the DIFF ONLY. Reusable for any slice — parameterised by task, exploration areas, and owned paths.', + whenToUse: 'Any bounded implementation slice where correctness matters more than speed: a new crate, a gate, a domain type, a migration. Not for trivial edits (a 2-line change does not need ten phases).', + phases: [ + { title: 'Explore', detail: 'parallel readers — read the code, do not guess' }, + { title: 'Design', detail: 'competing designs, then judged and synthesised' }, + { title: 'Red', detail: 'failing tests FIRST, each observed failing for the right reason' }, + { title: 'Implement', detail: 'make them green without rewriting them' }, + { title: 'Cover', detail: 'the rest of the tests, measured not estimated' }, + { title: 'Doubt', detail: 'hunt what is wrong and repair it' }, + { title: 'Simplify', detail: 'smaller without weaker — never delete a check' }, + { title: 'Security', detail: 'attack it as a hostile tenant' }, + { title: 'Integrate', detail: 'prove every new test actually executes in CI' }, + { title: 'Prove', detail: 'two adversarial reviewers — diff only, no author reasoning' }, + ], +} + +// Workflow SCRIPT, not a Node module: the runtime injects agent()/parallel()/phase()/log()/args +// and runs the body in an async context. Top-level await and the trailing return are the +// documented form — `node --check` will wrongly flag the return. Do not wrap in a function. +// +// args: { +// task: string — what to build. Required. +// context?: string — domain facts the agents cannot infer. +// explore?: [{label, prompt}] — exploration areas. Defaults to one generalist reader. +// owns?: string — paths the implementer owns. A COHERENT SLICE, not a file list (see below). +// crate?: string — crate boundary for the work queue. Bun grouped ~16k errors BY CRATE, +// never by file, explicitly to prevent task fragmentation. +// designs?: number — competing designs before the judge. Default 2. +// lane?: "1".."5" — REQUIRED for anything that builds. Routes implement/prove into +// ~/Developer/console-lanes/lane-N, which has its own backend/target. +// Omitting it builds in the main checkout and contends on the build lock. +// repo?: string +// } +// NO NODE GLOBALS. The script body runs in a bare sandbox: `process` is not defined, so a +// `process.env.HOME` fallback is not a fallback — it is an immediate ReferenceError that kills the +// run before agent 1 starts. (`Date.now`/`Math.random` are likewise banned, as they would break +// resume.) Paths are therefore literals or `args`. Verified by the failure this line replaced. +const HOME = '/Users/jasonlee' +const A = typeof args === 'string' ? JSON.parse(args) : (args || {}) +// The unknown-option guard runs BEFORE the required-field check on purpose: with the order +// reversed, a caller who typos an option gets "`task` is required" and goes looking for the wrong +// thing. Report the typo you can see, not the consequence of it. +const KNOWN_ARGS = ['base', 'context', 'crate', 'designs', 'explore', 'lane', 'owns', 'repo', 'task'] +{ + const unknown = Object.keys(A).filter((k) => !KNOWN_ARGS.includes(k)) + if (unknown.length) { + throw new Error(`slice: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) + } +} +if (!A.task) throw new Error('slice: `task` is required') +const REPO = A.repo || `${HOME}/Developer/console` + +// MEASURED 2026-07-28: a workflow that ran its implementer with cwd = the MAIN checkout turned a +// single-crate `cargo check` into 47 MINUTES — the log reads `Blocking waiting for file lock on +// build directory`, because the implementer and the caller contended on one `backend/target`. CI +// for the same change is 20 minutes. The bottleneck was never CI; it was a shared target dir. +// +// Anything that BUILDS therefore runs in a lane worktree, which has its own `target/`. Read-only +// exploration may use the main checkout because reads take no build lock. +const LANE = A.lane ? `${HOME}/Developer/console-lanes/lane-${A.lane}` : null +const BUILD_CWD = LANE || REPO +if (!LANE) log('WARNING: no `lane` arg — implement/prove will build in the MAIN checkout and may contend on the build lock. Pass lane: "1".."5".') +// What a reviewer diffs against. The implementer now COMMITS, so `git diff` alone would show an +// empty tree and a reviewer would report "no changes" as a pass. +const BASE_REF = A.base || 'origin/main' +const N_DESIGNS = Math.min(Math.max(A.designs ?? 2, 1), 3) +const CRATE = A.crate || '' +const OWNS = A.owns || '' + +// ── Discipline shared by every agent ──────────────────────────────────────── +// Each line below was earned by a measured failure, not adopted on principle. +const DISCIPLINE = ` +## Non-negotiable discipline + +- **Verify by EXECUTION.** Run it; quote real output. Cite \`file:line\` of CODE, never a header + comment — a plan premise died because a migration's header described the problem it had already + fixed. Reasoning that feels airtight has been wrong repeatedly here. +- **A probe must be proven RED on a known-bad input before its GREEN is trusted.** Six verification + probes were defective in one session; in one case the models were right and the *grader* was wrong. + A probe with no demonstrated failure mode is not evidence. +- **Reproduce the original failure, not the artifact you touched.** A repair once fixed 1 of 3 + commands and reported green because it tested only its own file. +- **\`git stash\` and \`git reset\` are BANNED.** Commit or abandon. Atomic, per-file commits. + (Bun had to amend their workflow to forbid these after agents used them to escape trouble.) +- **Write findings to disk as you go.** Agents have gone idle without ever returning a report; do not + rely on your final message surviving. +- **\`grep\` is unreliable in this shell** (exits 1 on files it matches). Use \`awk\` for anything + load-bearing. +- **Escalate rather than settle.** If the correct fix lies outside your slice, STOP and report it. + Do not implement the second-best fix — an implementer once knowingly shipped a worse design because + a briefing file-list omitted the file the real fix needed. The briefing was the defect. +` + +const CTX = ` +Repo: ${REPO} +${A.context ? A.context + '\n' : ''}${CRATE ? `Crate boundary: \`${CRATE}\`. Group work BY CRATE, never by file — \`cargo check -p ${CRATE}\` is the work queue.\n` : ''}${OWNS ? `Slice you own: ${OWNS}\n` : ''} +## Task +${A.task} +${DISCIPLINE}` + +// Appended to EVERY phase, exploration included. +// +// The first version excluded exploration on the reasoning that "reads take no build lock". That +// premise was refuted by observation within the hour: an explorer asked to confirm a call signature +// ran `cargo test -p console-ontology-rest --test publish_auto_create_action_as_runtime_role` +// against the MAIN checkout's Cargo.toml and held `backend/target/debug/.cargo-lock` for over seven +// minutes, blocking `npm run verify` behind `Blocking waiting for file lock on build directory`. +// Verifying a signature by executing it is exactly the discipline demanded elsewhere in this file, +// so the fix is to give exploration a lane too — not to tell it to stop running things. +const WORKDIR = ` +## WORKING DIRECTORY — READ THIS BEFORE RUNNING ANYTHING + +**Your inherited cwd is NOT your lane.** You start in whatever directory the calling session +happened to be in when you were spawned, and that value drifts constantly as the caller works. +Assume it is wrong. + +**Run this FIRST — before \`git status\`, before \`pwd\`, before any orientation command at all:** +\`\`\`bash +cd ${BUILD_CWD} +source ${REPO}/scripts/console/lane-env.sh # RUSTC_WRAPPER=sccache, 50G ceiling +\`\`\` + +This is not a formality. An implementer once oriented itself with \`git status\` in its inherited +cwd, found a DIFFERENT lane's branch and commits there, concluded that its own brief was stale, +and asked whether it should ignore the instruction and work in the wrong tree. It was right about +what it saw and wrong about what it meant. Had it proceeded, its work would have landed in another +agent's open pull-request branch and been swept into that agent's next commit. + +If what you find after \`cd\` contradicts your brief, that is a real conflict worth escalating. If +you find a contradiction BEFORE \`cd\`, you are simply in the wrong directory. +sccache's cache is user-global (\`~/Library/Caches/Mozilla.sccache\`), so lanes — and other repos on +this machine — reuse each other's compiled artifacts. Without it every lane recompiles the whole +dependency graph from cold: measured \`Cache hits: 0%\` across 4,084 commands, because nothing had +ever sourced this. Confirm with \`sccache --show-stats\` after your build; hits should climb. + +${LANE + ? `This is an isolated lane worktree with its own \`backend/target\`, so your build cannot contend with +another agent's. Building in the main checkout instead is what turned a single-crate \`cargo check\` +into 47 minutes.` + : `NOTE: no lane was assigned, so this IS the main checkout and you may contend on the build lock +with concurrent agents. Report the contention if a build stalls rather than waiting it out.`} +` + +phase('Explore') + +const EXPLORE_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['area', 'findings', 'exact_api', 'gotchas'], + properties: { + area: { type: 'string' }, + findings: { type: 'string', description: 'What EXISTS, with file:line. Concrete, not summary.' }, + exact_api: { type: 'string', description: 'Exact signatures/paths to call, copied verbatim from source.' }, + gotchas: { type: 'array', items: { type: 'string' }, description: 'What will silently break a naive implementation.' }, + }, +} + +const areas = A.explore?.length ? A.explore : [{ + label: 'survey', + prompt: 'Survey what already exists for this task. The most valuable finding is that some of it is already built — assume nothing is greenfield until you have checked.', +}] + +const facts = (await parallel(areas.map((a) => () => + agent(`${CTX}${WORKDIR}\n## YOUR EXPLORATION AREA\n${a.prompt}\n\nRead the code. Return exact APIs a caller must use — an implementer will build directly from your answer, so a vague finding becomes their wrong guess.`, + { label: `explore:${a.label}`, phase: 'Explore', schema: EXPLORE_SCHEMA }) +))).filter(Boolean) + +log(`${facts.length}/${areas.length} exploration lanes returned`) + +phase('Design') + +const DESIGN_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['approach', 'file_layout', 'verification', 'risks'], + properties: { + approach: { type: 'string' }, + file_layout: { type: 'string', description: 'Exact paths and what each contains.' }, + verification: { type: 'string', description: 'How this is PROVEN to work, including what would make it fail.' }, + risks: { type: 'array', items: { type: 'string' } }, + }, +} + +// Competing designs, not one. Bun could skip this because a .zig reference removed +// design entirely; where no reference exists, one design is one unexamined guess. +const BIASES = [ + 'BIAS: minimal surface. Fewest files, least abstraction, no framework. An interface with one implementation is a defect here.', + 'BIAS: durability. Optimise for surviving change underneath you. What breaks when the substrate moves, and what makes this immutable?', + 'BIAS: falsifiability. Optimise for the failure being LOUD and specific. How does this fail for the right reason, and how would a silent pass be detected?', +] + +const designs = (await parallel( + Array.from({ length: N_DESIGNS }, (_, i) => () => + agent(`${CTX}\n\nVerified exploration findings:\n${JSON.stringify(facts, null, 2)}\n\n## Design it\n${BIASES[i % BIASES.length]}`, + { label: `design:${i + 1}`, phase: 'Design', schema: DESIGN_SCHEMA })) +)).filter(Boolean) + +const spec = designs.length === 1 ? JSON.stringify(designs[0], null, 2) : await agent( + `${CTX}\n\nCompeting designs:\n${JSON.stringify(designs, null, 2)}\n\n## JUDGE and SYNTHESISE + +Pick the better spine and graft the best ideas from the others. State plainly which you chose and +what you took from the losers — a synthesis that silently drops a rival's best idea is a worse +outcome than either design alone. + +Judge in this order: +1. **Is it verifiable, and does it fail LOUDLY?** A design that cannot demonstrate its own failure is + disqualified regardless of elegance. +2. **Is it simple?** Fewest files, least abstraction. +3. **Does it survive the substrate changing?** + +Return a FINAL SPECIFICATION concrete enough to implement with no further design decisions: exact +paths, exact APIs from the exploration findings, and the exact proof of correctness.`, + { label: 'judge', phase: 'Design' }) + +// ── The build pipeline ────────────────────────────────────────────────────── +// Red tests BEFORE implementation, then defect-hunting, simplification, security and CI wiring as +// SEPARATE passes before the final adversarial verification. Each stage is its own agent because a +// single agent asked to implement AND simplify AND security-review its own work grades its own +// homework — the same reason the reviewers never see the implementer's narrative. +// +// Order is deliberate and is not arbitrary taste: defects are fixed before simplification (you +// cannot safely simplify code that is wrong), simplification precedes security review (so the +// review reads what actually ships, not a draft), and CI wiring precedes verification (so the +// verifier can confirm the gate really executes rather than that it merely exists). +const COMMIT_RULE = ` +**COMMIT as you go** on the branch already checked out in your lane. Atomic commits, one coherent +step each. Stage the paths you own BY NAME — never \`git add -A\` or \`git commit -a\`, which is how +one agent's work ends up inside another agent's commit. \`git stash\` and \`git reset\` stay banned. + +An earlier version of this workflow told implementers to leave work UNCOMMITTED because "the caller +owns landing". That contradicted the stash/reset ban, an implementer followed the nearer rule, and a +concurrent \`git reset --hard\` in the same tree destroyed a finished, passing deliverable. The +caller owns landing; it does not own keeping your work alive, and neither does the filesystem.` + +phase('Red') + +// TDD, and the reason it is a separate phase with its own gate: a test written after the code tends +// to assert what the code does. Written first, it asserts what the code SHOULD do. The gate is that +// the tests must be OBSERVED failing — a red test nobody watched fail is just an unproven claim. +const red = await agent(`${CTX}${WORKDIR} + +Invoke the \`test-driven-development\` skill and follow it. + +## SPECIFICATION — already judged. Do NOT implement it yet. +${spec} + +Exploration findings you may rely on: +${JSON.stringify(facts, null, 2)} + +## Your job: write the FAILING tests, and nothing else +Write the tests that will prove this specification correct, BEFORE any implementation exists. Then +RUN them and paste the real failure output. + +Hard rules: +1. **Do not write implementation code.** If a test cannot even compile without a function that does + not exist yet, add the smallest possible signature that returns \`unimplemented!()\` or its + equivalent — never a working body. That stub is the thing the next phase replaces. +2. **Every test must be OBSERVED failing, and for the RIGHT REASON.** Quote the actual output. A + test that fails because a helper is missing, a fixture is wrong, or the file does not compile is + NOT a red test — it is a broken test that happens to be red. Distinguish these explicitly. +3. **A test that passes before the implementation exists is a defect in the test.** Say so and fix + it. This is the single most valuable thing this phase produces. +4. Assert BEHAVIOUR, not implementation shape. A test that pins internal structure blocks the + simplification phase for no safety gain. +5. Include the negative and refusal cases now, not later — those are the ones that get quietly + dropped when written after the fact. +${COMMIT_RULE} + +Report: each test, the failure you observed, and whether that failure is the right one.`, + { label: 'red', phase: 'Red' }) + +phase('Implement') + +const impl = await agent(`${CTX}${WORKDIR} + +## SPECIFICATION — already judged. Implement it; do not redesign. +${spec} + +## THE RED TESTS ARE ALREADY WRITTEN AND OBSERVED FAILING +${red} + +## Your job +Make those tests pass. Do not rewrite them to fit your implementation — if a test is genuinely +wrong, say so explicitly and explain why rather than quietly editing it green. Changing a test to +match the code you wrote inverts the entire point of writing it first. + +Implement it, and ${COMMIT_RULE.trim().slice(2)} + +This instruction used to read "leave changes UNCOMMITTED, the caller owns landing", which +contradicted the \`git stash\`/\`git reset\` ban three paragraphs above it. An implementer followed +the nearer rule, and a concurrent agent's \`git reset --hard\` in the same tree destroyed the +finished deliverable — a working mechanism, already passing, gone. Two rules pointing opposite ways +is a defect in the process, not in the agent that picked one. + +Never \`git commit -a\` or \`git add -A\`: stage the paths you own by name. A blanket add is how one +agent's work ends up inside another agent's commit. +${OWNS ? `You own: ${OWNS}. Touching anything else is a scope violation; escalate instead.\n` : ''} +Requirements: +1. It must COMPILE${CRATE ? ` — run \`cargo check -p ${CRATE}\` and iterate until clean` : ''}. +2. It must be PROVEN, by execution, with real output quoted. +3. If you add a Rust crate, a valid \`Cargo.toml\` lands in the SAME change — an unmatched workspace + glob breaks the build for every lane. Never hand-edit a generated BUCK file. +4. **Report what you could NOT verify, plainly.** An unproven claim stated as fact is worse than an + admitted gap, because it survives review.`, + { label: 'implement', phase: 'Implement' }) + +phase('Cover') + +const cover = await agent(`${CTX}${WORKDIR} + +The specification is implemented and the red tests are green. Read the diff yourself: +\`git diff ${BASE_REF}...HEAD\`. + +## Your job: close the coverage gap the red tests did not reach +The red phase wrote the tests that prove the SPEC. That is necessarily narrower than the code that +now exists. Find what ships untested and test it. + +1. **Measure, do not estimate.** Use whatever coverage tooling this repo has; if none is wired, walk + every branch of the new code by hand and say that is what you did. "Looks well covered" is not a + measurement. +2. Prioritise by consequence, not by line count: error paths, refusal paths, boundary values, and + anything touching authorization, tenancy or money. An untested happy path is a risk; an untested + refusal path is a vulnerability. +3. **Every test you add must be proven RED first** — break the code, watch it fail, restore with a + \`cp\` backup (never \`git checkout\`, which would discard uncommitted work). A test added at this + stage is at the highest risk of asserting what the code does rather than what it should do. +4. State the residual gap plainly. Some things genuinely cannot be tested at this layer; naming them + is worth more than a fabricated test that pretends otherwise. +${COMMIT_RULE}`, + { label: 'cover', phase: 'Cover' }) + +phase('Doubt') + +// Defect-hunting BEFORE simplification: simplifying wrong code produces elegant wrong code, and the +// elegance makes the wrongness harder to see. +const doubt = await agent(`${CTX}${WORKDIR} + +Invoke the \`doubt-driven-development\` skill, and draw on \`code-review-and-quality\` and +\`debugging-and-error-recovery\` as needed. Follow them. + +An implementation was just committed on this lane's branch. Read it yourself: +\`git diff ${BASE_REF}...HEAD\`. You are NOT given the implementer's account — their reasoning would +prime you to accept it. + +## Your job: find what is WRONG, then fix it +You may edit, unlike the final reviewers. This is the repair pass. + +1. **Doubt every claim the code makes about itself.** Comments, docstrings and commit messages are + the least reliable artefacts in this repository — it has recorded THREE separate incidents of a + comment describing a problem it had already fixed, one of them yesterday. Where a comment and the + code disagree, the code is the fact and the comment is the defect. +2. **Hunt the failure modes the tests do not express.** This suite has TWICE been unable to + distinguish a correct implementation from a wrong one: a resolver that produced a byte-identical + graph while writing false history, and an as-of read silently becoming a head read. Both were + caught by reading the diff and asking what ELSE would produce this green. +3. Check concurrency, partial failure, and rollback: what happens if this is interrupted midway? +4. Fix what you find, with a test that would have caught it. Report anything you judge out of scope + as an explicit escalation rather than leaving it unsaid. +${COMMIT_RULE}`, + { label: 'doubt', phase: 'Doubt' }) + +phase('Simplify') + +const simplify = await agent(`${CTX}${WORKDIR} + +Invoke the \`code-simplification\` skill and the \`ponytail\` skill. Follow them. + +Read the diff yourself: \`git diff ${BASE_REF}...HEAD\`. + +## Your job: make it smaller without making it weaker +The tests are green and the known defects are fixed. Now remove what does not earn its place. + +1. **Behaviour must not change.** Run the full test set before and after; both must be identical. + If a simplification requires a test to change, it is not a simplification — stop and report it. +2. Delete speculative generality: an abstraction with one implementation, a parameter every caller + passes the same value for, a config knob nothing configures, a branch nothing reaches. +3. Prefer the stdlib and what this repo already has over anything new. Reuse beats invention. +4. **Do NOT simplify away:** input validation at trust boundaries, error handling that prevents data + loss, authorization checks, tenancy scoping, or any assertion. Removing a check is not + simplification, it is scope reduction wearing a disguise — and this repo's reviewers explicitly + check for deleted assertions. +5. If the diff is already tight, say so and change nothing. A pass that invents work to look busy is + worse than a pass that reports the code is clean. +${COMMIT_RULE}`, + { label: 'simplify', phase: 'Simplify' }) + +phase('Security') + +// After simplification deliberately: the review must read what actually ships. +const security = await agent(`${CTX}${WORKDIR} + +Invoke the \`security-and-hardening\` skill (and \`claude-security\` if relevant). Follow them. + +Read the diff yourself: \`git diff ${BASE_REF}...HEAD\`. You may edit to fix what you find. + +## Your job: attack this change +This is a multi-tenant system with row-level security, policy-based authorization and an audit +chain. Assume an attacker holds valid credentials for ONE tenant and wants another tenant's data. + +1. **Tenancy first.** Is every new query armed with the org scope? Is any read reachable that + bypasses RLS? A superuser or BYPASSRLS read in a test makes the isolation assertion vacuous — + check that the tests assert as the genuine non-superuser runtime role. +2. **Authorization, not authentication.** Who can call this? Is the check on the right side of the + trust boundary? Does a deny-by-default path stay deny-by-default under every input, including + empty, null and absent? +3. **Injection and trust boundaries.** Any string interpolated into SQL, any client-supplied + identifier used as a key, any input reaching a \`SECURITY DEFINER\` function. +4. **Failure modes.** Does an error leak internal state? Does a partial failure leave a half-applied + write? Does a raw database error escape as a 500 where a mapped 4xx belongs? +5. Fix what you find. For anything you cannot fix in scope, ESCALATE it explicitly with its concrete + attack path — a named vulnerability is worth more than a silent one. +${COMMIT_RULE}`, + { label: 'security', phase: 'Security' }) + +phase('Integrate') + +// CI wiring as its own phase because "a test that cannot execute in CI is not a deliverable" has +// been violated repeatedly here — most recently a Buck target that existed, was correct, and that no +// workflow ever referenced, so the only proof of a core mechanism had never once run. +const integrate = await agent(`${CTX}${WORKDIR} + +Invoke the \`ci-cd-and-automation\` skill and follow it. + +Read the diff yourself: \`git diff ${BASE_REF}...HEAD\`. + +## Your job: make every new test actually EXECUTE in CI +A test that cannot run in CI is not a deliverable. This repository has shipped the failure twice: a +Buck target that existed and was correct but that no workflow referenced — so the only committed +proof of a core mechanism had never executed — and a required check whose display name promised +something its steps did not run. + +1. Trace each new test from the file to the workflow step that runs it. Name the chain explicitly: + generator entry, build target, wrapper, workflow step. A missing link anywhere means it never runs. +2. Generated files are GENERATED. Never hand-edit one; change the generator and regenerate. +3. **Prove it, do not assume it.** Run the local equivalent of the CI job. If a job cannot run + locally, say exactly which link you could not verify and why. +4. If a new gate should become a required check, say so and explain the sequencing — a required + context that has never reported blocks every merge, so it must report green at least once first. +${COMMIT_RULE}`, + { label: 'integrate', phase: 'Integrate' }) + +phase('Prove') + +const VERDICT_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['verdict', 'proven_failable', 'weakening', 'residual'], + properties: { + verdict: { type: 'string', enum: ['PASS', 'FAIL'] }, + proven_failable: { type: 'string', description: 'Concrete inputs you RAN that make it fail, with real output. Could not make it fail = FAIL.' }, + weakening: { type: 'string', description: 'Was any assertion deleted, loosened, skipped or made conditional? Diff it yourself.' }, + residual: { type: 'array', items: { type: 'string' }, description: 'Still vacuous or unverified after this change.' }, + }, +} + +// Bun's reviewers received the DIFF ONLY and never the implementer's reasoning — the author's +// narrative primes a reviewer to accept. Passing the implementer's report here (as I did in an +// earlier workflow) quietly defeats the mechanism, so these agents are told to read the diff +// themselves and are given no summary of it. +const REVIEW_BASE = `${CTX}${WORKDIR} + +Invoke the \`verification-before-completion\` skill and follow it. + +**READ-ONLY. No edits, no git mutations.** Every earlier phase could edit; you cannot. You are the +last thing between this change and the branch, and your job is to disbelieve it. + +An implementation was just COMMITTED on this lane's branch. **Read the diff yourself** — +\`git diff ${BASE_REF}...HEAD\`, plus \`git log --oneline ${BASE_REF}..HEAD\` and +\`git status --short\` to confirm nothing was left dangling. You are deliberately NOT given the +implementer's account of what they did: their reasoning would prime you to accept it, and Bun's +reviewers saw the diff alone for exactly this reason. + +**If you build or test, do it in THIS lane and nowhere else.** Never run a build in another agent's +lane — two writers in one worktree share a \`target/\` and a build lock, and the results are not +merely slow but WRONG. A verification run made while another agent was building in the same tree +reported a test binary as 0-passed/3-failed; the identical command on an uncontended tree reported +3-passed/0-failed minutes later. A contended run is not evidence, in either direction. + +Assume the change is WRONG and look hard before conceding anything. Bun's reviewers also rejected +solutions that needed a paragraph of justification to defend — a workaround that must be explained +is a defect.` + +const verdicts = (await parallel([ + () => agent(`${REVIEW_BASE} + +## YOUR LENS: vacuity and correctness +1. Can you make it FAIL? Construct concrete failing inputs and RUN them. If you cannot make it fail, + that is a FAIL verdict — a check that cannot fail is not a check. +2. Would a trivial STUB satisfy this? Try to write one. Success here is the most valuable finding + available. +3. Was any existing assertion deleted, loosened or made conditional?`, + { label: 'prove:vacuity', phase: 'Prove', schema: VERDICT_SCHEMA }), + + () => agent(`${REVIEW_BASE} + +## YOUR LENS: discipline and honesty — deliberately different from the other reviewer +1. Does it actually compile and run? Run it. Do not accept that it does. +2. Are the project's testing constraints honoured — non-superuser runtime role, disposable Postgres, + \`--test-threads=1\`, no hand-edited generated files? +3. **Is anything claimed that was not verified?** Compare what the code proves against what any + comment or docstring asserts. Overstatement that survives review becomes tomorrow's false premise. +4. Did it stay inside its slice? Check \`git status\` for files outside ${OWNS || 'the declared scope'}.`, + { label: 'prove:discipline', phase: 'Prove', schema: VERDICT_SCHEMA }), +])).filter(Boolean) + +const passed = verdicts.length === 2 && verdicts.every((v) => v.verdict === 'PASS') + +// "Edit the process, not the outputs." When a lane produces bad work, Bun fixed the PROMPT and +// reran rather than hand-patching the diff. Emit that rerun brief instead of a bare failure, so the +// next attempt corrects the instruction rather than the symptom. +const rerun = passed ? null : { + guidance: 'Do NOT hand-patch the diff. Fix the brief and rerun this workflow — edit the process, not the outputs.', + failures: verdicts.filter((v) => v.verdict !== 'PASS').flatMap((v) => [v.proven_failable, v.weakening]).filter(Boolean), + suggested_context_additions: verdicts.flatMap((v) => v.residual || []), +} + +return { + specification: spec, + red, + implementation: impl, + cover, + doubt, + simplify, + security, + integrate, + verdicts, + passed, + rerun, +} diff --git a/scripts/console/workflows/stale-take-audit.js b/scripts/console/workflows/stale-take-audit.js new file mode 100644 index 000000000..090aad7d9 --- /dev/null +++ b/scripts/console/workflows/stale-take-audit.js @@ -0,0 +1,243 @@ +export const meta = { + name: 'stale-take-audit', + description: 'Find every file where a hand-rebuilt branch silently took the OLD side of a file the base branch had advanced — before CI finds them one push at a time', + whenToUse: 'After reconstructing a branch by copying a tree rather than merging, or after any hand-written exclusion list decided which side of a file to keep. Run it BEFORE the first push: it found in one pass what cost five CI round-trips to discover one file at a time.', + phases: [ + { title: 'Audit', detail: 'batched: does main have content this branch dropped?' }, + { title: 'Confirm', detail: 'adversarially re-check only the files claimed stale' }, + ], +} + +// The defect class, stated once so every agent judges the same thing: +// +// PR #618 was rebuilt by taking a superseded branch's TREE for the files it owned, with a +// hand-written exclusion list for files main owned. That list was a LIST -- each entry reasoned +// about individually -- so it was complete only for the cases already thought of. Five CI pushes +// have each surfaced one more file where main had advanced and the copy silently reverted it: +// ci.yml (un-wired a test suite main runs), and the ci-preflight lock and its counters. +// +// A revert that reintroduces old content is INVISIBLE in the diff-to-main -- it looks like an +// ordinary change. Only comparing against what main HAS reveals it. + +// args can arrive as a JSON STRING. Every other harness here guards for it and this one did not, +// so the first run died at line 16 having spawned zero agents. +let ARGS = args +if (typeof ARGS === 'string') { + try { ARGS = JSON.parse(ARGS) } catch (e) { + throw new Error(`stale-take-audit: args arrived as a string that is not valid JSON: ${e.message}`) + } +} +ARGS = ARGS || {} + +// Unknown options are rejected BEFORE required fields, so a typo is reported as the typo rather +// than as the missing field it happens to look like. +const KNOWN_ARGS = ['repo', 'main', 'files', 'batch'] +{ + const unknown = Object.keys(ARGS).filter((k) => !KNOWN_ARGS.includes(k)) + if (unknown.length) { + throw new Error(`stale-take-audit: unknown option(s) ${unknown.join(', ')}. Known: ${KNOWN_ARGS.join(', ')}.`) + } +} + +const FILES = ARGS.files +const REPO = ARGS.repo +const MAIN = ARGS.main +if (!Array.isArray(FILES) || !FILES.length) throw new Error('stale-take-audit: args.files must be a non-empty array') +if (!REPO || !MAIN) throw new Error('stale-take-audit: args.repo and args.main are required') + +const BATCH = ARGS.batch || 6 +const chunk = (xs, n) => xs.reduce((a, x, i) => (i % n ? a[a.length - 1].push(x) : a.push([x]), a), []) + +const RULES = ` +REPO: ${REPO} BASE BRANCH: ${MAIN} (read-only: do not edit, commit, or push anything) + +For each file, run BOTH directions IN ${REPO} (never the inherited cwd) and read them: + git -C ${REPO} diff HEAD ${MAIN} -- # '+' lines = what MAIN has that HEAD LACKS <-- the danger + git -C ${REPO} diff ${MAIN} HEAD -- # '+' lines = what HEAD adds + +You are looking for ONE thing: content present in ${MAIN} and ABSENT from HEAD, where the absence is +a REVERSION rather than a deliberate removal. Signals that it is a reversion: + - main's version is strictly larger and HEAD's matches an older shape + - a locked list, ratchet, counter, registry or wiring entry that main added and HEAD does not have + - a CI step, npm script, or test-suite registration that exists in main and not in HEAD + - a comment in main referencing a commit or PR that HEAD's version predates + +NOT a reversion, and must NOT be reported: + - content HEAD deliberately removes as its stated deliverable (this branch removes employees DML + from backend/app/src/hr.rs on purpose -- that is the whole point of the change) + - main's prose being merely reworded + - generated files whose content is derived (they are regenerated separately) + +For every file you judge STALE you must quote the exact missing lines. A file you cannot decide is +UNCERTAIN, not stale -- a false 'stale' costs a wrong revert, which is worse than another CI round. +` + +const SCHEMA = { + type: 'object', + required: ['results'], + properties: { + results: { + type: 'array', + items: { + type: 'object', + required: ['file', 'verdict', 'evidence'], + properties: { + file: { type: 'string' }, + verdict: { type: 'string', enum: ['STALE', 'CLEAN', 'UNCERTAIN'] }, + evidence: { type: 'string', description: 'the exact lines main has and HEAD lacks, or why it is clean' }, + missingFromHead: { type: 'string', description: 'verbatim content to graft back, if STALE' }, + wouldBreak: { type: 'string', description: 'which gate or behaviour breaks if this stays reverted' }, + }, + }, + }, + }, +} + +phase('Audit') +const audited = await parallel(chunk(FILES, BATCH).map((batch, i) => () => + agent( + `Audit ${batch.length} file(s) for silently-reverted content. +${RULES} + +FILES: +${batch.map((f) => ` ${f}`).join('\n')} + +Return one result per file, in order. Do not skip any.`, + // Cheap tier: every STALE claim from this pass is adversarially re-checked below, and a + // CLEAN verdict that is wrong shows up as the next CI failure rather than as a bad merge. + { schema: SCHEMA, label: `audit:${i}`, phase: 'Audit', model: 'sonnet' }, + ))) + +const all = (audited || []).filter(Boolean).flatMap((r) => r.results || []) +const dead = chunk(FILES, BATCH).length - (audited || []).filter(Boolean).length +if (dead) log(`!! ${dead} audit batch(es) died — those files are UNAUDITED, not clean`) + +// Batch objects returning is not coverage. A live batch that omits or duplicates a requested +// path used to leave `dead === 0` and report "full coverage" while classifying the omitted file +// as audited. Every requested path must appear exactly once before coverage is claimed. +const resultCounts = new Map() +for (const r of all) { + if (!r || typeof r.file !== 'string') continue + resultCounts.set(r.file, (resultCounts.get(r.file) || 0) + 1) +} +const missingAuditFiles = FILES.filter((f) => !resultCounts.has(f)) +const duplicatedAuditFiles = FILES.filter((f) => (resultCounts.get(f) || 0) > 1) +const coverageIncomplete = dead > 0 || missingAuditFiles.length > 0 || duplicatedAuditFiles.length > 0 +if (missingAuditFiles.length) { + log(`!! ${missingAuditFiles.length} requested file(s) have no audit result — UNAUDITED, not clean`) + for (const f of missingAuditFiles) log(` missing: ${f}`) +} +if (duplicatedAuditFiles.length) { + log(`!! ${duplicatedAuditFiles.length} requested file(s) have duplicate audit results — coverage ambiguous`) + for (const f of duplicatedAuditFiles) log(` duplicated: ${f}`) +} + +const suspect = all.filter((r) => r.verdict === 'STALE') +log(`audit: ${all.length} file(s) read, ${suspect.length} claimed STALE, ${all.filter((r) => r.verdict === 'UNCERTAIN').length} uncertain`) + +if (!suspect.length) { + return { + headline: [ + `No stale takes found across ${all.length} files.`, + coverageIncomplete ? `coverage INCOMPLETE — deadBatches=${dead}, missing=${missingAuditFiles.length}, duplicated=${duplicatedAuditFiles.length}` : 'full coverage', + ], + stale: [], + uncertain: all.filter((r) => r.verdict === 'UNCERTAIN'), + missingAuditFiles, + duplicatedAuditFiles, + } +} + +// Only the accusations get a second opinion; confirming a CLEAN verdict costs more than it is worth. +phase('Confirm') +const confirmed = await parallel(suspect.map((s) => () => + agent( + `Try to REFUTE this claim. Default to refuted=true when uncertain. + +CLAIM: ${MAIN} contains content that HEAD dropped by reversion, in ${s.file}. +EVIDENCE OFFERED: ${s.evidence} + +${RULES} + +Refute it if: the content is absent from HEAD deliberately (it is the branch's stated deliverable), +or main's version is not actually newer, or the lines quoted do not exist as claimed. Run the diffs +yourself rather than trusting the quote. + +Do NOT be handed a graft candidate from the first pass — a payload nobody re-read is a claim, not +evidence. If you uphold STALE (refuted=false), you MUST return missingFromHead as the exact graft +payload YOU reconstruct after re-reading the diffs. An upheld STALE without that payload is +unresolved, not confirmed. A mismatched or missing confirmation payload must not be substituted +from the first agent's text.`, + { + schema: { + type: 'object', + required: ['file', 'refuted', 'reasoning', 'missingFromHead'], + properties: { + file: { type: 'string' }, + refuted: { type: 'boolean' }, + reasoning: { type: 'string' }, + missingFromHead: { + type: 'string', + description: 'When refuted=false: the exact content to graft, attested by THIS pass. When refuted=true: empty string.', + }, + }, + }, + label: `confirm:${s.file.split('/').pop()}`, phase: 'Confirm', + }, + // `v && v.refuted` yields NULL for a dead agent, and null is neither `=== false` nor truthy -- so + // the suspicion fell out of BOTH lists below and vanished without trace while the headline still + // said "full coverage". `typeof === 'boolean'` is used rather than a null check because the schema + // is a request to the model, not an enforcement: an agent that returns an object without `refuted` + // must land in the same unresolved bucket as one that never returned at all. + ).then((v) => ({ + ...s, + refuted: v ? v.refuted : null, + refutation: v ? v.reasoning : null, + confirmedMissing: v && typeof v.missingFromHead === 'string' ? v.missingFromHead : null, + })))) + +const answered = (confirmed || []).filter((c) => c && typeof c.refuted === 'boolean') +// Upholding STALE without attesting the graft payload must not publish the first-pass quote. +const real = answered.filter((c) => + c.refuted === false + && typeof c.confirmedMissing === 'string' + && c.confirmedMissing.trim() !== '') +// A suspicion whose REFUTATION never ran is still a suspicion. This file's own rule two paragraphs +// up says a false 'stale' costs a wrong revert while a missed one costs another CI round -- but the +// missed one here is the worse half: the reversion this workflow exists to catch is a file whose +// un-wiring means the check that would have caught it does not run. +const unconfirmed = suspect.filter((s) => { + const a = answered.find((c) => c.file === s.file) + if (!a) return true + if (a.refuted === false && !(typeof a.confirmedMissing === 'string' && a.confirmedMissing.trim())) return true + return false +}) +if (unconfirmed.length) { + log(`!! ${unconfirmed.length} confirmation(s) never returned — those files are UNRESOLVED, not clean`) + for (const u of unconfirmed) log(` ${u.file}`) +} + +return { + headline: [ + `${all.length} files audited, ${suspect.length} suspected, ${real.length} CONFIRMED stale after adversarial re-check`, + unconfirmed.length ? `${unconfirmed.length} suspected file(s) UNRESOLVED — their confirmation never returned` : null, + coverageIncomplete || unconfirmed.length + ? `coverage INCOMPLETE — deadBatches=${dead}, missing=${missingAuditFiles.length}, duplicated=${duplicatedAuditFiles.length}, unresolved=${unconfirmed.length}` + : 'full coverage', + ].filter(Boolean), + // Publish ONLY the confirmer-attested graft. Never r.missingFromHead from the audit spread — + // that is an unconfirmed claim even when the STALE verdict itself was upheld. + stale: real.map((r) => ({ file: r.file, missingFromHead: r.confirmedMissing, wouldBreak: r.wouldBreak, evidence: r.evidence })), + // Unresolved accusations keep the first-pass claim under a non-graft key so an operator sees + // what was alleged without a ready-to-apply missingFromHead payload. + unconfirmed: unconfirmed.map((u) => ({ + file: u.file, + evidence: u.evidence, + claimedMissingFromHead: u.missingFromHead, + wouldBreak: u.wouldBreak, + })), + refuted: answered.filter((c) => c.refuted).map((c) => ({ file: c.file, why: c.refutation })), + uncertain: all.filter((r) => r.verdict === 'UNCERTAIN').map((r) => ({ file: r.file, evidence: r.evidence })), + missingAuditFiles, + duplicatedAuditFiles, +} diff --git a/scripts/cursor/validate-lane-receipt.mjs b/scripts/cursor/validate-lane-receipt.mjs index ad157dd4d..6b93a4f5c 100755 --- a/scripts/cursor/validate-lane-receipt.mjs +++ b/scripts/cursor/validate-lane-receipt.mjs @@ -3,10 +3,10 @@ * Cursor-native lane receipt validator. * * Mirrors the skim-proof BUILD_SCHEMA / REVIEW_SCHEMA fields from - * `.claude/workflows/lane-fanout.js`. Exit nonzero = not done. + * `scripts/console/workflows/lane-fanout.js`. Exit nonzero = not done. * * Usage: - * node scripts/cursor/validate-lane-receipt.mjs .cursor/receipts/.json + * node scripts/cursor/validate-lane-receipt.mjs scripts/console/fixtures/.json * node scripts/cursor/validate-lane-receipt.mjs --schema build|critic */ import { readFileSync } from 'node:fs' diff --git a/tools/lanes/wfcheck.mjs b/tools/lanes/wfcheck.mjs index 2ab1c8e9c..4279b6933 100755 --- a/tools/lanes/wfcheck.mjs +++ b/tools/lanes/wfcheck.mjs @@ -10,7 +10,7 @@ // and `||` short-circuits whenever `A.repo` is passed. The broken branch was never once evaluated. // A default that cannot execute is not a default; it is a latent crash keyed to an unset arg. // -// Usage: node tools/lanes/wfcheck.mjs .claude/workflows/slice.js '{"task":"t","lane":"1"}' +// Usage: node tools/lanes/wfcheck.mjs scripts/console/workflows/slice.js '{"task":"t","lane":"1"}' // Exit 0 = module scope evaluated clean. Exit 1 = it threw (message printed). // // NOTE ON THE PROBE ITSELF: the first version wrapped the body in `(async()=>{})()` and try/caught From 61006cc5a267c1c939853a9df34c8412bafabfee Mon Sep 17 00:00:00 2001 From: Jason Lee <56489493+jason931225@users.noreply.github.com> Date: Fri, 21 Aug 2026 04:20:04 -0400 Subject: [PATCH 7/7] fix(docs): drop untracked agent markdown from the documentation manifest Untracking .beads/.cursor/.omx left seven seed records pointing at blobs that are no longer first-party. ci/harness/README.md was tracked without a record. Both made the documentation manifest and local-link gates red once preflight started running again. --- docs/documentation-index.json | 80 ++++----------------------- docs/documentation-manifest.seed.json | 80 ++++----------------------- 2 files changed, 20 insertions(+), 140 deletions(-) diff --git a/docs/documentation-index.json b/docs/documentation-index.json index 9fc339675..75a5f31c6 100644 --- a/docs/documentation-index.json +++ b/docs/documentation-index.json @@ -83,76 +83,6 @@ } ], "documents": [ - { - "path": ".beads/README.md", - "class": "historical", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "blob_sha": "63e8f4c232ee83a6a239ae43b85f9c7903601dbb", - "archive_tag": null - }, - { - "path": ".cursor/PROCESS-SYNTHESIS.md", - "blob_sha": "040ca8c1d78a07403f42ce4c3b461cfbfa8ba8c5", - "class": "current", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".cursor/agents/lane-critic.md", - "blob_sha": "9dc9357231384841c19d7d60a3ee74c3c7ee6137", - "class": "current", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".cursor/agents/lane-implementer.md", - "blob_sha": "43ff3b765d60131c681e1afb3d0bd7bc96732157", - "class": "current", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".cursor/failure-classes-2026-08-10.md", - "blob_sha": "51ec61c1641e7a0dd4f17d5036c43b80d8ae3d63", - "class": "evidence", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".omx/context/reasoning-lens-contract-20260803T101035Z.md", - "class": "evidence", - "owner": "repository maintainers", - "status": "frozen", - "replacement": null, - "retention": "retain", - "blob_sha": "1db260293fbd0228b72abb72afa6a5bc0b383871", - "archive_tag": null - }, - { - "path": ".omx/plans/reasoning-lens-contract.md", - "class": "quarry", - "owner": "repository maintainers", - "status": "frozen", - "replacement": null, - "retention": "retain as context", - "blob_sha": "b7e6247c098c0f6abcfb41c9b52bf1368aa5083e", - "archive_tag": null - }, { "path": "AGENTS.md", "class": "historical", @@ -263,6 +193,16 @@ "blob_sha": "c8b570f980cdb6924f17a1ba5c7a277d27e9e4c0", "archive_tag": null }, + { + "path": "ci/harness/README.md", + "class": "current", + "owner": "repository maintainers", + "status": "active", + "replacement": null, + "retention": "retain", + "blob_sha": "6d1619fdc6408b6cce8932aeed1339d98279ede6", + "archive_tag": null + }, { "path": "deploy/OPS-RUNBOOK-baremetal.md", "class": "historical", diff --git a/docs/documentation-manifest.seed.json b/docs/documentation-manifest.seed.json index d0061b565..1465fb233 100644 --- a/docs/documentation-manifest.seed.json +++ b/docs/documentation-manifest.seed.json @@ -1,74 +1,4 @@ [ - { - "path": ".beads/README.md", - "class": "historical", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "blob_sha": "63e8f4c232ee83a6a239ae43b85f9c7903601dbb", - "archive_tag": null - }, - { - "path": ".cursor/PROCESS-SYNTHESIS.md", - "blob_sha": "040ca8c1d78a07403f42ce4c3b461cfbfa8ba8c5", - "class": "current", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".cursor/agents/lane-critic.md", - "blob_sha": "9dc9357231384841c19d7d60a3ee74c3c7ee6137", - "class": "current", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".cursor/agents/lane-implementer.md", - "blob_sha": "43ff3b765d60131c681e1afb3d0bd7bc96732157", - "class": "current", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".cursor/failure-classes-2026-08-10.md", - "blob_sha": "51ec61c1641e7a0dd4f17d5036c43b80d8ae3d63", - "class": "evidence", - "owner": "repository maintainers", - "status": "active", - "replacement": null, - "retention": "retain", - "archive_tag": null - }, - { - "path": ".omx/context/reasoning-lens-contract-20260803T101035Z.md", - "class": "evidence", - "owner": "repository maintainers", - "status": "frozen", - "replacement": null, - "retention": "retain", - "blob_sha": "1db260293fbd0228b72abb72afa6a5bc0b383871", - "archive_tag": null - }, - { - "path": ".omx/plans/reasoning-lens-contract.md", - "class": "quarry", - "owner": "repository maintainers", - "status": "frozen", - "replacement": null, - "retention": "retain as context", - "blob_sha": "b7e6247c098c0f6abcfb41c9b52bf1368aa5083e", - "archive_tag": null - }, { "path": "AGENTS.md", "class": "historical", @@ -179,6 +109,16 @@ "blob_sha": "c8b570f980cdb6924f17a1ba5c7a277d27e9e4c0", "archive_tag": null }, + { + "path": "ci/harness/README.md", + "class": "current", + "owner": "repository maintainers", + "status": "active", + "replacement": null, + "retention": "retain", + "blob_sha": "6d1619fdc6408b6cce8932aeed1339d98279ede6", + "archive_tag": null + }, { "path": "deploy/OPS-RUNBOOK-baremetal.md", "class": "historical",