@matt82198/aesop 0.1.0-beta.3 → 0.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +37 -94
  2. package/CLAUDE-TEMPLATE.md +115 -0
  3. package/README.md +89 -264
  4. package/aesop.config.example.json +27 -26
  5. package/bin/CLAUDE.md +39 -0
  6. package/bin/cli.js +368 -24
  7. package/daemons/CLAUDE.md +33 -0
  8. package/daemons/backup-fleet.sh +92 -21
  9. package/daemons/run-watchdog.sh +90 -10
  10. package/dash/CLAUDE.md +32 -0
  11. package/dash/dash-extra.mjs +194 -21
  12. package/dash/watchdog-gui.sh +23 -6
  13. package/docs/BEHAVIORAL-PR-REVIEW.md +86 -0
  14. package/docs/CHECKPOINTING.md +100 -0
  15. package/docs/DISPATCH-MODEL.md +11 -7
  16. package/docs/FORENSICS.md +204 -0
  17. package/docs/GOVERNANCE.md +4 -2
  18. package/docs/HOOK-INSTALL.md +307 -0
  19. package/docs/HOW-THE-LOOP-WORKS.md +175 -0
  20. package/docs/MEMORY-TEMPLATE.md +61 -0
  21. package/docs/README.md +44 -0
  22. package/docs/RELIABILITY.md +61 -0
  23. package/docs/RESTORE.md +397 -0
  24. package/docs/SCRIPTS-POLICY.md +97 -0
  25. package/docs/spikes/tiered-cognition/ACTIVATION.md +125 -0
  26. package/docs/spikes/tiered-cognition/DESIGN.md +287 -0
  27. package/docs/spikes/tiered-cognition/FINDINGS.md +113 -0
  28. package/docs/spikes/tiered-cognition/README.md +29 -0
  29. package/docs/spikes/tiered-cognition/aesop-cognition.example.md +32 -0
  30. package/docs/spikes/tiered-cognition/force-model-policy.merged.mjs +673 -0
  31. package/docs/spikes/tiered-cognition/strip-tools-hook.mjs +434 -0
  32. package/hooks/CLAUDE.md +38 -0
  33. package/hooks/claude/force-model-policy.mjs +159 -0
  34. package/hooks/pre-push-policy.sh +753 -0
  35. package/monitor/.signal-state.json +3 -0
  36. package/monitor/ACTIONS.log +1 -0
  37. package/monitor/BRIEF.md +24 -0
  38. package/monitor/CHARTER.md +29 -8
  39. package/monitor/CLAUDE.md +40 -0
  40. package/monitor/SIGNALS.json +54 -0
  41. package/monitor/collect-signals.mjs +529 -62
  42. package/package.json +12 -3
  43. package/skills/CLAUDE.md +3 -0
  44. package/skills/power/SKILL.md +161 -0
  45. package/tools/CLAUDE.md +191 -0
  46. package/tools/__pycache__/buildlog.cpython-314.pyc +0 -0
  47. package/tools/__pycache__/ci_merge_wait.cpython-314.pyc +0 -0
  48. package/tools/__pycache__/ensure_state.cpython-314.pyc +0 -0
  49. package/tools/__pycache__/eod_sweep.cpython-314.pyc +0 -0
  50. package/tools/__pycache__/fleet_ledger.cpython-314.pyc +0 -0
  51. package/tools/__pycache__/heartbeat.cpython-314.pyc +0 -0
  52. package/tools/__pycache__/inbox_drain.cpython-314.pyc +0 -0
  53. package/tools/__pycache__/launch_tui.cpython-314.pyc +0 -0
  54. package/tools/__pycache__/orchestrator_status.cpython-314.pyc +0 -0
  55. package/tools/__pycache__/power_selftest.cpython-314.pyc +0 -0
  56. package/tools/__pycache__/prepublish_scan.cpython-314.pyc +0 -0
  57. package/tools/__pycache__/rotate_logs.cpython-314.pyc +0 -0
  58. package/tools/__pycache__/scanner_selftest.cpython-314.pyc +0 -0
  59. package/tools/__pycache__/secret_scan.cpython-314.pyc +0 -0
  60. package/tools/__pycache__/stall_check.cpython-314.pyc +0 -0
  61. package/tools/__pycache__/verify_dash.cpython-314.pyc +0 -0
  62. package/tools/__pycache__/verify_submit_encoding.cpython-314.pyc +0 -0
  63. package/tools/agent-forensics.sh +186 -0
  64. package/tools/buildlog.py +182 -0
  65. package/tools/ci_merge_wait.py +204 -0
  66. package/tools/ensure_state.py +94 -0
  67. package/tools/eod_sweep.py +239 -0
  68. package/tools/fleet_ledger.py +272 -0
  69. package/tools/heartbeat.py +131 -0
  70. package/tools/inbox_drain.py +249 -0
  71. package/tools/launch_tui.py +51 -3
  72. package/tools/lock.mjs +211 -0
  73. package/tools/metrics_gate.py +205 -0
  74. package/tools/orchestrator_status.py +99 -0
  75. package/tools/power_selftest.py +386 -0
  76. package/tools/prepublish_scan.py +84 -0
  77. package/tools/proposals.mjs +248 -0
  78. package/tools/reconstitute.sh +467 -0
  79. package/tools/rotate_logs.py +228 -0
  80. package/tools/scanner_selftest.py +303 -0
  81. package/tools/secret_scan.py +131 -31
  82. package/tools/stall_check.py +172 -0
  83. package/tools/verify_dash.py +694 -0
  84. package/tools/verify_submit_encoding.py +194 -0
  85. package/ui/CLAUDE.md +109 -0
  86. package/ui/__pycache__/agents.cpython-314.pyc +0 -0
  87. package/ui/__pycache__/collectors.cpython-314.pyc +0 -0
  88. package/ui/__pycache__/config.cpython-314.pyc +0 -0
  89. package/ui/__pycache__/csrf.cpython-314.pyc +0 -0
  90. package/ui/__pycache__/handler.cpython-314.pyc +0 -0
  91. package/ui/__pycache__/render.cpython-314.pyc +0 -0
  92. package/ui/__pycache__/serve.cpython-314.pyc +0 -0
  93. package/ui/__pycache__/sse.cpython-314.pyc +0 -0
  94. package/ui/agents.py +179 -0
  95. package/ui/api/__init__.py +84 -0
  96. package/ui/api/__pycache__/__init__.cpython-314.pyc +0 -0
  97. package/ui/api/__pycache__/submit.cpython-314.pyc +0 -0
  98. package/ui/api/__pycache__/tracker.cpython-314.pyc +0 -0
  99. package/ui/api/submit.py +58 -0
  100. package/ui/api/tracker.py +116 -0
  101. package/ui/collectors.py +586 -0
  102. package/ui/config.py +114 -0
  103. package/ui/csrf.py +140 -0
  104. package/ui/handler.py +426 -0
  105. package/ui/render.py +29 -0
  106. package/ui/serve.py +68 -796
  107. package/ui/sse.py +168 -0
  108. package/ui/templates/dashboard.html +1202 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@matt82198/aesop",
