@matt82198/aesop 0.1.0-beta.3 → 0.1.0-beta.5
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.
- package/CHANGELOG.md +79 -95
- package/CLAUDE-TEMPLATE.md +115 -0
- package/README.md +126 -245
- package/aesop.config.example.json +34 -26
- package/bin/CLAUDE.md +67 -0
- package/bin/cli.js +593 -36
- package/daemons/CLAUDE.md +36 -0
- package/daemons/backup-fleet.sh +92 -21
- package/daemons/run-watchdog.sh +95 -12
- package/dash/CLAUDE.md +32 -0
- package/dash/dash-extra.mjs +194 -21
- package/dash/watchdog-gui.sh +23 -6
- package/docs/BEHAVIORAL-PR-REVIEW.md +86 -0
- package/docs/CHECKPOINTING.md +100 -0
- package/docs/DISPATCH-MODEL.md +11 -7
- package/docs/FORENSICS.md +204 -0
- package/docs/GOVERNANCE.md +4 -2
- package/docs/HOOK-INSTALL.md +307 -0
- package/docs/HOW-THE-LOOP-WORKS.md +175 -0
- package/docs/MEMORY-TEMPLATE.md +61 -0
- package/docs/README.md +44 -0
- package/docs/RELEASING.md +159 -0
- package/docs/RELIABILITY.md +61 -0
- package/docs/RESTORE.md +397 -0
- package/docs/SCRIPTS-POLICY.md +97 -0
- package/docs/archive/README.md +3 -0
- package/docs/archive/spikes/tiered-cognition/ACTIVATION.md +125 -0
- package/docs/archive/spikes/tiered-cognition/DESIGN.md +287 -0
- package/docs/archive/spikes/tiered-cognition/FINDINGS.md +113 -0
- package/docs/archive/spikes/tiered-cognition/README.md +27 -0
- package/docs/archive/spikes/tiered-cognition/aesop-cognition.example.md +32 -0
- package/docs/archive/spikes/tiered-cognition/force-model-policy.merged.mjs +673 -0
- package/docs/archive/spikes/tiered-cognition/strip-tools-hook.mjs +434 -0
- package/docs/case-study-portfolio.md +61 -0
- package/docs/self-stats-data.json +11 -0
- package/docs/templates/FLEET-OPS-ANALYSIS.example.md +27 -0
- package/docs/templates/FLEET-OPS-RECOMMENDATIONS.example.md +24 -0
- package/docs/templates/PROPOSALS-LOG.example.md +64 -0
- package/hooks/CLAUDE.md +61 -0
- package/hooks/claude/force-model-policy.mjs +159 -0
- package/hooks/pre-push-policy.sh +753 -0
- package/mcp/CLAUDE.md +213 -0
- package/mcp/package.json +26 -0
- package/mcp/server.mjs +543 -0
- package/monitor/.signal-state.json +3 -0
- package/monitor/ACTIONS.log +1 -0
- package/monitor/BRIEF.md +24 -0
- package/monitor/CHARTER.md +75 -88
- package/monitor/CLAUDE.md +40 -0
- package/monitor/SIGNALS.json +54 -0
- package/monitor/collect-signals.mjs +613 -61
- package/package.json +15 -3
- package/scan/fleet-scan.example.mjs +292 -0
- package/skills/CLAUDE.md +3 -0
- package/skills/healthcheck/SKILL.md +44 -0
- package/skills/power/SKILL.md +161 -0
- package/state_store/CLAUDE.md +39 -0
- package/state_store/__init__.py +24 -0
- package/state_store/__pycache__/__init__.cpython-314.pyc +0 -0
- package/state_store/__pycache__/api.cpython-314.pyc +0 -0
- package/state_store/__pycache__/export.cpython-314.pyc +0 -0
- package/state_store/__pycache__/ingest.cpython-314.pyc +0 -0
- package/state_store/__pycache__/projections.cpython-314.pyc +0 -0
- package/state_store/__pycache__/store.cpython-314.pyc +0 -0
- package/state_store/api.py +35 -0
- package/state_store/export.py +19 -0
- package/state_store/ingest.py +24 -0
- package/state_store/projections.py +52 -0
- package/state_store/store.py +102 -0
- package/tools/CLAUDE.md +72 -0
- package/tools/__pycache__/alert_bridge.cpython-314.pyc +0 -0
- package/tools/__pycache__/buildlog.cpython-314.pyc +0 -0
- package/tools/__pycache__/ci_merge_wait.cpython-314.pyc +0 -0
- package/tools/__pycache__/ensure_state.cpython-314.pyc +0 -0
- package/tools/__pycache__/eod_sweep.cpython-314.pyc +0 -0
- package/tools/__pycache__/fleet_ledger.cpython-314.pyc +0 -0
- package/tools/__pycache__/fleet_prompt_extractor.cpython-314.pyc +0 -0
- package/tools/__pycache__/healthcheck.cpython-314.pyc +0 -0
- package/tools/__pycache__/heartbeat.cpython-314.pyc +0 -0
- package/tools/__pycache__/inbox_drain.cpython-314.pyc +0 -0
- package/tools/__pycache__/launch_tui.cpython-314.pyc +0 -0
- package/tools/__pycache__/metrics_gate.cpython-314.pyc +0 -0
- package/tools/__pycache__/orchestrator_status.cpython-314.pyc +0 -0
- package/tools/__pycache__/power_selftest.cpython-314.pyc +0 -0
- package/tools/__pycache__/prepublish_scan.cpython-314.pyc +0 -0
- package/tools/__pycache__/rotate_logs.cpython-314.pyc +0 -0
- package/tools/__pycache__/scanner_selftest.cpython-314.pyc +0 -0
- package/tools/__pycache__/secret_scan.cpython-314.pyc +0 -0
- package/tools/__pycache__/self_stats.cpython-314.pyc +0 -0
- package/tools/__pycache__/session_usage_summary.cpython-314.pyc +0 -0
- package/tools/__pycache__/stall_check.cpython-314.pyc +0 -0
- package/tools/__pycache__/transcript_replay.cpython-314.pyc +0 -0
- package/tools/__pycache__/transcript_timeline.cpython-314.pyc +0 -0
- package/tools/__pycache__/verify_dash.cpython-314.pyc +0 -0
- package/tools/__pycache__/verify_submit_encoding.cpython-314.pyc +0 -0
- package/tools/agent-forensics.sh +186 -0
- package/tools/alert_bridge.py +449 -0
- package/tools/buildlog.py +182 -0
- package/tools/ci_merge_wait.py +317 -0
- package/tools/ensure_state.py +94 -0
- package/tools/eod_sweep.py +239 -0
- package/tools/fleet_ledger.py +272 -0
- package/tools/fleet_prompt_extractor.py +134 -0
- package/tools/healthcheck.py +296 -0
- package/tools/heartbeat.py +131 -0
- package/tools/inbox_drain.py +249 -0
- package/tools/launch_tui.py +51 -3
- package/tools/lock.mjs +211 -0
- package/tools/metrics_gate.py +205 -0
- package/tools/orchestrator_status.py +99 -0
- package/tools/power_selftest.py +386 -0
- package/tools/prepublish_scan.py +84 -0
- package/tools/proposals.mjs +248 -0
- package/tools/reconstitute.sh +467 -0
- package/tools/rotate_logs.py +228 -0
- package/tools/scanner_selftest.py +303 -0
- package/tools/secret_scan.py +138 -31
- package/tools/self_stats.py +509 -0
- package/tools/session_usage_summary.py +198 -0
- package/tools/stall_check.py +172 -0
- package/tools/svg_to_png.mjs +50 -0
- package/tools/transcript_replay.py +236 -0
- package/tools/transcript_timeline.py +184 -0
- package/tools/verify_dash.py +513 -0
- package/tools/verify_submit_encoding.py +202 -0
- package/ui/CLAUDE.md +125 -0
- package/ui/__pycache__/agents.cpython-314.pyc +0 -0
- package/ui/__pycache__/collectors.cpython-314.pyc +0 -0
- package/ui/__pycache__/config.cpython-314.pyc +0 -0
- package/ui/__pycache__/cost.cpython-314.pyc +0 -0
- package/ui/__pycache__/csrf.cpython-314.pyc +0 -0
- package/ui/__pycache__/handler.cpython-314.pyc +0 -0
- package/ui/__pycache__/render.cpython-314.pyc +0 -0
- package/ui/__pycache__/serve.cpython-314.pyc +0 -0
- package/ui/__pycache__/sse.cpython-314.pyc +0 -0
- package/ui/agents.py +183 -0
- package/ui/api/__init__.py +84 -0
- package/ui/api/__pycache__/__init__.cpython-314.pyc +0 -0
- package/ui/api/__pycache__/submit.cpython-314.pyc +0 -0
- package/ui/api/__pycache__/tracker.cpython-314.pyc +0 -0
- package/ui/api/submit.py +97 -0
- package/ui/api/tracker.py +116 -0
- package/ui/collectors.py +735 -0
- package/ui/config.py +123 -0
- package/ui/cost.py +260 -0
- package/ui/csrf.py +144 -0
- package/ui/handler.py +676 -0
- package/ui/render.py +49 -0
- package/ui/serve.py +68 -796
- package/ui/sse.py +183 -0
- package/ui/web/.gitattributes +13 -0
- package/ui/web/dist/assets/index-2LZDQirC.js +9 -0
- package/ui/web/dist/assets/index-D4M1qyOv.css +1 -0
- package/ui/web/dist/index.html +14 -0
- package/ui/web/index.html +13 -0
- package/ui/web/package-lock.json +2225 -0
- package/ui/web/package.json +26 -0
- package/ui/web/src/App.test.tsx +74 -0
- package/ui/web/src/App.tsx +142 -0
- package/ui/web/src/CONTRIBUTING-UI.md +49 -0
- package/ui/web/src/components/AgentRow.css +187 -0
- package/ui/web/src/components/AgentRow.test.tsx +209 -0
- package/ui/web/src/components/AgentRow.tsx +207 -0
- package/ui/web/src/components/AgentsPanel.css +108 -0
- package/ui/web/src/components/AgentsPanel.test.tsx +41 -0
- package/ui/web/src/components/AgentsPanel.tsx +58 -0
- package/ui/web/src/components/AlertsPanel.css +88 -0
- package/ui/web/src/components/AlertsPanel.test.tsx +51 -0
- package/ui/web/src/components/AlertsPanel.tsx +67 -0
- package/ui/web/src/components/BacklogPanel.test.tsx +126 -0
- package/ui/web/src/components/BacklogPanel.tsx +122 -0
- package/ui/web/src/components/CostChart.css +110 -0
- package/ui/web/src/components/CostChart.test.tsx +144 -0
- package/ui/web/src/components/CostChart.tsx +152 -0
- package/ui/web/src/components/CostTable.css +93 -0
- package/ui/web/src/components/CostTable.test.tsx +165 -0
- package/ui/web/src/components/CostTable.tsx +94 -0
- package/ui/web/src/components/EventsFeed.css +68 -0
- package/ui/web/src/components/EventsFeed.test.tsx +36 -0
- package/ui/web/src/components/EventsFeed.tsx +31 -0
- package/ui/web/src/components/HealthHeader.css +137 -0
- package/ui/web/src/components/HealthHeader.test.tsx +278 -0
- package/ui/web/src/components/HealthHeader.tsx +281 -0
- package/ui/web/src/components/InboxForm.css +135 -0
- package/ui/web/src/components/InboxForm.test.tsx +208 -0
- package/ui/web/src/components/InboxForm.tsx +116 -0
- package/ui/web/src/components/MessagesTail.module.css +144 -0
- package/ui/web/src/components/MessagesTail.test.tsx +176 -0
- package/ui/web/src/components/MessagesTail.tsx +94 -0
- package/ui/web/src/components/ReposPanel.css +90 -0
- package/ui/web/src/components/ReposPanel.test.tsx +45 -0
- package/ui/web/src/components/ReposPanel.tsx +67 -0
- package/ui/web/src/components/Scorecard.css +106 -0
- package/ui/web/src/components/Scorecard.test.tsx +117 -0
- package/ui/web/src/components/Scorecard.tsx +85 -0
- package/ui/web/src/components/Timeline.module.css +151 -0
- package/ui/web/src/components/Timeline.test.tsx +215 -0
- package/ui/web/src/components/Timeline.tsx +99 -0
- package/ui/web/src/components/TrackerBoard.test.tsx +121 -0
- package/ui/web/src/components/TrackerBoard.tsx +107 -0
- package/ui/web/src/components/TrackerCard.test.tsx +180 -0
- package/ui/web/src/components/TrackerCard.tsx +160 -0
- package/ui/web/src/components/TrackerForm.test.tsx +189 -0
- package/ui/web/src/components/TrackerForm.tsx +144 -0
- package/ui/web/src/lib/api.ts +218 -0
- package/ui/web/src/lib/format.test.ts +89 -0
- package/ui/web/src/lib/format.ts +103 -0
- package/ui/web/src/lib/sanitizeUrl.test.ts +84 -0
- package/ui/web/src/lib/sanitizeUrl.ts +38 -0
- package/ui/web/src/lib/types.ts +230 -0
- package/ui/web/src/lib/useHashRoute.test.ts +60 -0
- package/ui/web/src/lib/useHashRoute.ts +23 -0
- package/ui/web/src/lib/useSSE.ts +175 -0
- package/ui/web/src/main.tsx +10 -0
- package/ui/web/src/styles/global.css +179 -0
- package/ui/web/src/styles/theme.css +184 -0
- package/ui/web/src/styles/work.css +572 -0
- package/ui/web/src/test/fixtures.ts +385 -0
- package/ui/web/src/test/setup.ts +49 -0
- package/ui/web/src/views/Activity.module.css +43 -0
- package/ui/web/src/views/Activity.test.tsx +89 -0
- package/ui/web/src/views/Activity.tsx +31 -0
- package/ui/web/src/views/Cost.css +87 -0
- package/ui/web/src/views/Cost.test.tsx +142 -0
- package/ui/web/src/views/Cost.tsx +54 -0
- package/ui/web/src/views/Overview.css +51 -0
- package/ui/web/src/views/Overview.test.tsx +76 -0
- package/ui/web/src/views/Overview.tsx +46 -0
- package/ui/web/src/views/Work.test.tsx +82 -0
- package/ui/web/src/views/Work.tsx +79 -0
- package/ui/web/src/vite-env.d.ts +10 -0
- package/ui/web/tsconfig.json +22 -0
- package/ui/web/vite.config.ts +25 -0
- package/ui/web/vitest.config.ts +12 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Forensics: Making Agent Failures Git-Bisectable
|
|
2
|
+
|
|
3
|
+
## The Pillar
|
|
4
|
+
|
|
5
|
+
Agent behavior is source code. When an agent misbehaves, you can reconstruct exactly what rules, memory, and policies were active at the time of failure — then bisect the codebase to find the commit that broke it. This turns vague "the agent was weird yesterday" into a reproducible root cause.
|
|
6
|
+
|
|
7
|
+
**Key principle**: Rules (CARDINAL-RULES.md, CLAUDE.md), checkpoints (STATE.md, BUILDLOG.md), and policies (hooks/, monitor/CHARTER.md) are the agent's executable spec. Versioning them in git means agent failures become debuggable like code regressions.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
### `tools/agent-forensics.sh`
|
|
12
|
+
|
|
13
|
+
Read-only incident-forensics tool. Reconstructs agent behavior from git history using only git plumbing (no checkout, no working-tree mutation).
|
|
14
|
+
|
|
15
|
+
**Usage:**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Snapshot agent behavior at a commit
|
|
19
|
+
bash tools/agent-forensics.sh <commit>
|
|
20
|
+
|
|
21
|
+
# Show what behavior changed between two commits
|
|
22
|
+
bash tools/agent-forensics.sh --diff <commitA> <commitB>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Output:**
|
|
26
|
+
- Commit header (hash, date, subject)
|
|
27
|
+
- First ~40 lines of rules (docs/CARDINAL-RULES.md)
|
|
28
|
+
- CLAUDE.md snapshot (if present)
|
|
29
|
+
- STATE.md snapshot (if present)
|
|
30
|
+
- Last 30 lines of BUILDLOG.md (if tracked)
|
|
31
|
+
- For `--diff`: side-by-side changes in CLAUDE.md, STATE.md, docs/, hooks/, monitor/CHARTER.md
|
|
32
|
+
|
|
33
|
+
**Exit codes:**
|
|
34
|
+
- `0` on success
|
|
35
|
+
- `1` on error (unknown commit, I/O failure); clean error message printed to stderr, never raw stack traces
|
|
36
|
+
|
|
37
|
+
**Examples:**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Forensics at HEAD
|
|
41
|
+
bash tools/agent-forensics.sh HEAD
|
|
42
|
+
|
|
43
|
+
# Forensics at a specific commit
|
|
44
|
+
bash tools/agent-forensics.sh 858ac08
|
|
45
|
+
|
|
46
|
+
# What changed in behavior between two releases?
|
|
47
|
+
bash tools/agent-forensics.sh --diff v0.1.0-beta.2 v0.1.0-beta.3
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Recipe: Bisect an Agent Regression
|
|
51
|
+
|
|
52
|
+
When an agent's behavior regressed (e.g., "rules started being ignored on July 10"):
|
|
53
|
+
|
|
54
|
+
1. **Identify the failure time window.** (e.g., "agent started proposing without filtering on 2026-07-10 18:00 UTC")
|
|
55
|
+
|
|
56
|
+
2. **Run forensics at the last known-good commit** and the first known-bad commit:
|
|
57
|
+
```bash
|
|
58
|
+
bash tools/agent-forensics.sh <good-commit>
|
|
59
|
+
bash tools/agent-forensics.sh <bad-commit>
|
|
60
|
+
```
|
|
61
|
+
Compare the output manually. Often you'll spot the culprit immediately (e.g., a rule was deleted, CLAUDE.md lost a policy).
|
|
62
|
+
|
|
63
|
+
3. **If manual inspection doesn't find it, bisect:**
|
|
64
|
+
```bash
|
|
65
|
+
git bisect start
|
|
66
|
+
git bisect bad <bad-commit>
|
|
67
|
+
git bisect good <good-commit>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
4. **Write a behavior predicate** (a test that reproduces the failure):
|
|
71
|
+
```bash
|
|
72
|
+
# Example: check that docs/CARDINAL-RULES.md mentions "Haiku" at this commit
|
|
73
|
+
git bisect run bash -c '
|
|
74
|
+
git show HEAD:docs/CARDINAL-RULES.md | grep -q "Haiku" && exit 0 || exit 1
|
|
75
|
+
'
|
|
76
|
+
```
|
|
77
|
+
Bisect will pin-point the exact commit that broke the behavior.
|
|
78
|
+
|
|
79
|
+
5. **Review the offending commit** using `--diff`:
|
|
80
|
+
```bash
|
|
81
|
+
bash tools/agent-forensics.sh --diff <parent-of-bad> <bad-commit>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
6. **Fix the issue** (restore the rule, reinstate the policy, etc.) in a new commit on a feature branch.
|
|
85
|
+
|
|
86
|
+
## What to Check When an Incident Happens
|
|
87
|
+
|
|
88
|
+
### Incident Discovery
|
|
89
|
+
1. Capture the **current behavior** (what went wrong, when, which agent):
|
|
90
|
+
- Run `bash tools/agent-forensics.sh HEAD` to see rules + state at time of report.
|
|
91
|
+
- Check logs in `state/FLEET-BACKUP.log`, `state/ACTIONS.log`, or agent transcript.
|
|
92
|
+
- Note the **exact timestamp** of the first wrong behavior.
|
|
93
|
+
|
|
94
|
+
2. **Cross-reference the timestamp** with `git log --since="<time>"` to find commits near that time.
|
|
95
|
+
|
|
96
|
+
### Root Cause Narrowing
|
|
97
|
+
3. **Did a rule change?**
|
|
98
|
+
```bash
|
|
99
|
+
git log -p --since="<before-incident>" -- docs/CARDINAL-RULES.md CLAUDE.md | head -100
|
|
100
|
+
```
|
|
101
|
+
Look for deletions, edits to retry logic, dispatch budgets, or policy changes.
|
|
102
|
+
|
|
103
|
+
4. **Did state get corrupted?**
|
|
104
|
+
```bash
|
|
105
|
+
bash tools/agent-forensics.sh <commit-before-incident>
|
|
106
|
+
bash tools/agent-forensics.sh <commit-at-incident>
|
|
107
|
+
# Compare STATE.md and BUILDLOG.md for anomalies (e.g., PHASE stuck, cost spike)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
5. **Did a hook fail silently?**
|
|
111
|
+
```bash
|
|
112
|
+
git log -p --since="<before-incident>" -- hooks/ monitor/CHARTER.md
|
|
113
|
+
cat state/SECURITY-AUDIT.log | tail -50
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Confirmation via Bisect
|
|
117
|
+
6. Once you have a suspect commit or time range, use the bisect recipe above to narrow it down.
|
|
118
|
+
|
|
119
|
+
7. **Write the fix** in a feature branch. Update CLAUDE.md if the rule itself was wrong; add an explanatory comment referencing the incident date.
|
|
120
|
+
|
|
121
|
+
8. **Add a regression test:** if the incident was "agent ignored retry cap," add a check in pre-push-policy.sh or a future test-harness to catch that rule drifting again.
|
|
122
|
+
|
|
123
|
+
## Durable Artifacts to Check
|
|
124
|
+
|
|
125
|
+
| File | Purpose | Checked via |
|
|
126
|
+
|------|---------|-------------|
|
|
127
|
+
| **docs/CARDINAL-RULES.md** | Dispatch, cost, retry, TDD policy | `bash tools/agent-forensics.sh <commit>` |
|
|
128
|
+
| **CLAUDE.md** | Project-level rules, branch discipline, commit gates | manual forensics; git log |
|
|
129
|
+
| **STATE.md** | Current phase, locked decisions, next steps | forensics snapshot |
|
|
130
|
+
| **BUILDLOG.md** | Append-only cycle log (cost, actions, phase changes) | forensics (last 5 entries) |
|
|
131
|
+
| **hooks/** | Pre-push policy, secret-scan gate, lint checks | git show or forensics --diff |
|
|
132
|
+
| **monitor/CHARTER.md** | Orchestration monitor config (AUTO/PROPOSE thresholds) | forensics --diff |
|
|
133
|
+
|
|
134
|
+
## Example: Debugging a Cost Spike
|
|
135
|
+
|
|
136
|
+
**Symptom**: Agent token spend jumped from 50k to 200k tokens on 2026-07-10.
|
|
137
|
+
|
|
138
|
+
1. Run forensics at the spike time:
|
|
139
|
+
```bash
|
|
140
|
+
bash tools/agent-forensics.sh <commit-on-2026-07-10>
|
|
141
|
+
```
|
|
142
|
+
Check STATE.md and BUILDLOG.md. Look for:
|
|
143
|
+
- Did `parallelism` increase? (more agents spawned → more tokens)
|
|
144
|
+
- Did `retries` increase? (retry cap lowered → more respawns)
|
|
145
|
+
- Did `model` change to Opus? (rule 1 violation)
|
|
146
|
+
|
|
147
|
+
2. Narrow the window:
|
|
148
|
+
```bash
|
|
149
|
+
git log --oneline --since="2026-07-09" --until="2026-07-11"
|
|
150
|
+
bash tools/agent-forensics.sh --diff <2026-07-09-commit> <2026-07-10-spike-commit>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
3. If a rule changed (e.g., retries bumped from 3 to 6), fix it:
|
|
154
|
+
```bash
|
|
155
|
+
git log -p --since="2026-07-09" -- docs/CARDINAL-RULES.md | grep -A5 -B5 retries
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
4. Restore or correct the rule, commit, and monitor spend in the next cycle.
|
|
159
|
+
|
|
160
|
+
## Integration with the Refinement Loop
|
|
161
|
+
|
|
162
|
+
Forensic replay strengthens the orchestration refinement cycle by enabling rapid root-cause analysis when the fleet exhibits unexpected behavior changes.
|
|
163
|
+
|
|
164
|
+
**When to invoke forensics in refinement:**
|
|
165
|
+
|
|
166
|
+
1. **Suspected behavior regression** — If PROPOSALS.md or monitoring signals suggest rules changed unexpectedly:
|
|
167
|
+
```bash
|
|
168
|
+
# Find the suspect commit range
|
|
169
|
+
git log --oneline --all | head -20
|
|
170
|
+
|
|
171
|
+
# Compare behavior-controlling files between two commits
|
|
172
|
+
bash tools/agent-forensics.sh --diff <last-known-good> <suspect-bad>
|
|
173
|
+
```
|
|
174
|
+
This immediately shows what changed in CLAUDE.md, STATE.md, docs/, hooks/, and monitor/CHARTER.md.
|
|
175
|
+
|
|
176
|
+
2. **Cost or performance spike** — If SIGNALS.json reports elevated token spend or cost anomalies:
|
|
177
|
+
```bash
|
|
178
|
+
# Snapshot state at the anomaly time
|
|
179
|
+
bash tools/agent-forensics.sh <commit-at-spike-time>
|
|
180
|
+
```
|
|
181
|
+
Check the BUILDLOG.md excerpt for cost-related entries, parallelism changes, or dispatch budget adjustments.
|
|
182
|
+
|
|
183
|
+
3. **After merging a PR** — Before rolling to production, verify no unintended behavior changes were introduced:
|
|
184
|
+
```bash
|
|
185
|
+
# Compare main...feature-branch
|
|
186
|
+
bash tools/agent-forensics.sh --diff main <feature-branch-head>
|
|
187
|
+
```
|
|
188
|
+
If behavior-controlling files show unexpected changes, halt the merge and investigate.
|
|
189
|
+
|
|
190
|
+
**Output interpretation:**
|
|
191
|
+
|
|
192
|
+
- **CLAUDE.md or STATE.md changed** → review what rules or phase-state was altered
|
|
193
|
+
- **docs/CARDINAL-RULES.md modified** → check if dispatch budgets, retry caps, or cost levers were adjusted
|
|
194
|
+
- **hooks/ or monitor/CHARTER.md changed** → verify policy enforcement or signal thresholds are intentional
|
|
195
|
+
- **No changes in behavior-controlling files** → regression is likely in agent logic (non-behavior files) or external state
|
|
196
|
+
|
|
197
|
+
**Integration point:** The monitor/collect-signals.mjs can emit a PROPOSAL when it detects rule drift or state anomalies, prompting operators to run forensics on the linked commits to verify the change was intentional.
|
|
198
|
+
|
|
199
|
+
## See Also
|
|
200
|
+
|
|
201
|
+
- **docs/CARDINAL-RULES.md** — The executable spec for agent behavior.
|
|
202
|
+
- **docs/DISPATCH-MODEL.md** — Cost levers, parallelism, watchdog stall detection.
|
|
203
|
+
- **docs/GOVERNANCE.md** — Memory/state single-writer guarantees, proposal flow.
|
|
204
|
+
- **README.md** — Quick start; includes examples of running forensics on a live incident.
|
package/docs/GOVERNANCE.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Governance & Control
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**TL;DR**: Keep the system coherent: one instance of each loop (heartbeat protocol), single writer per control file (MEMORY.md, STATE.md), append-only logs, secret-scan gate on every push, feature branches only (never direct-to-main). Prevents races, data loss, credential leaks, and makes work auditable.
|
|
4
|
+
|
|
5
|
+
---
|
|
4
6
|
|
|
5
7
|
## Single-instance loops
|
|
6
8
|
|
|
@@ -41,7 +43,7 @@ Some files must have exactly one writer to prevent race conditions and data loss
|
|
|
41
43
|
|
|
42
44
|
## Inbox pattern for coordination
|
|
43
45
|
|
|
44
|
-
Loops that need to request memory updates or signal decisions use an append-only inbox. The orchestrator reviews inbox entries on every `/power` cycle.
|
|
46
|
+
Loops that need to request memory updates or signal decisions use an append-only inbox. The orchestrator reviews inbox entries on every `/power` cycle. See [skills/power/SKILL.md](../skills/power/SKILL.md) for orchestrator priming details.
|
|
45
47
|
|
|
46
48
|
### Inbox format
|
|
47
49
|
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Git Pre-Push Hook Installation Guide
|
|
2
|
+
|
|
3
|
+
**Ship a hook, not a memo.** The pre-push policy hook is **auto-installed during scaffold** (see below). This guide explains customization, verification, and org-wide distribution.
|
|
4
|
+
|
|
5
|
+
## Security Model: Local Convenience Defense Only
|
|
6
|
+
|
|
7
|
+
**IMPORTANT**: The pre-push hook is a **local-machine convenience defense** — it is **NOT cryptographic protection**. Any developer can bypass it with `git push --no-verify` or by editing/deleting `.git/hooks/pre-push`. The audit log (`SECURITY-AUDIT.log`) is stored locally and can also be edited by a user with file system access.
|
|
8
|
+
|
|
9
|
+
**Real enforcement requires server-side branch protection.** See GitHub Configuration below.
|
|
10
|
+
|
|
11
|
+
## What the Hook Does
|
|
12
|
+
|
|
13
|
+
`hooks/pre-push-policy.sh` enforces two checks at git push time:
|
|
14
|
+
|
|
15
|
+
1. **Branch Policy**: Blocks direct pushes to `main` or `master` branches (feature branches only).
|
|
16
|
+
2. **Secret Scan**: Runs `tools/secret_scan.py --staged` to detect credentials before they reach the remote.
|
|
17
|
+
|
|
18
|
+
Both blocks append a JSON audit record to `state/SECURITY-AUDIT.log` with timestamp, repo, reason, and user — creating a reviewable trail of policy enforcement.
|
|
19
|
+
|
|
20
|
+
## Auto-Installation During Scaffold
|
|
21
|
+
|
|
22
|
+
**Default Behavior:** When you scaffold a new aesop fleet with `npx @matt82198/aesop [target-dir]`, the CLI automatically installs the pre-push hook into `.git/hooks/pre-push`.
|
|
23
|
+
|
|
24
|
+
- **On Unix/macOS/Git Bash**: Creates a symlink so hook updates are automatic
|
|
25
|
+
- **On Windows**: Copies the hook directly (symlinks don't work reliably on all NTFS setups)
|
|
26
|
+
- **Idempotent**: Re-running scaffold doesn't clobber a user-customized hook
|
|
27
|
+
- **Preserve Existing**: If you have a different pre-push hook, scaffold warns and preserves it
|
|
28
|
+
- **Force Replace**: Use `npx @matt82198/aesop [target-dir] --force` to replace any existing hook
|
|
29
|
+
|
|
30
|
+
**Example:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Initial scaffold (creates and installs hook automatically)
|
|
34
|
+
npx @matt82198/aesop my-fleet
|
|
35
|
+
|
|
36
|
+
# Later: re-scaffold the same directory (preserves customizations)
|
|
37
|
+
npx @matt82198/aesop my-fleet
|
|
38
|
+
|
|
39
|
+
# Force replace (even if hook was customized)
|
|
40
|
+
npx @matt82198/aesop my-fleet --force
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Manual Installation
|
|
44
|
+
|
|
45
|
+
If you're installing into an existing repo (not scaffolded), or you need to manually add the hook:
|
|
46
|
+
|
|
47
|
+
### Option 1: Symlink (Linux / macOS / Git Bash on Windows)
|
|
48
|
+
|
|
49
|
+
The cleanest method — hook stays in sync with repo updates:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ln -s ../../hooks/pre-push-policy.sh .git/hooks/pre-push
|
|
53
|
+
chmod +x .git/hooks/pre-push
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Option 2: Copy (Windows, or to break sync)
|
|
57
|
+
|
|
58
|
+
Copy the hook directly into `.git/hooks/`:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
Copy-Item hooks\pre-push-policy.sh .git\hooks\pre-push
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
On Windows PowerShell, mark it executable if your git respects file mode:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git config core.filemode false
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Testing
|
|
71
|
+
|
|
72
|
+
Before committing to org-wide deployment, verify the hook works:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bash hooks/pre-push-policy.sh --test
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Expected output: **PASS** for all three checks (branch policy, feature branch allowance, audit log format).
|
|
79
|
+
|
|
80
|
+
## GitHub Configuration (Server-Side Enforcement)
|
|
81
|
+
|
|
82
|
+
To pair this local hook with real enforcement, enable branch protection on GitHub:
|
|
83
|
+
|
|
84
|
+
### Step 1: Create a Protected Branch
|
|
85
|
+
|
|
86
|
+
1. Go to your GitHub repo **Settings** > **Branches**.
|
|
87
|
+
2. Click **Add rule** under "Branch protection rules".
|
|
88
|
+
3. Enter branch name pattern: `main` (or `master`, depending on your default branch).
|
|
89
|
+
|
|
90
|
+
### Step 2: Enable Required Protections
|
|
91
|
+
|
|
92
|
+
- **Require pull request reviews before merging**: ✓ (enforces PR review workflow)
|
|
93
|
+
- **Require status checks to pass before merging**: ✓ (use this for CI)
|
|
94
|
+
- **Require branches to be up to date before merging**: ✓
|
|
95
|
+
- **Restrict who can push to matching branches**: ✓ (optional; allows only admins to push to main)
|
|
96
|
+
|
|
97
|
+
### Step 3: Dismiss Stale Reviews (Recommended)
|
|
98
|
+
|
|
99
|
+
- **Dismiss stale pull request approvals when new commits are pushed**: ✓
|
|
100
|
+
|
|
101
|
+
### Example: Minimal Protected Branch Rule for `main`
|
|
102
|
+
|
|
103
|
+
| Setting | Value |
|
|
104
|
+
|---------|-------|
|
|
105
|
+
| Branch name pattern | `main` |
|
|
106
|
+
| Require pull request reviews | Yes (1 approval) |
|
|
107
|
+
| Require status checks to pass | Yes (if you have CI) |
|
|
108
|
+
| Require branches up to date | Yes |
|
|
109
|
+
| Restrict pushes to | Admins only |
|
|
110
|
+
|
|
111
|
+
Once this is configured, even if a developer bypasses the local hook with `--no-verify`, the remote will **refuse the push**.
|
|
112
|
+
|
|
113
|
+
## Audit Log Format & Integrity
|
|
114
|
+
|
|
115
|
+
Each block writes one JSON line to `${AESOP_ROOT:-$HOME/aesop}/state/SECURITY-AUDIT.log`:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{"seq":1,"prev_hash":"GENESIS","ts":"2025-07-12T14:32:01Z","repo":"aesop","event":"push_blocked","reason":"secret_scan_failure","user":"alice"}
|
|
119
|
+
{"seq":2,"prev_hash":"f4b92becb47baa447e839330cf3c0c6e8dea947acc9ec372bb99063ee416d036","ts":"2025-07-12T14:32:02Z","repo":"aesop","event":"push_blocked","reason":"secret_scan_failure","user":"bob"}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Fields:**
|
|
123
|
+
- `seq`: Monotonically increasing sequence number (starts at 1). Enables detection of truncation or missing entries.
|
|
124
|
+
- `prev_hash`: SHA-256 hash of the previous line (without trailing newline). First entry uses `"GENESIS"`. Enables tampering detection.
|
|
125
|
+
- `ts`: ISO-8601 UTC timestamp
|
|
126
|
+
- `repo`: Repository basename
|
|
127
|
+
- `event`: Always `push_blocked` (or `secret_scan_unavailable`)
|
|
128
|
+
- `reason`: `push_to_protected_branch`, `secret_scan_failure`, or other block reason
|
|
129
|
+
- `user`: Git user.name (fallback: "unknown"); all special characters and control chars are JSON-escaped
|
|
130
|
+
|
|
131
|
+
**Write Safety & Concurrent Access:**
|
|
132
|
+
All audit log writes are protected by a file-system atomic lock (`.audit-log-lock/` directory). This ensures that concurrent pushes from different repositories (or simultaneous local pushes) do not corrupt the hash chain. The lock has a 300-second stale-lock recovery mechanism to handle crashed holders.
|
|
133
|
+
|
|
134
|
+
**Tail Hash Anchor:**
|
|
135
|
+
Each write also updates `${AESOP_ROOT:-$HOME/aesop}/state/.audit-tail-hash` with the SHA-256 hash of the newly appended line. This sidecar file acts as an anchor against tail truncation.
|
|
136
|
+
|
|
137
|
+
### Verifying Audit Log Integrity
|
|
138
|
+
|
|
139
|
+
The hash chain and truncation anchor allow you to detect if someone edited, deleted, or truncated audit log entries:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
bash hooks/pre-push-policy.sh --verify-audit-log
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Output on intact log:
|
|
146
|
+
```
|
|
147
|
+
Audit log verification OK (42 entries)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Output if line tampering is detected:
|
|
151
|
+
```
|
|
152
|
+
Error: Hash chain broken at line 15
|
|
153
|
+
Expected prev_hash: f4b92becb47baa447e839330cf3c0c6e8dea947acc9ec372bb99063ee416d036
|
|
154
|
+
Actual prev_hash: abc123...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Output if tail truncation is detected:
|
|
158
|
+
```
|
|
159
|
+
TRUNCATION SUSPECTED: Tail hash mismatch (stored: f4b..., actual: 8e6...)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The truncation detection compares the stored tail hash (`state/.audit-tail-hash`) against the actual SHA-256 hash of the current log's last line. If someone deletes the last N lines, the hashes will not match.
|
|
163
|
+
|
|
164
|
+
**Note**: Verification is a convenience check; it does not prevent tampering on a machine where the attacker has file system access. For real auditability, centralize audit logs to a secure remote (e.g., CloudWatch, Datadog, or a separate immutable log server).
|
|
165
|
+
|
|
166
|
+
Parse the log with standard JSON tools:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
cat state/SECURITY-AUDIT.log | jq '.reason' | sort | uniq -c
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Org Distribution & Customization
|
|
173
|
+
|
|
174
|
+
### Standard Distribution
|
|
175
|
+
|
|
176
|
+
1. Commit the hook to a shared repo or policy repository.
|
|
177
|
+
2. Instruct teams to symlink or copy per option above.
|
|
178
|
+
3. Communicate audit log location and parsing examples.
|
|
179
|
+
|
|
180
|
+
### Customizing Checks
|
|
181
|
+
|
|
182
|
+
To add org-specific rules (e.g., branch naming conventions, required CI status):
|
|
183
|
+
|
|
184
|
+
1. Edit `hooks/pre-push-policy.sh` and add a new check function:
|
|
185
|
+
```bash
|
|
186
|
+
check_my_org_policy() {
|
|
187
|
+
# your logic here
|
|
188
|
+
return 0 or 1
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
2. Call it from `main()` before exit, and log blocks:
|
|
193
|
+
```bash
|
|
194
|
+
if ! check_my_org_policy; then
|
|
195
|
+
log_block "my_org_policy_violation"
|
|
196
|
+
exit 1
|
|
197
|
+
fi
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
3. Update `run_test_mode()` to include test cases for the new check.
|
|
201
|
+
|
|
202
|
+
4. Commit and redistribute.
|
|
203
|
+
|
|
204
|
+
### Disabling in CI/CD
|
|
205
|
+
|
|
206
|
+
If CI systems need to bypass the hook (e.g., for automated releases), set:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
export GIT_SKIP_HOOKS=pre-push
|
|
210
|
+
git push
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Or use `git push --no-verify` (explicitly allowing humans to opt out).
|
|
214
|
+
|
|
215
|
+
## Audit Log Rotation
|
|
216
|
+
|
|
217
|
+
The audit log is append-only. For long-running deployments, consider log rotation:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
if [ -f state/SECURITY-AUDIT.log ] && [ $(stat -f%z state/SECURITY-AUDIT.log 2>/dev/null || stat -c%s state/SECURITY-AUDIT.log) -gt 1000000 ]; then
|
|
221
|
+
mv state/SECURITY-AUDIT.log "state/SECURITY-AUDIT.$(date +%Y%m%d-%H%M%S).log"
|
|
222
|
+
fi
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Troubleshooting
|
|
226
|
+
|
|
227
|
+
**Hook not running?**
|
|
228
|
+
- Verify `.git/hooks/pre-push` exists and is executable: `ls -la .git/hooks/pre-push`
|
|
229
|
+
- Check symlink target if using option 1: `readlink .git/hooks/pre-push`
|
|
230
|
+
- Git version must be 1.8.2+; run `git --version`
|
|
231
|
+
|
|
232
|
+
**"Push blocked" but no reason in audit log?**
|
|
233
|
+
- Check `${AESOP_ROOT:-$HOME/aesop}/state/` directory exists and is writable.
|
|
234
|
+
- Verify git config: `git config user.name` is not empty.
|
|
235
|
+
|
|
236
|
+
**Secret scan warns but doesn't block?**
|
|
237
|
+
- `secret_scan.py` must exist at `AESOP_ROOT/tools/secret_scan.py`.
|
|
238
|
+
- If missing, the hook warns but allows push (fail-open default).
|
|
239
|
+
- For fail-closed behavior, edit the hook and remove the early `return 0`.
|
|
240
|
+
|
|
241
|
+
## References
|
|
242
|
+
|
|
243
|
+
- Hook source: `hooks/pre-push-policy.sh`
|
|
244
|
+
- Secret scanner: `tools/secret_scan.py`
|
|
245
|
+
- Audit log location: `state/SECURITY-AUDIT.log` (git-ignored)
|
|
246
|
+
- Cardinal rule: [CARDINAL-RULES.md](CARDINAL-RULES.md)
|
|
247
|
+
|
|
248
|
+
## Claude Code hooks
|
|
249
|
+
|
|
250
|
+
Beyond git hooks, aesop ships policy for the agent harness itself.
|
|
251
|
+
`hooks/claude/force-model-policy.mjs` is a Claude Code **PreToolUse** hook that
|
|
252
|
+
enforces the "subagents are always Haiku" cardinal rule in code: every `Agent`
|
|
253
|
+
or `Task` dispatch whose `model` is absent or non-compliant is rewritten to the
|
|
254
|
+
policy model before the subagent launches. Versioned in git, it is org policy
|
|
255
|
+
you can review, diff, and test — not a memo agents can forget.
|
|
256
|
+
|
|
257
|
+
### What it enforces
|
|
258
|
+
|
|
259
|
+
- **Model policy**: subagent dispatches run on `haiku` by default. If
|
|
260
|
+
`aesop.config.json` defines `cardinal_rules.subagent_model` (looked up in
|
|
261
|
+
`$AESOP_ROOT`, then the working directory), that model is enforced instead.
|
|
262
|
+
- **Rewrite, not block**: non-compliant dispatches are allowed through with
|
|
263
|
+
`model` rewritten via the hook output contract
|
|
264
|
+
(`hookSpecificOutput.updatedInput`), so work proceeds at the right cost tier.
|
|
265
|
+
- **Fail-open reliability**: malformed input produces no output and exit 0.
|
|
266
|
+
The hook never crashes the harness and never logs payload contents.
|
|
267
|
+
|
|
268
|
+
### Registration (settings.json)
|
|
269
|
+
|
|
270
|
+
Add to `.claude/settings.json` in the project (or `~/.claude/settings.json`
|
|
271
|
+
for user-wide enforcement):
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"hooks": {
|
|
276
|
+
"PreToolUse": [
|
|
277
|
+
{
|
|
278
|
+
"matcher": "Agent|Task",
|
|
279
|
+
"hooks": [
|
|
280
|
+
{
|
|
281
|
+
"type": "command",
|
|
282
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/hooks/claude/force-model-policy.mjs\""
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
If the hook lives outside the project (e.g., a shared policy checkout), use an
|
|
292
|
+
absolute path to the `.mjs` file instead of `$CLAUDE_PROJECT_DIR`.
|
|
293
|
+
|
|
294
|
+
### Escape hatch
|
|
295
|
+
|
|
296
|
+
For dispatches that genuinely need a bigger model, include the literal marker
|
|
297
|
+
`[[ALLOW-NON-HAIKU]]` anywhere in the subagent prompt. The hook passes the
|
|
298
|
+
dispatch through untouched — and because the marker sits in the prompt, every
|
|
299
|
+
opt-out is visible in the transcript and auditable after the fact.
|
|
300
|
+
|
|
301
|
+
### Testing
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
node --test tests/force-model-policy.test.mjs
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Covers the rewrite, escape-hatch, malformed-stdin, and config-override cases.
|