@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
package/docs/RESTORE.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Cross-Machine Reconstitution — Restore Aesop & Fleet After a Wipe
|
|
2
|
+
|
|
3
|
+
**Purpose**: Aesop is designed so that compute is disposable. The brain (rules, memory, configuration) and fleet work (git commits, backups) live in remotes. This playbook describes how to reconstitute a full fleet on a new machine from scratch using only git and the aesop harness.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Before a Wipe: Continuous Posture
|
|
8
|
+
|
|
9
|
+
**What the watchdog guarantees** (if running continuously):
|
|
10
|
+
|
|
11
|
+
The watchdog daemon (`daemons/run-watchdog.sh`) runs every 150 seconds and:
|
|
12
|
+
- **Discovers fleet repos** automatically by scanning `~/.* ~/* ~/dev/*` (dot-directories in home, home root, dev subtree)
|
|
13
|
+
- **Backs up uncommitted work** as ephemeral commits to `backup/wip-YYYYMMDD` branches (named by calendar date)
|
|
14
|
+
- **Backs up unpushed commits** on `master`/`main` to `backup/master-wip-YYYYMMDD` branches
|
|
15
|
+
- **Pushes feature branches** normally to origin
|
|
16
|
+
- **Scans for secrets** before every push (via `tools/secret_scan.py`); blocks if credentials are detected
|
|
17
|
+
- **Writes heartbeat** to `state/.watchdog-heartbeat` (epoch seconds; used by the watchdog itself to avoid duplicate runs)
|
|
18
|
+
- **Logs all actions** to `state/FLEET-BACKUP.log` (append-only; persists across sessions)
|
|
19
|
+
- **Records repo state** to `state/.watchdog-repos.json` (which repos were touched, what state: SNAPSHOTTED/PUSHED/BLOCKED/CLEAN)
|
|
20
|
+
|
|
21
|
+
**Verify your current posture right now**:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 1. Check watchdog heartbeat age (should be <200s if daemon is alive)
|
|
25
|
+
stat state/.watchdog-heartbeat
|
|
26
|
+
# On Unix: check "Modify" timestamp
|
|
27
|
+
# On Windows: check "Last Write Time"
|
|
28
|
+
|
|
29
|
+
# 2. Tail the backup log to see last cycle
|
|
30
|
+
tail -20 state/FLEET-BACKUP.log
|
|
31
|
+
|
|
32
|
+
# 3. Check which repos were touched in the last cycle
|
|
33
|
+
cat state/.watchdog-repos.json | jq '.' # (requires jq, or use any JSON viewer)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**What is NOT backed up automatically**:
|
|
37
|
+
- Heartbeat files, logs, and JSON status are transient (in `.gitignore`) — useful for live monitoring but not recovered after a wipe.
|
|
38
|
+
- `aesop.config.json` (holds your local paths, credentials, custom settings) is git-ignored for security — you must recreate it.
|
|
39
|
+
- The private brain directory (`~/.claude/`) lives in a separate private remote, not in this public aesop repo.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. After a Wipe: Reconstitute the Fleet
|
|
44
|
+
|
|
45
|
+
### Step 1: Clone Aesop
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone <aesop-remote-url> ~/aesop
|
|
49
|
+
cd ~/aesop
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Replace `<aesop-remote-url>` with your remote (e.g., `https://github.com/your-org/aesop` or `git@github.com:your-org/aesop.git`).
|
|
53
|
+
|
|
54
|
+
### Step 2: Restore the Private Brain
|
|
55
|
+
|
|
56
|
+
The team brain lives in a separate private remote (not in the aesop repo). Clone it early so rules and memory prime your session:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone <brain-remote-url> ~/.claude
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This populates:
|
|
63
|
+
- `~/.claude/CLAUDE.md` — your team's cardinal rules and agent governance
|
|
64
|
+
- `~/.claude/MEMORY.md` or individual fact files in `~/.claude/memory/` — your team knowledge
|
|
65
|
+
- `~/.claude/docs/` — architecture guides, dispatch patterns, etc.
|
|
66
|
+
|
|
67
|
+
Without the brain, Claude Code sessions will lack your org's policies and context.
|
|
68
|
+
|
|
69
|
+
### Step 3: Create Local Configuration
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd ~/aesop
|
|
73
|
+
cp aesop.config.example.json aesop.config.json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Edit `aesop.config.json` to match your setup:
|
|
77
|
+
- `aesop_root`: path to your aesop installation (e.g., `/home/user/aesop` or `$HOME/aesop`)
|
|
78
|
+
- `brain_root`: path to your brain (e.g., `$HOME/.claude`)
|
|
79
|
+
- `scripts_root`: path to reusable scripts (e.g., `$HOME/scripts`)
|
|
80
|
+
- `temp_root`: temporary scratch directory (e.g., `/tmp` or `$env:TEMP` on Windows)
|
|
81
|
+
- `repos[]`: list of fleet repos (path, name, primary_branch, backup_branch)
|
|
82
|
+
- `watchdog.cycle_seconds`: how often the daemon runs (default 150)
|
|
83
|
+
- `watchdog.heartbeat_threshold_seconds`: how long before heartbeat is considered stale (default 200)
|
|
84
|
+
|
|
85
|
+
### Step 4: Create Required Directories
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
mkdir -p ~/aesop/state
|
|
89
|
+
mkdir -p ~/.claude/memory # If not already created by brain clone
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The `state/` directory will be populated by the watchdog at runtime.
|
|
93
|
+
|
|
94
|
+
### Step 5: Set Environment Variables (Optional but Recommended)
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Bash/Unix
|
|
98
|
+
export AESOP_ROOT=$HOME/aesop
|
|
99
|
+
|
|
100
|
+
# PowerShell/Windows
|
|
101
|
+
$env:AESOP_ROOT = "$env:USERPROFILE\aesop"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The scripts check `AESOP_ROOT` and default to `.` (current directory) if unset. Setting it explicitly avoids confusion.
|
|
105
|
+
|
|
106
|
+
### Step 6: Verify the Watchdog
|
|
107
|
+
|
|
108
|
+
Run the watchdog in single-shot mode to verify the setup:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bash $AESOP_ROOT/daemons/run-watchdog.sh --once
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Expected output: logs to `state/FLEET-BACKUP.log` showing which repos it discovered and their states (CLEAN, SNAPSHOTTED, PUSHED, or BLOCKED).
|
|
115
|
+
|
|
116
|
+
### Step 7: Clone Fleet Repos Using Reconstitute
|
|
117
|
+
|
|
118
|
+
The aesop harness includes a bootstrap tool to clone and fetch all your fleet repos automatically. This is the recommended approach for reconstituting a full fleet:
|
|
119
|
+
|
|
120
|
+
#### Option A: Using aesop.config.json (Recommended)
|
|
121
|
+
|
|
122
|
+
If your `aesop.config.json` includes a `repos` array with `url` and `path` fields, run:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bash $AESOP_ROOT/tools/reconstitute.sh
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This will clone any missing repos and fetch existing ones, printing a summary of cloned, fetched, and failed repos.
|
|
129
|
+
|
|
130
|
+
#### Option B: Using a repos file
|
|
131
|
+
|
|
132
|
+
Create a text file with one repo per line in the format `<url> <target-dir>` or `<url>⇥<target-dir>` (tab-delimited):
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
https://github.com/user/project1.git ~/project1
|
|
136
|
+
https://github.com/user/project2.git ~/project2
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Then run:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
bash $AESOP_ROOT/tools/reconstitute.sh --repos-file repos.txt
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Dry-run mode (preview without executing)
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
bash $AESOP_ROOT/tools/reconstitute.sh --dry-run
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Manual fallback
|
|
152
|
+
|
|
153
|
+
If you prefer to clone repos manually or need fine-grained control, you can still clone individually:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# For each fleet repo, clone from its remote
|
|
157
|
+
git clone <repo-url> <local-path>
|
|
158
|
+
|
|
159
|
+
# Example: if repos are at ~/project1, ~/project2, etc.
|
|
160
|
+
cd ~/project1 && git fetch -q origin
|
|
161
|
+
cd ~/project2 && git fetch -q origin
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Step 8: Recover Snapshotted Uncommitted Work
|
|
165
|
+
|
|
166
|
+
The watchdog pushed uncommitted work to `backup/wip-YYYYMMDD` branches. To recover:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# In each repo, check for backup branches
|
|
170
|
+
git branch -r | grep backup
|
|
171
|
+
|
|
172
|
+
# Example: backup/wip-20260712 exists
|
|
173
|
+
# Inspect what was snapshotted
|
|
174
|
+
git log --oneline origin/backup/wip-20260712 -5
|
|
175
|
+
|
|
176
|
+
# RECOMMENDED: Cherry-pick specific commits (safe, adds commits on top)
|
|
177
|
+
git cherry-pick origin/backup/wip-20260712
|
|
178
|
+
|
|
179
|
+
# WARNING: Only use reset if you will NOT do Step 9 after this step.
|
|
180
|
+
# Resetting here will be overwritten by Step 9's reset, losing work.
|
|
181
|
+
# (Use reset only if you're certain you want to replace your entire branch
|
|
182
|
+
# and have no unpushed commits to recover in Step 9)
|
|
183
|
+
# git reset --hard origin/backup/wip-20260712
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Step 9: Recover Snapshotted Unpushed Commits on Main/Master
|
|
187
|
+
|
|
188
|
+
If you had unpushed commits on your default branch, the watchdog pushed them to `backup/master-wip-YYYYMMDD`:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Check for master/main backup branches
|
|
192
|
+
git branch -r | grep backup.*master
|
|
193
|
+
|
|
194
|
+
# Example: backup/master-wip-20260712 exists
|
|
195
|
+
# Inspect the commits
|
|
196
|
+
git log --oneline origin/backup/master-wip-20260712 -10
|
|
197
|
+
|
|
198
|
+
# Reapply them to your main branch
|
|
199
|
+
git reset --hard origin/backup/master-wip-20260712
|
|
200
|
+
git push origin HEAD:main # or 'master' if that's your primary branch
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Step 10: Restart the Daemon
|
|
204
|
+
|
|
205
|
+
Once your fleet is restored and verified, restart the watchdog in daemon mode:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
bash $AESOP_ROOT/daemons/run-watchdog.sh &
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Or, if using your own process manager (systemd, supervisor, etc.), start it according to your setup.
|
|
212
|
+
|
|
213
|
+
The watchdog will now run every 150 seconds, backing up any new changes and pushing them to origin.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 3. Recovering the Private Brain
|
|
218
|
+
|
|
219
|
+
The brain directory (`~/.claude/`) is the single point of truth for your org's rules, memory, and agent behavior. It lives in a **separate private remote** (not in the public aesop repo) so credentials and private facts never leak.
|
|
220
|
+
|
|
221
|
+
### Clone the Brain
|
|
222
|
+
|
|
223
|
+
Before or alongside restoring aesop, clone your brain:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
git clone <your-private-brain-remote> ~/.claude
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Brain Contents
|
|
230
|
+
|
|
231
|
+
Once cloned, you have:
|
|
232
|
+
|
|
233
|
+
**CLAUDE.md** — Your team's version of the cardinal rules, tailored to your org:
|
|
234
|
+
- Which domains your team works on (e.g., "infrastructure," "security," "data")
|
|
235
|
+
- Org-specific policies (e.g., approval chains, secret-scan rules, cost limits)
|
|
236
|
+
- Escalation paths and decision-makers
|
|
237
|
+
- Reusable tool policies (e.g., "AWS CLI always requires approval")
|
|
238
|
+
|
|
239
|
+
**MEMORY.md or `memory/` fact files** — Long-lived team knowledge:
|
|
240
|
+
- Team names, email addresses, and roles
|
|
241
|
+
- Past decisions and their rationales (searchable history)
|
|
242
|
+
- Known limitations and workarounds
|
|
243
|
+
- Approved vendors, tool licenses, cost budgets
|
|
244
|
+
|
|
245
|
+
**docs/** — Org-specific architecture and tutorials:
|
|
246
|
+
- Dispatch patterns that work well for your team
|
|
247
|
+
- Performance characteristics and lessons learned
|
|
248
|
+
- Custom monitoring and alerting playbooks
|
|
249
|
+
|
|
250
|
+
### Why It's Separate
|
|
251
|
+
|
|
252
|
+
The brain is in a private repo because:
|
|
253
|
+
- It may contain credentials (API keys for dashboards, auth tokens for internal services).
|
|
254
|
+
- It may reference internal team facts (email addresses, org structure) that are not public.
|
|
255
|
+
- It evolves independently of the aesop harness itself.
|
|
256
|
+
|
|
257
|
+
**Do NOT** put the brain in the public aesop repo. Clone it from your private remote on every fresh machine.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 4. Drill: Test Recovery on a Second Machine or VM
|
|
262
|
+
|
|
263
|
+
To verify this playbook works end-to-end, run a recovery on a clean machine (or a new VM):
|
|
264
|
+
|
|
265
|
+
### Prerequisites
|
|
266
|
+
|
|
267
|
+
- Git is installed and configured with access to your remotes (SSH keys or credentials available).
|
|
268
|
+
- Bash is available (Git Bash on Windows, bash on Unix).
|
|
269
|
+
- Python 3.10+ is available (for secret-scan, optional for testing the watchdog).
|
|
270
|
+
- Node.js 18+ is available (optional; dashboard requires it but is not essential for the watchdog).
|
|
271
|
+
|
|
272
|
+
### Drill Steps
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# 1. Start with a clean home directory (or VM)
|
|
276
|
+
# Simulate a wipe by deleting ~/aesop, ~/project1, ~/project2, etc.
|
|
277
|
+
|
|
278
|
+
# 2. Clone aesop
|
|
279
|
+
git clone <aesop-remote> ~/aesop
|
|
280
|
+
cd ~/aesop
|
|
281
|
+
|
|
282
|
+
# 3. Clone the brain
|
|
283
|
+
git clone <brain-remote> ~/.claude
|
|
284
|
+
|
|
285
|
+
# 4. Create config (edit paths to match the new machine)
|
|
286
|
+
cp aesop.config.example.json aesop.config.json
|
|
287
|
+
# Edit aesop.config.json with your local paths and repo list (including repos[].url)
|
|
288
|
+
|
|
289
|
+
# 5. Create state directory
|
|
290
|
+
mkdir -p ~/aesop/state
|
|
291
|
+
|
|
292
|
+
# 6. Clone fleet repos using reconstitute
|
|
293
|
+
bash $AESOP_ROOT/tools/reconstitute.sh
|
|
294
|
+
|
|
295
|
+
# 7. Run watchdog once
|
|
296
|
+
export AESOP_ROOT=$HOME/aesop
|
|
297
|
+
bash $AESOP_ROOT/daemons/run-watchdog.sh --once
|
|
298
|
+
|
|
299
|
+
# 8. Check the log
|
|
300
|
+
tail -30 $AESOP_ROOT/state/FLEET-BACKUP.log
|
|
301
|
+
|
|
302
|
+
# 9. Recover any snapshotted work from backup branches
|
|
303
|
+
cd ~/project1
|
|
304
|
+
git branch -r | grep backup
|
|
305
|
+
git log --oneline origin/backup/wip-* -5
|
|
306
|
+
# (cherry-pick or reset as needed)
|
|
307
|
+
|
|
308
|
+
# 10. Verify all work is recovered
|
|
309
|
+
git status
|
|
310
|
+
git log --oneline -10
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Expected Recovery Time
|
|
314
|
+
|
|
315
|
+
For a typical fleet (3–5 repos, average size 100MB–500MB):
|
|
316
|
+
- **Clone aesop + brain**: ~30 seconds
|
|
317
|
+
- **Create config + directories**: ~10 seconds
|
|
318
|
+
- **Clone fleet repos**: ~2–5 minutes (depends on size and network)
|
|
319
|
+
- **Run watchdog once**: ~30 seconds
|
|
320
|
+
- **Recover snapshotted work**: ~1–2 minutes (git operations)
|
|
321
|
+
|
|
322
|
+
**Total**: ~5–10 minutes start-to-finish.
|
|
323
|
+
|
|
324
|
+
For very large repos (>1GB) or slow networks, add 2–3 minutes per repo.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 5. Known Limitations
|
|
329
|
+
|
|
330
|
+
### What This Playbook Does NOT Do
|
|
331
|
+
|
|
332
|
+
1. **Automatic repo discovery on clone (Solved by reconstitute.sh)**
|
|
333
|
+
- The watchdog discovers repos when it runs, but only repos that already exist on disk.
|
|
334
|
+
- Use `bash $AESOP_ROOT/tools/reconstitute.sh` to clone all repos from `aesop.config.json` automatically.
|
|
335
|
+
- Reconstitute supports `--dry-run` for preview and `--repos-file` for custom repo lists.
|
|
336
|
+
|
|
337
|
+
2. **Recover transient state/ directory**
|
|
338
|
+
- Heartbeat files (`.watchdog-heartbeat`), logs (`FLEET-BACKUP.log`), and JSON status (`.watchdog-repos.json`) are git-ignored.
|
|
339
|
+
- They are recreated fresh when the watchdog runs, so you lose the history of what happened during the wipe.
|
|
340
|
+
- Future enhancement: persist logs to a cloud service or central logging system.
|
|
341
|
+
|
|
342
|
+
3. **Recover aesop.config.json**
|
|
343
|
+
- The config file is git-ignored for security (contains local paths, possibly credentials).
|
|
344
|
+
- You must recreate it manually after a wipe.
|
|
345
|
+
- Workaround: store a template or commented version in a separate private repo, or use a configuration management tool (Ansible, Terraform).
|
|
346
|
+
|
|
347
|
+
4. **Authenticate to private remotes**
|
|
348
|
+
- Git credentials (SSH keys, personal access tokens) are machine-local and not backed up.
|
|
349
|
+
- You must set up git authentication (SSH keys, git credential helper, or `~/.netrc`) on the new machine before cloning remotes.
|
|
350
|
+
- This is a feature, not a bug: credentials must never leave the machine.
|
|
351
|
+
|
|
352
|
+
5. **Restore stashed work**
|
|
353
|
+
- If you stashed uncommitted work in a repo (e.g., via `git stash`), it is NOT in the watchdog backups.
|
|
354
|
+
- The watchdog only backs up uncommitted changes that are in the working directory or staging area at the time it runs.
|
|
355
|
+
- Mitigation: always commit or snapshot work before a wipe; use `git stash pop` before a wipe to restore stashed work.
|
|
356
|
+
|
|
357
|
+
6. **Recover monitor state**
|
|
358
|
+
- The orchestration monitor (`monitor/collect-signals.mjs`) writes `BRIEF.md`, `SIGNALS.json`, and `.monitor-heartbeat` files, which are git-ignored.
|
|
359
|
+
- After a wipe, you start with a fresh slate; you lose the history of orchestration signals.
|
|
360
|
+
- Future enhancement: persist monitor state to a cloud service.
|
|
361
|
+
|
|
362
|
+
7. **Recover custom dashboards or extensions**
|
|
363
|
+
- If you've customized `dash/watchdog-gui.sh`, `ui/serve.py`, or added extensions to `tools/`, ensure they are committed to the repo.
|
|
364
|
+
- Only committed code is restored; uncommitted customizations are lost.
|
|
365
|
+
|
|
366
|
+
### Mitigations
|
|
367
|
+
|
|
368
|
+
- **For critical uncommitted work**: Ensure the watchdog is always running so work is snapshotted every 150 seconds.
|
|
369
|
+
- **For credentials and config**: Use a separate private remote for `aesop.config.json` templates and credentials, or use a CI/CD system to inject them.
|
|
370
|
+
- **For large repos**: Consider using git shallow clones (`--depth 1`) or sparse checkouts to speed up recovery for disaster scenarios.
|
|
371
|
+
- **For compliance and audit trails**: Periodically archive `state/FLEET-BACKUP.log` to a central logging system so you can audit what happened after a wipe.
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Summary
|
|
376
|
+
|
|
377
|
+
| Phase | Action | Time |
|
|
378
|
+
|-------|--------|------|
|
|
379
|
+
| Clone | `git clone <aesop>` + `git clone <brain>` | ~1 min |
|
|
380
|
+
| Config | Create `aesop.config.json` + `mkdir state/` | ~2 min |
|
|
381
|
+
| Repos | `bash tools/reconstitute.sh` (clone + fetch) | ~2–5 min |
|
|
382
|
+
| Verify | `bash daemons/run-watchdog.sh --once` | ~1 min |
|
|
383
|
+
| Recover | `git cherry-pick` from `backup/wip-*` branches | ~1–2 min |
|
|
384
|
+
| Restart | `bash daemons/run-watchdog.sh &` | immediate |
|
|
385
|
+
| **Total** | | **~7–12 min** |
|
|
386
|
+
|
|
387
|
+
The watchdog, combined with continuous git discipline, ensures that **compute is disposable but work is durable**. A machine wipe is annoying but not catastrophic.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## References
|
|
392
|
+
|
|
393
|
+
- `README.md` — installation and quick-start guide
|
|
394
|
+
- `CARDINAL-RULES.md` — foundational principles (rule #5: "durable state")
|
|
395
|
+
- `daemons/run-watchdog.sh` — watchdog daemon implementation
|
|
396
|
+
- `daemons/backup-fleet.sh` — backup and push logic
|
|
397
|
+
- `.gitignore` — what is and isn't persisted across wipes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Scripts Policy: Local-only, shared library
|
|
2
|
+
|
|
3
|
+
This policy governs how auxiliary scripts and automation tools are written, organized, and maintained in Aesop-driven orchestration systems.
|
|
4
|
+
|
|
5
|
+
## Rule 1: Never run scripts in the cloud
|
|
6
|
+
|
|
7
|
+
Python, shell, and other ad-hoc scripts execute **LOCALLY on the orchestrator machine only** — never inside:
|
|
8
|
+
- Cloud sandboxes or serverless functions
|
|
9
|
+
- Remote/scheduled cloud agents
|
|
10
|
+
- Remote workflow environments
|
|
11
|
+
- Containerized runners without local bind-mounts
|
|
12
|
+
|
|
13
|
+
**Why?** Local scripts often rely on local state (configuration files, home directory, machine-specific paths, local credentials). Pushing them to cloud breaks those assumptions and risks secret leaks. Local-only keeps boundaries clear and reproducible.
|
|
14
|
+
|
|
15
|
+
## Rule 2: One common scripts library
|
|
16
|
+
|
|
17
|
+
Before writing any new script, **check the library first**.
|
|
18
|
+
|
|
19
|
+
**Workflow**:
|
|
20
|
+
1. Look in the shared library (e.g., `aesop/tools/` for core tools, or a dedicated `~/scripts` folder for utility scripts)
|
|
21
|
+
2. If the script exists, edit or extend the existing one
|
|
22
|
+
3. If it doesn't exist and it's genuinely reusable (useful across tasks/projects/teams), add it to the library
|
|
23
|
+
4. If it's task-local and unlikely to be reused, keep it in a temporary scratchpad
|
|
24
|
+
|
|
25
|
+
**Library conventions**:
|
|
26
|
+
- Snake_case filenames (e.g., `secret_scan.py`, `rotate_logs.py`)
|
|
27
|
+
- Module docstring at the top explaining purpose, usage, and parameters
|
|
28
|
+
- One-line entry in the library's `CLAUDE.md` index
|
|
29
|
+
- Python: include a `if __name__ == "__main__":` section with example usage
|
|
30
|
+
|
|
31
|
+
**Example entry**:
|
|
32
|
+
```markdown
|
|
33
|
+
- `secret_scan.py` — scan staged files for secrets; blocks push if found. Usage: `python secret_scan.py --staged --repo <path>`
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Rule 3: Task-local vs. reusable
|
|
37
|
+
|
|
38
|
+
**Task-local scripts** not worth keeping are deleted at task end. Examples:
|
|
39
|
+
- One-off data transforms for a single feature or bugfix
|
|
40
|
+
- Temporary test harnesses
|
|
41
|
+
- Ad-hoc debugging helpers for a specific issue
|
|
42
|
+
|
|
43
|
+
**Reusable scripts** added to the library. Examples:
|
|
44
|
+
- Secret scanning (multi-project, multi-task)
|
|
45
|
+
- Build validation and health checks
|
|
46
|
+
- Linting and format automation
|
|
47
|
+
- Data migration templates
|
|
48
|
+
- Log rotation and archival
|
|
49
|
+
|
|
50
|
+
**Decision heuristic**: If you'd use it again in another project or another day on this project, it belongs in the library. If it's solving a one-time problem, delete it.
|
|
51
|
+
|
|
52
|
+
## Rule 4: Library growth & discovery
|
|
53
|
+
|
|
54
|
+
The library evolves as new patterns emerge. On every orchestration boot or refresh cycle, the orchestrator can scan the library's `CLAUDE.md` to discover available tools.
|
|
55
|
+
|
|
56
|
+
**Keep the index current**: Every new reusable script gets a one-line entry so it's discoverable and documented. Entries stay alphabetical and include usage examples.
|
|
57
|
+
|
|
58
|
+
## Rule 5: Environment & security
|
|
59
|
+
|
|
60
|
+
Scripts must be idempotent and deterministic.
|
|
61
|
+
|
|
62
|
+
**DO**:
|
|
63
|
+
- Detect and skip if already run (idempotent)
|
|
64
|
+
- Use local paths and respect working directory
|
|
65
|
+
- Log actions for auditability
|
|
66
|
+
- Handle errors explicitly (exit 1 on failure)
|
|
67
|
+
|
|
68
|
+
**DON'T**:
|
|
69
|
+
- Hardcode machine-specific paths (use `$HOME`, relative paths, or environment variables)
|
|
70
|
+
- Embed credentials (read from environment variables or secure config files instead)
|
|
71
|
+
- Leave large temporary files in `/tmp` or scratchpad (clean up on exit)
|
|
72
|
+
- Run cloud-bound commands that phone home
|
|
73
|
+
|
|
74
|
+
## Rule 6: Version control & archival
|
|
75
|
+
|
|
76
|
+
Scripts in the library are **checked into git** so they're versioned and auditable. Task-local scripts can be git-ignored or committed temporarily (depending on repo policy).
|
|
77
|
+
|
|
78
|
+
**Library scripts**:
|
|
79
|
+
- Committed to the repo
|
|
80
|
+
- Updates are reviewed like any other code
|
|
81
|
+
- On removal/deprecation, archive the old version (add date suffix, keep for 3 months for reference)
|
|
82
|
+
|
|
83
|
+
## Implementation checklist
|
|
84
|
+
|
|
85
|
+
When adding a new script to the library:
|
|
86
|
+
- [ ] Snake_case filename
|
|
87
|
+
- [ ] Module docstring explaining purpose and usage
|
|
88
|
+
- [ ] Entry in CLAUDE.md with one-line description and usage example
|
|
89
|
+
- [ ] Idempotent (safe to run multiple times)
|
|
90
|
+
- [ ] Handles errors explicitly (exit 1 on failure)
|
|
91
|
+
- [ ] No hardcoded personal paths or credentials
|
|
92
|
+
- [ ] Runs locally only (no cloud / scheduled execution)
|
|
93
|
+
- [ ] Git-committed and documented
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
**Why this policy matters**: A shared, versioned scripts library reduces duplication, improves discoverability, and ensures automation is auditable and reproducible across team members and machines. Local-only execution keeps secrets safe and performance predictable. Together, they form the **automation core** that enables reliable, maintainable orchestration at scale.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# ACTIVATION runbook — force-model-policy.merged.mjs
|
|
2
|
+
|
|
3
|
+
Swap the staged merged hook in for the live model-policy hook. **Do not run
|
|
4
|
+
this casually** — the live hook governs every subagent dispatch in every
|
|
5
|
+
session. Perform the swap between waves, not mid-fleet.
|
|
6
|
+
|
|
7
|
+
Paths used below:
|
|
8
|
+
|
|
9
|
+
- LIVE hook: `C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs`
|
|
10
|
+
- STAGED file: `<aesop>\docs\spikes\tiered-cognition\force-model-policy.merged.mjs`
|
|
11
|
+
- Shim agent def: `<aesop>\docs\spikes\tiered-cognition\aesop-cognition.example.md`
|
|
12
|
+
|
|
13
|
+
## Phase A activation (recommended first step)
|
|
14
|
+
|
|
15
|
+
Default behavior after the swap: model policy identical to today, plus
|
|
16
|
+
opus/fable-frontmatter dispatches rewritten to the message-only
|
|
17
|
+
`aesop-cognition` shim. Sonnet specialists untouched (no prompt loss —
|
|
18
|
+
FINDINGS.md condition 3 deferred). Layer 2 stays dormant.
|
|
19
|
+
|
|
20
|
+
1. **Install the shim agent definition** (the merged hook rewrites cognition
|
|
21
|
+
dispatches to `subagent_type: aesop-cognition`; without the definition
|
|
22
|
+
those dispatches would fail):
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
Copy-Item <aesop>\docs\spikes\tiered-cognition\aesop-cognition.example.md `
|
|
26
|
+
C:\Users\matt8\.claude\agents\aesop-cognition.md
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then edit the copied file's `description:` to remove the "NOT INSTALLED"
|
|
30
|
+
sentence. Keep `tools: Agent, SendMessage, TaskStop, Monitor` and do NOT
|
|
31
|
+
add a `model:` line (the hook passes the effective model explicitly).
|
|
32
|
+
|
|
33
|
+
2. **Back up the live hook** (timestamped, same directory):
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
Copy-Item C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs `
|
|
37
|
+
C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs.bak-$(Get-Date -Format yyyyMMdd-HHmmss)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
3. **Verify the staged file before copying** (must print `SELF-TEST: ALL PASS`,
|
|
41
|
+
exit 0):
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
node --check <aesop>\docs\spikes\tiered-cognition\force-model-policy.merged.mjs
|
|
45
|
+
node <aesop>\docs\spikes\tiered-cognition\force-model-policy.merged.mjs --self-test
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
4. **Copy over the live hook, keeping the live FILENAME** so settings.json
|
|
49
|
+
needs no edit:
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
Copy-Item <aesop>\docs\spikes\tiered-cognition\force-model-policy.merged.mjs `
|
|
53
|
+
C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs -Force
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
5. **Verify the settings matcher is unchanged and correct.** In
|
|
57
|
+
`C:\Users\matt8\.claude\settings.json` the `hooks.PreToolUse` entry with
|
|
58
|
+
matcher `Agent|Task` must point at
|
|
59
|
+
`~/.claude/hooks/force-haiku-subagents.mjs` (node command). Do NOT add any
|
|
60
|
+
other hook on that matcher — this file must remain the ONLY `updatedInput`
|
|
61
|
+
owner for `Agent|Task`; wiring a second rewriting hook there reintroduces
|
|
62
|
+
the exact collision this merge exists to fix.
|
|
63
|
+
|
|
64
|
+
6. **Re-run the self-test on the installed copy** (proves the copy is intact):
|
|
65
|
+
|
|
66
|
+
```powershell
|
|
67
|
+
node C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs --self-test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
7. **Restart.** Claude Code snapshots hook config at session start — running
|
|
71
|
+
sessions keep the old hook until restarted. Restart the orchestrator
|
|
72
|
+
session and any long-lived fleet sessions/daemons that dispatch subagents.
|
|
73
|
+
|
|
74
|
+
8. **Smoke-check in a fresh session:** dispatch a generic Haiku agent (expect
|
|
75
|
+
pass/route-to-haiku, no shim), a `bash-pro` (expect claude-sonnet-5, no
|
|
76
|
+
shim), and — if any opus/fable-frontmatter agent def is installed — one of
|
|
77
|
+
those (expect the `⛓ … tools stripped via 'aesop-cognition' shim` system
|
|
78
|
+
message). Confirm escape uses append to
|
|
79
|
+
`state/MODEL-POLICY-ESCAPES.log` / `state/TIER-POLICY-ESCAPES.log`.
|
|
80
|
+
|
|
81
|
+
## Phase B (later, deliberate)
|
|
82
|
+
|
|
83
|
+
Set `TIER_STRIP_SCOPE=all` in the environment Claude Code launches from to
|
|
84
|
+
extend the shim to sonnet specialists — ONLY after per-specialist cognition
|
|
85
|
+
variants exist (FINDINGS.md condition 3), otherwise specialist system prompts
|
|
86
|
+
are lost to the generic shim. `TIER_STRIP_SCOPE=off` reduces the hook to pure
|
|
87
|
+
live-hook model-policy behavior (useful as a soft rollback).
|
|
88
|
+
|
|
89
|
+
## Phase C — arming Layer 2 (call-time backstop; later, deliberate)
|
|
90
|
+
|
|
91
|
+
Dormant until BOTH of these are true:
|
|
92
|
+
|
|
93
|
+
1. `TIER_ENFORCE=1` in the environment.
|
|
94
|
+
2. settings.json gains a second `PreToolUse` entry running this same hook file
|
|
95
|
+
with a matcher covering the denied tools, e.g.
|
|
96
|
+
`Read|Write|Edit|MultiEdit|NotebookEdit|Bash|PowerShell|Glob|Grep|WebFetch|WebSearch|Skill|Artifact`.
|
|
97
|
+
(That entry never emits `updatedInput` — deny/allow only — so the
|
|
98
|
+
single-owner rule is preserved.)
|
|
99
|
+
|
|
100
|
+
Before arming: validate transcript sniffing on real sidechain transcripts and
|
|
101
|
+
decide the main-thread carve-out (FINDINGS.md condition 2 / risk 4).
|
|
102
|
+
|
|
103
|
+
## Rollback
|
|
104
|
+
|
|
105
|
+
1. Restore the newest backup:
|
|
106
|
+
|
|
107
|
+
```powershell
|
|
108
|
+
Copy-Item C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs.bak-<TS> `
|
|
109
|
+
C:\Users\matt8\.claude\hooks\force-haiku-subagents.mjs -Force
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
2. Optionally remove `C:\Users\matt8\.claude\agents\aesop-cognition.md`
|
|
113
|
+
(harmless to leave installed; nothing routes to it without the merged hook).
|
|
114
|
+
3. Unset `TIER_ENFORCE` / `TIER_STRIP_SCOPE` if set; remove any Layer-2
|
|
115
|
+
matcher added in Phase C.
|
|
116
|
+
4. Restart running sessions (same snapshot rule as step 7 above).
|
|
117
|
+
|
|
118
|
+
## Env knobs (merged hook)
|
|
119
|
+
|
|
120
|
+
| Var | Default | Effect |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `FORCE_ALL_HAIKU=1` | off | Every dispatch → haiku (bash-pro excepted) — unchanged from live hook |
|
|
123
|
+
| `TIER_STRIP_SCOPE` | `opus-fable` | `opus-fable` (Phase A) / `all` (Phase B) / `off` (model policy only) |
|
|
124
|
+
| `TIER_ENFORCE=1` | off | Arms the Layer-2 call-time backstop (needs the Phase C matcher too) |
|
|
125
|
+
| `AESOP_ROOT` | `~/aesop` | Root for `state/*-ESCAPES.log` audit logs |
|