3
- "version": "0.1.0-beta.3",
4
- "description": "Multi-agent orchestration for AI coding agents, designed to survive failure a plain-file brain, git as the only durable layer, cheap subagent fleets, and guardrails enforced in code.",
3
+ "version": "0.1.0-beta.4",
4
+ "description": "Multi-agent orchestration for AI coding agents, designed to survive failure \u00e2\u20ac\u201d a plain-file brain, git as the only durable layer, cheap subagent fleets, and guardrails enforced in code.",
5
5
  "bin": {
6
6
  "aesop": "bin/cli.js"
7
7
  },
@@ -14,7 +14,10 @@
14
14
  "tools/",
15
15
  "ui/",
16
16
  "docs/",
17
+ "hooks/",
18
+ "skills/",
17
19
  "aesop.config.example.json",
20
+ "CLAUDE-TEMPLATE.md",
18
21
  "README.md",
19
22
  "LICENSE",
20
23
  "CHANGELOG.md"
@@ -45,5 +48,11 @@
45
48
  "publishConfig": {
46
49
  "access": "public"
47
50
  },
48
- "scripts": {}
51
+ "scripts": {
52
+ "test": "node --test --test-force-exit --test-timeout=60000 tests/**/*.test.mjs",
53
+ "test:node": "node --test --test-force-exit --test-timeout=60000 tests/**/*.test.mjs",
54
+ "test:sh": "bash tests/test_pre_push_policy.sh && bash tests/test-run-watchdog.sh && bash tests/backup-fleet.test.sh && bash tests/test_reconstitute.sh && bash tests/test_reconstitute_fixes.sh && bash tests/test_agent_forensics.sh && bash hooks/pre-push-policy.sh --test && bash tools/reconstitute.sh --test",
55
+ "test:py": "python -m unittest discover -s tests",
56
+ "test:all": "npm run test:node && npm run test:sh && npm run test:py"
57
+ }
49
58
  }
@@ -0,0 +1,3 @@
1
+ # skills/ — Orchestration and priming skills
2
+
3
+ - **power/** — /power skill: prime the filesystem brain (load rules, dispatch model, memory, state) and report system health — see power/SKILL.md
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: power
3
+ description: Prime the "filesystem brain" — load all operating rules, behaviors, the multi-agent dispatch model, memory, and the current project's semantics from disk. Report a compact primed brief with system health and next steps.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Power — prime the filesystem brain
8
+
9
+ Everything that governs how I should behave lives on disk. This skill reads it, internalizes it,
10
+ and reports a compact "primed" summary — **without inflating context** (code is read via subagents
11
+ that return briefs, not dumped raw).
12
+
13
+ The principle: **all rules can be abstracted to consistent agent behavior.** Rules are files;
14
+ reading them = becoming the agent the files describe.
15
+
16
+ **Scope: this is a priming skill for aesop orchestration itself** — rules, dispatch model, memory,
17
+ machinery health. Running it does NOT imply any particular project is being worked on. Project
18
+ state loads only when the session is actually resuming that project (step 3); deferred projects
19
+ are rolled up to counts, never item-by-item — and their repos/daemons are left untouched.
20
+
21
+ ## Procedure (run in order)
22
+
23
+ ### 1. Load global rules + dispatch model
24
+ Read these directly (they are small and authoritative):
25
+ - `~/.claude/CLAUDE.md` — the **cardinal rules** (autonomous continuation, dispatch model,
26
+ durable checkpointing, build env). These override defaults.
27
+ - Any project-local `CLAUDE.md` in the current working directory and its parents.
28
+
29
+ Then run the **brain integrity check**: `~/.claude` should be a git repo (if you version it).
30
+ Check `git -C ~/.claude status --porcelain` and ahead/behind vs origin:
31
+ - Deliberate uncommitted rule/hook/skill/memory changes → commit + push now (gated by
32
+ `python <project-root>/tools/secret_scan.py --staged`);
33
+ - **Unexpected** modifications or deletions → report as alert — stop, diff against remote, restore
34
+ before continuing (core defense: don't let tooling shadow your rules).
35
+
36
+ Also check for unactioned entries in `~/.claude/PROPOSALS.md` or similar and surface a
37
+ one-line summary of each in the primed report.
38
+
39
+ Internalize the **dispatch model** and apply it for the rest of the session. Document
40
+ the dispatch model's key invariants locally (in your private repo's `CLAUDE.md`,
41
+ if you version `~/.claude`), for example:
42
+ - **Subagents model** (Haiku vs specialists vs orchestrator tier)
43
+ - **Parallelism defaults** (e.g., fan out 6–8 Haikus per domain)
44
+ - **TDD-first discipline** and checkpointing (STATE.md, BUILDLOG.md)
45
+ - **Observable machinery**: every action logged, every cost tracked
46
+
47
+ ### 2. Load memory
48
+ - Read `~/.claude/memory/MEMORY.md` (the index, or use your configured path).
49
+ - For any memory entry whose one-line hook is relevant to the current task/project,
50
+ read that memory file.
51
+ - Treat recalled memories as background context that reflects when they were written —
52
+ **verify any file/flag/path they name still exists before relying on it.**
53
+
54
+ ### 3. Load the active project's state
55
+ If working inside (or resuming) a known project, read its durable checkpoint first:
56
+ - Look for a `STATE.md` and `BUILDLOG.md` in the project root or a handoff folder.
57
+ - `STATE.md` is the source of truth for intent, locked decisions, contracts, and **NEXT STEPS**.
58
+ - Re-sync from disk (git log, file state) — never trust stale assumptions.
59
+
60
+ ### 4. Codebase semantics — already-primed vs first-time init
61
+ **First check whether this codebase has been primed before**: does it already carry its scoped
62
+ `.md` layer (a project-root `CLAUDE.md`, domain `CLAUDE.md` units, a `STATE.md`)?
63
+
64
+ - **Already primed** (has `CLAUDE.md`s and optionally `STATE.md`): do NOT re-investigate the code.
65
+ The `.md` layer IS the persisted semantic memory — the project `CLAUDE.md`s are sufficient;
66
+ the codebase is simply **ready to work**. Only re-scan a specific domain if its `CLAUDE.md`
67
+ is contradicted by on-disk reality (then fix the `.md`, which is the bug).
68
+ - **First time / not primed** (no scoped `.md`s found): run **init-prime** — fan out Haiku
69
+ subagents to read the whole codebase (briefs only, never raw dumps) and **autonomously write
70
+ the scoped `.md` layer it needs**:
71
+ - a project-root `CLAUDE.md`: purpose, build/run commands + toolchain, top gotchas
72
+ - smallest self-contained **domain `CLAUDE.md` units** (minimal, never lossy) — module layout,
73
+ key contracts, domain logic, non-obvious invariants
74
+ - a `STATE.md` checkpoint seed if the work warrants it
75
+
76
+ Commit + push the new `.md`s (secret-scan gated). Every future `/power` on that codebase
77
+ then takes the already-primed fast path.
78
+
79
+ ### 5. Report "primed" (compact)
80
+ Output a short brief, not a wall of text:
81
+
82
+ - **Drain the UI inbox first** (if configured in `aesop.config.json`): run
83
+ `python <project-root>/tools/inbox_drain.py pending`. If items exist, surface each under
84
+ a **"QUEUED FROM DASHBOARD"** heading as `[ISO-ts] text` one-liners. Mark them processed
85
+ (`python <project-root>/tools/inbox_drain.py mark-all`) after actioning.
86
+ - **Health line first**: run `python <project-root>/tools/power_selftest.py` and lead with its
87
+ `POWER-SELFTEST:` line (hooks, brain push state, heartbeats, decisions, scanner state).
88
+ Bullet anything non-OK.
89
+ - **Open decisions**: surface each ACTIVE line of any decisions/proposals file and any
90
+ unactioned inbox entries as one-liners (rolled-up counts for deferred items, never item-by-item).
91
+ - **Rules loaded**: dispatch model + any project-specific overrides in one or two lines.
92
+ - **Project**: name, purpose, status, repo, key paths.
93
+ - **Semantics**: the domain model + main flow in a few bullets.
94
+ - **Gotchas**: the verified ones that change how I act.
95
+ - **Next steps**: from `STATE.md`, ready to execute (autonomously, per the dispatch model).
96
+
97
+ ### 6. Project app launch (if applicable)
98
+ After reporting primed, bring the active project's runnable app up in the BACKGROUND if it has one.
99
+ Make it **idempotent**: if the port is already serving a healthy instance, skip the start and
100
+ just report the URL.
101
+
102
+ **Pattern** (project adopters fill in their own):
103
+ - Define a `run` skill or script in your project (or use an existing `start`/`run` command).
104
+ - Detect if the service is already healthy (e.g., `curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/`).
105
+ - If healthy (200), skip startup and just report the URL.
106
+ - If not healthy, start the service in the background via Bash with `run_in_background: true`.
107
+ - Poll readiness WITHOUT blocking sleep: use curl retries or a cheap probe loop.
108
+ - Report the URL + background task ID so it can be stopped on request.
109
+
110
+ **DO NOT** hardcode personal app paths (e.g., Spring Boot repos, IDEs, personal tools).
111
+ Instead, document in your project's `CLAUDE.md` how to launch the app, and reference that
112
+ in your local version of this skill or a project-specific override.
113
+
114
+ ### 6b. Machinery health — orchestration monitor (optional)
115
+ If your project uses an orchestration monitor, spin up a **background Haiku agent** that:
116
+ 1. Collects health signals (cheap, deterministic)
117
+ 2. Reads only that brief and acts per a charter — AUTO-applies safe refinements and stages
118
+ rule/behavior changes to a `PROPOSALS.md` for review
119
+ 3. Self-paces via ScheduleWakeup and beats a heartbeat
120
+
121
+ Skip if `.monitor-heartbeat` is already <300s old. Announce its task id so it can be paused/stopped.
122
+
123
+ ### 7. Standing dev loops (optional; only when actively developing)
124
+ If the session is doing active development (writing/changing code — not just answering questions),
125
+ keep these background loops running continuously. They are the "tail": cheap, persistent,
126
+ append-only, and never inflate the main context.
127
+
128
+ **Patterns** (project adopters fill in their own):
129
+
130
+ 1. **Build/status + backup watchdog** (background bash loop) — appends timestamped git/test/build
131
+ snapshots. For example, if your project has a `daemons/` folder with a `run-watchdog.sh`,
132
+ launch that via Bash with `run_in_background: true` and let it beat a heartbeat so you know
133
+ it is live. Stop with TaskStop when development pauses.
134
+
135
+ 2. **Memory keeper** (Haiku, background) — as new decisions, contracts, or gotchas emerge,
136
+ updates the relevant memory files compactly (one fact per memory; never bloat). Re-invoke
137
+ via SendMessage to keep context.
138
+
139
+ 3. **Continuous QA loop** — after each feature reaches a checkpoint: **Haiku reviews →
140
+ Haiku bugfixes → Haiku lint/format**, looping until the build is green and review is clean,
141
+ with **Opus orchestrator final-catch** before merge. TDD-first: failing tests before code.
142
+
143
+ 4. **Tail-drift alignment** (Haiku, background) — periodically checks in-flight agents' output
144
+ against their domain `CLAUDE.md` contract, and realigns anything drifting.
145
+
146
+ 5. **Hung-Haiku watchdog** (orchestrator's main thread, NOT a subagent) — keep watching every
147
+ spawned Haiku for stalls. On hang: TaskStop it and relaunch from checkpoint or respawn.
148
+
149
+ Launch any that aren't already running; **stop them with TaskStop when development pauses**.
150
+
151
+ Mechanism: use background `Agent` (run_in_background) for keeper/drift loops, `Monitor` for
152
+ the watchdog, and self-pace longer cycles with `/loop` skill or `ScheduleWakeup`.
153
+ Always announce which loops were started (and their task IDs) so they can be paused/stopped.
154
+
155
+ ## Notes
156
+ - If no project context exists yet, steps 1–2 still apply; skip 3–4 and say so.
157
+ - Steps 1–5 always run on `/power`; **step 6 (launch app) runs only if the active project has one**;
158
+ **step 6b (monitor) runs only if configured**; step 7 (dev loops) runs only during active development.
159
+ - Prefer subagents for anything large; the goal is a *brain*, not a *dump*.
160
+ - After priming, proceed under the dispatch model (continue approved work autonomously; stop only
161
+ for genuine decisions or unauthorized outward/destructive actions).
@@ -0,0 +1,191 @@
1
+ # tools/ — Build utilities
2
+
3
+ Local-only Python (stdlib only, no external deps), bash (POSIX, CRLF-safe). Never print secrets — report by pattern name/location only.
4
+
5
+ ## FILES
6
+
7
+ **Locking & atomicity**:
8
+ - `lock.mjs` — Fail-closed atomic lock acquisition (exponential backoff + stale-lock detection) for PROPOSALS.md and related single-writer operations
9
+
10
+ **Secret scanning & compliance**:
11
+ - `secret_scan.py` — Pre-push secret/credential detection gate; scans staged, history, or paths by regex + filename patterns
12
+ - `scanner_selftest.py` — Regression harness for secret_scan.py; validates TP/FP vectors and self-scan cleanliness
13
+ - `prepublish_scan.py` — Pre-publish gate running full git history + staged-changes scans; exit 0 only if CLEAR-TO-PUBLISH
14
+
15
+ **Browser-level verification (CI gates)**:
16
+ - `verify_dash.py` — Browser proof for realtime SSE dashboard; validates console errors, backlog rendering, live SSE updates (use `--allow-skip` in browserless environments)
17
+ - `verify_submit_encoding.py` — Browser proof for /submit UTF-8 inbox bootstrap; validates encoding on Windows and real CSRF flow (use `--allow-skip` in browserless environments)
18
+
19
+ **CI/merge operations**:
20
+ - `ci_merge_wait.py` — CI-gated merge helper; polls gh pr view until checks conclude (SUCCESS/FAILURE), then merges ONLY if SUCCESS (structurally unreachable otherwise)
21
+
22
+ **Orchestration infrastructure**:
23
+ - `proposals.mjs` — Proposal lifecycle manager (list/accept/reject); uses fail-closed locking for atomic state updates
24
+ - `power_selftest.py` — Health check harness for /power bootstrap; validates hooks, brain, heartbeats, decisions, and scanner
25
+ - `buildlog.py` — Uniform BUILDLOG.md appender; ensures consistent timestamp + message + git HEAD ref formatting
26
+ - `ensure_state.py` — Scaffold STATE.md and BUILDLOG.md templates in state directory if missing
27
+ - `fleet_ledger.py` — Append-only ledger of agent runs, resource use, and verdicts; supports harvest (scan tasks) and rotate (archive)
28
+ - `heartbeat.py` — Single-instance loop liveness registry; write beats to state/.heartbeats/<name>, check staleness across fleet
29
+ - `stall_check.py` — Automated silent-hang detector; scans agent transcript mtimes to identify stalled agents, feeds watchdog monitoring
30
+ - `inbox_drain.py` — Drain UI inbox submissions; tracks processed dashboard work items (queue while no session running)
31
+ - `orchestrator_status.py` — Atomic writer for state/orchestrator-status.json (set/clear activity+phase); feeds the dashboard status panel
32
+
33
+ **Repository operations**:
34
+ - `reconstitute.sh` — Clone or fetch repos from config (tab/space-delimited); validates clone targets against fleet-root (physical paths, security)
35
+ - `eod_sweep.py` — End-of-day safety check; scans repos for dirty trees, unpushed commits, untracked files; optional auto-push
36
+
37
+ **Utilities**:
38
+ - `agent-forensics.sh` — Incident forensics / behavior reconstruction; read-only git plumbing to snapshot or diff behavior-controlling files
39
+ - `launch_tui.py` — Spawn bash TUI script in detached terminal; finds terminal (Git Bash → Windows Terminal), idempotent via pidfile
40
+ - `rotate_logs.py` — Log rotation utility; archives oldest lines when file exceeds size/line thresholds; ensures no data loss
41
+
42
+ ## secret_scan.py — Pre-push secret/credential detection gate
43
+
44
+ Scans staged/history/paths for secrets by regex pattern and credential filenames; blocks pushes on findings.
45
+
46
+ - `secret_scan.py --staged [--repo PATH]` — scan git staged files
47
+ - `secret_scan.py --history [--repo PATH]` — scan all git history blobs
48
+ - `secret_scan.py PATH [PATH...]` — scan files/dirs directly (recurse)
49
+
50
+ Exit: 0=clean, 1=findings, 2=usage error. Output masks secrets as `xxxx...`.
51
+
52
+ **Pragma escape** (pattern findings only; credential filenames always fatal):
53
+ ```
54
+ # secretscan: allow-pattern-docs
55
+ ```
56
+ Mark file's first 10 lines to report rule-based findings as ALLOWED-DOC (non-fatal). Use only for deliberate pattern documentation.
57
+
58
+ ## agent-forensics.sh — Incident forensics / behavior reconstruction
59
+
60
+ Read-only git plumbing; reconstructs agent behavior snapshot or diffs behavior-controlling files.
61
+
62
+ - `bash tools/agent-forensics.sh <commit>` — print commit header, rules snapshot, CLAUDE.md, STATE.md, last 30 lines of BUILDLOG.md
63
+ - `bash tools/agent-forensics.sh --diff <commitA> <commitB>` — diff CLAUDE.md, STATE.md, docs/, monitor/CHARTER.md, hooks/ between commits
64
+
65
+ Exit: 0=success, 1=error (never raw git traces). Requires: git, head, tail, wc, grep.
66
+
67
+ ## launch_tui.py — Spawn bash TUI script in detached terminal
68
+
69
+ Finds terminal (prefer Git Bash → Windows Terminal wt.exe), spawns script detached, idempotent via pidfile.
70
+
71
+ - `python launch_tui.py --script <path> [--title <title>] [--pidfile <path>]`
72
+
73
+ Exit: 0=success, 1=error. Output: exactly one line (`spawned (pid N)` or `already running (pid N)` or ERROR).
74
+
75
+ ## power_selftest.py — Health check harness for /power bootstrap
76
+
77
+ Validates hooks (settings.json), brain (git status), heartbeats (state/ beat files), decisions/inbox counts, and secret scanner.
78
+
79
+ - `python power_selftest.py` — run all checks and print summary line + bullets for non-OK items
80
+
81
+ Configuration via `aesop.config.json` or env vars: `BRAIN_ROOT`, `AESOP_STATE_ROOT`, `SCRIPTS_ROOT`.
82
+ Gracefully degrades when targets don't exist (reports `n/a` instead of crashing).
83
+
84
+ Exit: 0=OK/DEGRADED, 1=FAIL. Output: `POWER-SELFTEST: OK|DEGRADED|FAIL — <checks>` + optional bullets.
85
+
86
+ ## inbox_drain.py — Drain UI inbox submissions
87
+
88
+ Tracks processed dashboard submissions (work items queued while no session was running).
89
+
90
+ - `python inbox_drain.py pending` — list unprocessed inbox items
91
+ - `python inbox_drain.py mark <ISO-ts>` — mark one item processed
92
+ - `python inbox_drain.py mark-all` — mark all pending items processed
93
+
94
+ Configuration via `aesop.config.json` or env vars: `AESOP_INBOX_PATH`, `AESOP_INBOX_SEEN_PATH`, `AESOP_STATE_ROOT`.
95
+ Gracefully handles missing inbox/seen files (no crash).
96
+
97
+ Exit: 0 always. Output: `pending` lists items one per line; `mark`/`mark-all` prints summary to stderr.
98
+
99
+ ## heartbeat.py — Single-instance loop liveness registry
100
+
101
+ Fleet-wide heartbeat tracking for monitoring loop freshness. Write heartbeat "beat" to registry, check all beats for staleness.
102
+
103
+ - `heartbeat.py beat <name> [status] [--state-dir DIR] [--brain]` — write epoch to state/.heartbeats/<name> (or ~/.claude/.heartbeats with --brain)
104
+ - `heartbeat.py check [--max-age SEC] [--state-dir DIR]` — check all beats, exit 0 if all alive
105
+
106
+ ## stall_check.py — Automated silent-hang detector for agent transcripts
107
+
108
+ Scans agent transcript files (agent-*.jsonl) by mtime to detect stalled/silent-hanging agents.
109
+
110
+ - `stall_check.py [--transcripts-root DIR] [--threshold-seconds SEC] [--json] [--exit-nonzero-on-stall]`
111
+
112
+ Options:
113
+ - `--transcripts-root DIR` — Root directory to scan; defaults to AESOP_TRANSCRIPTS_ROOT env var or ~/.claude/projects
114
+ - `--threshold-seconds SEC` — Max age (seconds) for a "fresh" transcript (default: 600); transcripts older are flagged as stalled
115
+ - `--json` — Output as JSON list of {agent_id, age_seconds, stalled, last_mtime} instead of human table
116
+ - `--exit-nonzero-on-stall` — Exit 1 if any agent is stalled (for CI/monitor integration); default always exits 0
117
+
118
+ Exit: 0 always (or 1 if --exit-nonzero-on-stall + stalls found). Output: human table or JSON; reports "no transcripts found" gracefully.
119
+
120
+ ## buildlog.py — Uniform BUILDLOG entry appender
121
+
122
+ Ensures consistent BUILDLOG.md formatting across orchestrated work: timestamp + message + optional git HEAD ref.
123
+
124
+ - `buildlog.py "<message>" [--state-dir DIR] [--head] [--repo-path PATH]` — append entry to state/BUILDLOG.md
125
+
126
+ ## ensure_state.py — Scaffold checkpointing directories
127
+
128
+ Creates STATE.md and BUILDLOG.md templates in state directory if missing.
129
+
130
+ - `ensure_state.py --state-dir DIR` — scaffold templates
131
+
132
+ ## fleet_ledger.py — Outcome audit trail for dispatched agents
133
+
134
+ Append-only ledger of agent runs, resource use, and verdicts. Supports harvest (scan temp tasks) and rotate (archive old lines).
135
+
136
+ - `fleet_ledger.py append <ts> <agent_type> <model> <dur_sec> <tokens_in> <tokens_out> [verdict]`
137
+ - `fleet_ledger.py harvest` — scan session tasks and append missing outcomes
138
+ - `fleet_ledger.py rotate` — archive old lines when ledger exceeds ~200 lines
139
+
140
+ ## scanner_selftest.py — Regression harness for secret_scan.py
141
+
142
+ Validates scanner against TP/FP test vectors. Ensures self-scan is clean (no pragma reliance).
143
+
144
+ - `python scanner_selftest.py [--temp-dir DIR]` — run full test suite, exit 0 only if all pass
145
+
146
+ ## prepublish_scan.py — Pre-publish gate (history + staged)
147
+
148
+ Runs full git history and staged-changes scans before public release. Both must pass.
149
+
150
+ - `prepublish_scan.py [--repo PATH]` — scan full history + staged changes, exit 0 only if CLEAR-TO-PUBLISH
151
+
152
+ ## eod_sweep.py — End-of-day repository safety check
153
+
154
+ Scans listed repos for: dirty working tree, unpushed commits, untracked files. Optional auto-push on safe repos.
155
+
156
+ - `eod_sweep.py [--repos PATHS] [--readonly-repos PATHS] [--fix-push]` — check repo health, optionally push
157
+
158
+ ## orchestrator_status.py — Atomic orchestrator status updates (wave-8+)
159
+
160
+ Manages orchestrator heartbeat and activity tracking for SSE status section and wave-9+ multi-orchestrator coordination.
161
+
162
+ - `python orchestrator_status.py set --activity "dispatching wave-8" --phase audit [--id main --role orchestrator]` — atomically write status
163
+ - `python orchestrator_status.py clear` — remove status file
164
+
165
+ Writes `state/orchestrator-status.json` atomically (temp+replace). Forward-compatible with bare-object → list normalization in serve.py. Exit: 0=success, 1=error.
166
+
167
+ ## ci_merge_wait.py — CI-gated merge helper
168
+
169
+ Polls gh pr view until all status checks conclude (SUCCESS/FAILURE), then merges ONLY if all checks pass. The `gh pr merge` call is STRUCTURALLY UNREACHABLE unless CI status is SUCCESS — this prevents merge-on-CI-failure edge cases (e.g., wave-7 PR #80).
170
+
171
+ - `python ci_merge_wait.py <PR-number> [--timeout SECONDS] [--poll SECONDS] [--merge-method merge|squash|rebase]`
172
+
173
+ Exit codes:
174
+ - 0 = PR merged successfully
175
+ - 2 = CI checks failed (do NOT merge, prints which check failed)
176
+ - 3 = Timeout waiting for CI to conclude
177
+ - 4 = PR not mergeable or has merge conflicts
178
+
179
+ Requires: `gh` CLI available on PATH. Gracefully exits with error if gh is missing.
180
+
181
+ Implementation note: This is the reusable form of the orchestrator's merge-gating discipline (buildsystem Phase 1). Core invariant: the merge call is STRUCTURALLY UNREACHABLE on any status other than SUCCESS.
182
+
183
+ ## Invariants
184
+
185
+ - **Dependency-light**: Python tools must work on base Python 3 (no pip installs).
186
+ - **CRLF-safe shell**: no line continuations in .sh scripts; Git Bash + Linux compatible.
187
+ - **Never print secrets**: mask as pattern name + masked value only.
188
+ - **Config-driven paths**: heartbeat/ledger/logs use AESOP_STATE_ROOT env var (default ./state) or CLI args; no hardcoded personal paths.
189
+ - **Fragment-assembled secrets in tests**: scanner_selftest.py concatenates dummy secrets at runtime so pattern text never appears contiguously (self-scan invariant).
190
+ - **verify_*.py are mandatory CI gates**: verify_dash.py and verify_submit_encoding.py are required pre-push gates; use `--allow-skip` only in truly browserless environments (CI must run both).
191
+ - **lock.mjs is the ONLY lock implementation**: never reimplement locking in proposals.mjs or elsewhere; all proposals/state updates must use fail-closed lock.mjs with exponential backoff + stale-lock breaking.
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env bash
2
+ # agent-forensics.sh — incident forensics tool
3
+ # Reconstruct agent behavior as of a commit using git plumbing.
4
+ # Usage:
5
+ # bash tools/agent-forensics.sh <commit>
6
+ # bash tools/agent-forensics.sh --diff <commitA> <commitB>
7
+ #
8
+ # Prints:
9
+ # - Commit header (hash, date, subject)
10
+ # - Rules snapshot (docs/CARDINAL-RULES.md)
11
+ # - CLAUDE.md (if present)
12
+ # - STATE.md (if present)
13
+ # - Last 30 lines of BUILDLOG.md (if tracked)
14
+ # - For --diff: behavior changes between two commits
15
+ #
16
+ # Exit codes: 0 on success, 1 on error (never raw git stack traces).
17
+ # Requires: git, head, tail, wc, grep
18
+ # CRLF-safe: no line continuations, Git Bash + Linux compatible.
19
+
20
+ set -e
21
+
22
+ show_error() {
23
+ printf 'Error: %s\n' "$1" >&2
24
+ exit 1
25
+ }
26
+
27
+ show_usage() {
28
+ cat <<'EOF'
29
+ Usage:
30
+ bash tools/agent-forensics.sh <commit> Forensics at <commit>
31
+ bash tools/agent-forensics.sh --diff <commitA> <commitB> Behavior diff
32
+ EOF
33
+ exit 1
34
+ }
35
+
36
+ # --- Commit header (hash, date, subject) ---
37
+ print_commit_header() {
38
+ local commit="$1"
39
+
40
+ # Verify commit exists
41
+ if ! git cat-file -t "$commit" >/dev/null 2>&1; then
42
+ show_error "unknown commit: $commit"
43
+ fi
44
+
45
+ local hash
46
+ local date
47
+ local subject
48
+
49
+ hash=$(git rev-parse "$commit" 2>/dev/null || show_error "commit parse failed: $commit")
50
+ date=$(git log -1 --format=%ai "$commit" 2>/dev/null || show_error "failed to read commit date")
51
+ subject=$(git log -1 --format=%s "$commit" 2>/dev/null || show_error "failed to read commit subject")
52
+
53
+ printf '# Forensics @ %s\n\n' "$hash"
54
+ printf 'Commit: %s\n' "$hash"
55
+ printf 'Date: %s\n' "$date"
56
+ printf 'Subject: %s\n\n' "$subject"
57
+ }
58
+
59
+ # --- Print first ~40 lines of docs/CARDINAL-RULES.md ---
60
+ print_rules() {
61
+ local commit="$1"
62
+
63
+ printf '## Rules (docs/CARDINAL-RULES.md)\n\n'
64
+
65
+ if ! git cat-file -e "$commit:docs/CARDINAL-RULES.md" 2>/dev/null; then
66
+ printf '(file not present at this commit)\n\n'
67
+ return
68
+ fi
69
+
70
+ git show "$commit:docs/CARDINAL-RULES.md" 2>/dev/null | head -40
71
+
72
+ # Check if file was truncated
73
+ local total_lines
74
+ total_lines=$(git show "$commit:docs/CARDINAL-RULES.md" 2>/dev/null | wc -l)
75
+ if [ "$total_lines" -gt 40 ]; then
76
+ printf '\n[... %d more lines; see git show %s:docs/CARDINAL-RULES.md]\n' "$((total_lines - 40))" "$commit"
77
+ fi
78
+ printf '\n\n'
79
+ }
80
+
81
+ # --- Print CLAUDE.md if present ---
82
+ print_claude_md() {
83
+ local commit="$1"
84
+
85
+ printf '## CLAUDE.md\n\n'
86
+
87
+ if ! git cat-file -e "$commit:CLAUDE.md" 2>/dev/null; then
88
+ printf '(not present at this commit)\n\n'
89
+ return
90
+ fi
91
+
92
+ git show "$commit:CLAUDE.md" 2>/dev/null
93
+ printf '\n\n'
94
+ }
95
+
96
+ # --- Print STATE.md if present ---
97
+ print_state_md() {
98
+ local commit="$1"
99
+
100
+ printf '## STATE.md\n\n'
101
+
102
+ if ! git cat-file -e "$commit:STATE.md" 2>/dev/null; then
103
+ printf '(not present at this commit)\n\n'
104
+ return
105
+ fi
106
+
107
+ git show "$commit:STATE.md" 2>/dev/null
108
+ printf '\n\n'
109
+ }
110
+
111
+ # --- Print last 30 lines of BUILDLOG.md ---
112
+ print_buildlog() {
113
+ local commit="$1"
114
+
115
+ printf '## BUILDLOG.md (last 30 lines)\n\n'
116
+
117
+ if ! git cat-file -e "$commit:BUILDLOG.md" 2>/dev/null; then
118
+ printf '(not tracked at this commit)\n\n'
119
+ return
120
+ fi
121
+
122
+ git show "$commit:BUILDLOG.md" 2>/dev/null | tail -30
123
+ printf '\n\n'
124
+ }
125
+
126
+ # --- Diff behavior between two commits ---
127
+ diff_behavior() {
128
+ local commitA="$1"
129
+ local commitB="$2"
130
+
131
+ # Verify both commits exist
132
+ if ! git cat-file -t "$commitA" >/dev/null 2>&1; then
133
+ show_error "unknown commit: $commitA"
134
+ fi
135
+ if ! git cat-file -t "$commitB" >/dev/null 2>&1; then
136
+ show_error "unknown commit: $commitB"
137
+ fi
138
+
139
+ printf '# Behavior Diff\n\n'
140
+ printf '%s -> %s\n\n' "$(git rev-parse "$commitA")" "$(git rev-parse "$commitB")"
141
+
142
+ # Files that affect behavior: rules, CLAUDE.md, STATE.md, hooks, monitor/CHARTER.md
143
+ local behavior_files
144
+ behavior_files="CLAUDE.md STATE.md docs/CARDINAL-RULES.md docs/DISPATCH-MODEL.md docs/GOVERNANCE.md hooks/ monitor/CHARTER.md"
145
+
146
+ printf '## Changes in behavior-controlling files\n\n'
147
+
148
+ local has_changes=0
149
+ for file in $behavior_files; do
150
+ if git diff "$commitA" "$commitB" --name-only -- "$file" 2>/dev/null | grep -q .; then
151
+ has_changes=1
152
+ printf '### %s\n\n' "$file"
153
+ git diff "$commitA" "$commitB" -- "$file" 2>/dev/null | head -100
154
+ printf '\n'
155
+ fi
156
+ done
157
+
158
+ if [ "$has_changes" -eq 0 ]; then
159
+ printf '(no changes in behavior-controlling files)\n\n'
160
+ fi
161
+ }
162
+
163
+ # --- Main dispatch ---
164
+ case "${1:-}" in
165
+ --diff)
166
+ if [ $# -lt 3 ]; then
167
+ show_error "--diff requires two commits: --diff <commitA> <commitB>"
168
+ fi
169
+ diff_behavior "$2" "$3"
170
+ ;;
171
+ --help|-h)
172
+ show_usage
173
+ ;;
174
+ '')
175
+ show_usage
176
+ ;;
177
+ *)
178
+ print_commit_header "$1"
179
+ print_rules "$1"
180
+ print_claude_md "$1"
181
+ print_state_md "$1"
182
+ print_buildlog "$1"
183
+ ;;
184
+ esac
185
+
186
+ exit 0