@matt82198/aesop 0.1.0-beta.4 → 0.1.0-rc.1
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 +187 -5
- package/LICENSE +66 -21
- package/README.md +143 -23
- package/aesop.config.example.json +14 -1
- package/bin/CLAUDE.md +42 -5
- package/bin/cli.js +479 -95
- package/daemons/CLAUDE.md +5 -2
- package/daemons/backup-fleet.sh +209 -51
- package/daemons/run-watchdog.sh +211 -62
- package/dash/dash-extra.mjs +73 -4
- package/dash/watchdog-gui.sh +41 -35
- package/docs/ARCHITECTURE.md +296 -0
- package/docs/CONCEPTS.md +254 -0
- package/docs/CONFIGURE.md +256 -0
- package/docs/FIRST-WAVE.md +276 -0
- package/docs/INSTALL.md +224 -0
- package/docs/README.md +176 -27
- package/docs/RELEASING.md +159 -0
- package/docs/archive/README.md +3 -0
- package/docs/{spikes → archive/spikes}/tiered-cognition/README.md +1 -3
- package/docs/autonomous-swe.md +149 -0
- package/docs/case-study-portfolio.md +61 -0
- package/docs/reproduce.md +121 -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 +51 -0
- package/hooks/install-waveguard.sh +68 -0
- package/hooks/pre-commit-waveguard.sh +26 -0
- package/hooks/pre-push-policy.sh +253 -15
- package/mcp/CLAUDE.md +213 -0
- package/mcp/package.json +26 -0
- package/mcp/server.mjs +543 -0
- package/monitor/CHARTER.md +76 -110
- package/monitor/CLAUDE.md +3 -3
- package/monitor/collect-signals.mjs +218 -20
- package/package.json +17 -6
- package/scan/CLAUDE.md +30 -0
- package/scan/fleet-scan.example.mjs +292 -0
- package/skills/CLAUDE.md +1 -0
- package/skills/healthcheck/SKILL.md +44 -0
- package/state_store/CLAUDE.md +39 -0
- package/state_store/__init__.py +24 -0
- package/state_store/api.py +35 -0
- package/state_store/export.py +19 -0
- package/state_store/ingest.py +41 -0
- package/state_store/projections.py +126 -0
- package/state_store/store.py +197 -0
- package/tools/CLAUDE.md +37 -149
- package/tools/alert_bridge.py +453 -0
- package/tools/bench_runner.py +438 -0
- package/tools/buildlog.py +4 -7
- package/tools/ci_merge_wait.py +308 -14
- package/tools/common.py +62 -0
- package/tools/cost_ceiling.py +191 -0
- package/tools/dash.js +102 -0
- package/tools/doctor.js +220 -0
- package/tools/fleet_ledger.py +189 -17
- package/tools/fleet_prompt_extractor.py +134 -0
- package/tools/halt.py +172 -0
- package/tools/healthcheck.py +299 -0
- package/tools/heartbeat.py +4 -7
- package/tools/metrics_gate.py +8 -2
- package/tools/orchestrator_status.py +4 -9
- package/tools/reconcile.py +277 -0
- package/tools/rotate_logs.py +152 -62
- package/tools/scanner_selftest.py +28 -3
- package/tools/secret_scan.py +367 -91
- package/tools/self_stats.py +797 -0
- package/tools/session_usage_summary.py +198 -0
- package/tools/status.js +187 -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_agent_inspector.py +289 -0
- package/tools/verify_dash.py +361 -542
- package/tools/verify_prboard.py +344 -0
- package/tools/verify_submit_encoding.py +12 -4
- package/tools/watch.js +49 -0
- package/ui/CLAUDE.md +59 -41
- package/ui/agents.py +337 -34
- package/ui/api/submit.py +44 -5
- package/ui/api/tracker.py +15 -7
- package/ui/collectors.py +294 -52
- package/ui/config.py +31 -2
- package/ui/cost.py +355 -0
- package/ui/csrf.py +9 -4
- package/ui/handler.py +450 -12
- package/ui/render.py +26 -6
- package/ui/sse.py +114 -13
- package/ui/wave_prs.py +230 -0
- package/ui/web/dist/assets/index-0qQYnvMC.js +9 -0
- package/ui/web/dist/assets/index-BdIlFieV.css +1 -0
- package/ui/web/dist/index.html +14 -0
- package/monitor/.signal-state.json +0 -3
- package/monitor/ACTIONS.log +0 -1
- package/monitor/BRIEF.md +0 -24
- package/monitor/SIGNALS.json +0 -54
- 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__/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__/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__/stall_check.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/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__/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/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/templates/dashboard.html +0 -1202
- /package/docs/{spikes → archive/spikes}/tiered-cognition/ACTIVATION.md +0 -0
- /package/docs/{spikes → archive/spikes}/tiered-cognition/DESIGN.md +0 -0
- /package/docs/{spikes → archive/spikes}/tiered-cognition/FINDINGS.md +0 -0
- /package/docs/{spikes → archive/spikes}/tiered-cognition/aesop-cognition.example.md +0 -0
- /package/docs/{spikes → archive/spikes}/tiered-cognition/force-model-policy.merged.mjs +0 -0
- /package/docs/{spikes → archive/spikes}/tiered-cognition/strip-tools-hook.mjs +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Reproducing Aesop
|
|
2
|
+
|
|
3
|
+
This document explains how to reproduce the project's claims from a clean checkout.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Aesop project includes:
|
|
8
|
+
1. **Full test suites** (Node.js + Python) that verify core functionality
|
|
9
|
+
2. **An offline benchmark scorer** that proves the benchmark scoring logic works correctly
|
|
10
|
+
|
|
11
|
+
Both can be reproduced entirely offline, without API keys or external dependencies, from any clean clone of the repository.
|
|
12
|
+
|
|
13
|
+
## Reproducing from a Clean Clone
|
|
14
|
+
|
|
15
|
+
### Prerequisites
|
|
16
|
+
|
|
17
|
+
- **Node.js**: 18 or later
|
|
18
|
+
- **Python**: 3.12 or later
|
|
19
|
+
- **Git**: any recent version
|
|
20
|
+
|
|
21
|
+
### Steps
|
|
22
|
+
|
|
23
|
+
1. Clone the repository (no special flags needed):
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/matt82198/aesop.git
|
|
26
|
+
cd aesop
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. Verify Node.js and Python are installed:
|
|
30
|
+
```bash
|
|
31
|
+
node --version
|
|
32
|
+
python --version
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Install Node.js dependencies:
|
|
36
|
+
```bash
|
|
37
|
+
npm ci
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
4. Run the full test suite (Python + Node.js):
|
|
41
|
+
```bash
|
|
42
|
+
# Run Node.js tests
|
|
43
|
+
npm run test:node
|
|
44
|
+
|
|
45
|
+
# Run Python tests
|
|
46
|
+
python -m unittest discover -s tests
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
5. Run the benchmark scorer offline:
|
|
50
|
+
```bash
|
|
51
|
+
# Run the benchmark unit tests (syntax, ground truth format, etc.)
|
|
52
|
+
python -m unittest tests.test_bench_runner -v
|
|
53
|
+
|
|
54
|
+
# Run the offline mock benchmark
|
|
55
|
+
python tools/bench_runner.py --runner mock
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## What the `.github/workflows/reproduce.yml` Job Does
|
|
59
|
+
|
|
60
|
+
The `reproduce` workflow in GitHub Actions automates the above steps:
|
|
61
|
+
|
|
62
|
+
1. **Fresh checkout**: Uses a clean clone (no state reuse from other CI jobs)
|
|
63
|
+
2. **Syntax checks**: Validates all Node.js (.mjs) and shell scripts (.sh)
|
|
64
|
+
3. **Node.js tests**: Runs the full test suite with `npm run test:node`
|
|
65
|
+
4. **Python tests**: Runs the full test suite with `python -m unittest discover`
|
|
66
|
+
5. **Benchmark scorer**: Proves the offline mock benchmark reproduces (no external API calls)
|
|
67
|
+
|
|
68
|
+
The workflow is triggered:
|
|
69
|
+
- **Manually**: Via GitHub Actions "Run workflow" button (workflow_dispatch)
|
|
70
|
+
- **Weekly**: Every Sunday at 2:00 AM UTC (schedule)
|
|
71
|
+
|
|
72
|
+
## What Is and Isn't Reproduced
|
|
73
|
+
|
|
74
|
+
### Reproduced (offline, no API keys needed)
|
|
75
|
+
|
|
76
|
+
- ✓ All committed test suites (correctness, integration, governance)
|
|
77
|
+
- ✓ The benchmark **scorer logic** (matching, scoring, reporting)
|
|
78
|
+
- ✓ The benchmark **mock runner** (deterministic offline accuracy score)
|
|
79
|
+
- ✓ The committed benchmark files (`bench/tasks.jsonl`, `bench/ground_truth.jsonl`)
|
|
80
|
+
|
|
81
|
+
### Not Reproduced (requires external APIs)
|
|
82
|
+
|
|
83
|
+
- ✗ A real multi-model benchmark (calling Claude API, OpenAI, etc.)
|
|
84
|
+
- ✗ The dashboard browser tests (Playwright, requires UI setup)
|
|
85
|
+
- ✗ Performance/timing benchmarks
|
|
86
|
+
- ✗ The full CI job's additional linting and drift checks
|
|
87
|
+
|
|
88
|
+
The benchmark mock runner always returns the same accuracy score because it uses a hardcoded heuristic (plain string/regex matching), not a real model. This is intentional — the tests prove the *scorer* works correctly, not that any model achieves a specific accuracy.
|
|
89
|
+
|
|
90
|
+
## Interpreting Results
|
|
91
|
+
|
|
92
|
+
### Success
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
YAML is valid
|
|
96
|
+
All Node.js tests pass
|
|
97
|
+
All Python tests pass
|
|
98
|
+
Benchmark reproduction successful
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Failure
|
|
102
|
+
|
|
103
|
+
If any step fails, the workflow exits with a non-zero exit code and prints the failure details. Common issues:
|
|
104
|
+
|
|
105
|
+
1. **Node.js test failure**: Check `tests/*.test.mjs` for the specific failing test.
|
|
106
|
+
2. **Python test failure**: Check the error message; most often a missing test fixture or a test isolation bug.
|
|
107
|
+
3. **Benchmark reproduction failure**: Run `python tools/bench_runner.py --runner mock` manually to see the exact error.
|
|
108
|
+
|
|
109
|
+
## For Project Maintainers
|
|
110
|
+
|
|
111
|
+
When you add new features or tests:
|
|
112
|
+
|
|
113
|
+
1. Ensure all tests pass locally: `npm run test:all` (or individually per suite)
|
|
114
|
+
2. The `reproduce` workflow will catch any regressions automatically
|
|
115
|
+
3. If reproduction fails, it indicates the committed test fixtures or scorer are inconsistent
|
|
116
|
+
|
|
117
|
+
## References
|
|
118
|
+
|
|
119
|
+
- Test discovery: `python -m unittest discover --help`
|
|
120
|
+
- Benchmark scorer: `python tools/bench_runner.py --help`
|
|
121
|
+
- GitHub Actions workflow syntax: https://docs.github.com/en/actions
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_source": "Populated by orchestrator at wave close via fleet telemetry aggregation",
|
|
3
|
+
"_updated": "Awaiting orchestrator population",
|
|
4
|
+
"total_sessions": null,
|
|
5
|
+
"total_turns": null,
|
|
6
|
+
"total_user_prompts": null,
|
|
7
|
+
"max_tokens_single_turn": null,
|
|
8
|
+
"cumulative_agent_runs": null,
|
|
9
|
+
"cumulative_tokens": null,
|
|
10
|
+
"total_coding_hours": null
|
|
11
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Fleet-Ops Analysis Summary — Cycle N
|
|
2
|
+
|
|
3
|
+
**Generated:** YYYY-MM-DDTHH:MM:SSZ
|
|
4
|
+
|
|
5
|
+
## Top Failure Modes (cumulative this session)
|
|
6
|
+
|
|
7
|
+
| Category | Count | Severity | Notes |
|
|
8
|
+
|----------|-------|----------|-------|
|
|
9
|
+
| [FAILURE_MODE_NAME] | 0 | [HIGH/MED/LOW] | Brief description of failure pattern, root cause hypothesis, and reference to any related recommendations or PRs. |
|
|
10
|
+
| | | | |
|
|
11
|
+
| | | | |
|
|
12
|
+
|
|
13
|
+
## Token Spend Summary
|
|
14
|
+
|
|
15
|
+
Brief summary of session token spend patterns:
|
|
16
|
+
- [Agent/component type]: ~XXXk tokens — brief observation
|
|
17
|
+
- Heavy [category]: XXXk — specific tasks or drivers
|
|
18
|
+
- Typical [operation type]: XXk-XXXk; [context or pattern]
|
|
19
|
+
|
|
20
|
+
Include observations about:
|
|
21
|
+
- Which domains consumed the most tokens
|
|
22
|
+
- Whether spend is aligned with task scope
|
|
23
|
+
- Any unexpected patterns or efficiency issues
|
|
24
|
+
|
|
25
|
+
## Notes
|
|
26
|
+
|
|
27
|
+
(Optional section for additional context, anomalies, or deferred investigations)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Fleet-Ops Recommendations — machinery fixes for wave open
|
|
2
|
+
|
|
3
|
+
Rolling, append-only list maintained by the orchestration/fleet-ops monitor.
|
|
4
|
+
The buildsystem skill reads this at every wave open (Phase 0.3). Mark items
|
|
5
|
+
ADOPTED/REJECTED/IN-PROGRESS with wave number when actioned; do not delete rows.
|
|
6
|
+
|
|
7
|
+
| # | Date | Source | Severity | Recommendation | Status |
|
|
8
|
+
|---|------|--------|----------|----------------|--------|
|
|
9
|
+
| 1 | YYYY-MM-DD | [source/agent/session phase] | [HIGH/MED/LOW] | Clear, actionable recommendation describing the problem, suggested fix, and rationale. Include specific file paths or component names where applicable. | OPEN |
|
|
10
|
+
| 2 | YYYY-MM-DD | [source/agent/session phase] | [HIGH/MED/LOW] | Second example recommendation with similar detail level. Link to related findings if applicable. | OPEN |
|
|
11
|
+
| | | | | | |
|
|
12
|
+
|
|
13
|
+
## How to Use This Template
|
|
14
|
+
|
|
15
|
+
1. **Record findings at wave close:** The orchestration monitor appends new rows for every machinery issue or systemic improvement identified.
|
|
16
|
+
2. **Mark status transitions:** When a recommendation is adopted (wave-N or PR reference), update Status to `ADOPTED wave-N`. For rejected items, mark `REJECTED wave-N` with brief rationale.
|
|
17
|
+
3. **Track implementation:** Use Status column to indicate OPEN (unactioned), IN-PROGRESS (wave-X, assigned to agent/owner), ADOPTED (implemented, wave-Y), or REJECTED (not pursued, wave-Y).
|
|
18
|
+
4. **Preserve history:** Never delete rows — the log is append-only. This creates an auditable trail for machinery decisions.
|
|
19
|
+
|
|
20
|
+
## Notes
|
|
21
|
+
|
|
22
|
+
- **Source field:** Where did this insight come from? (session coordinator, CI failure, agent forensics, monitor signal, PR review, etc.)
|
|
23
|
+
- **Severity:** HIGH = blocks reliable operation or impacts multiple waves; MED = worth addressing in near term; LOW = nice-to-have optimization.
|
|
24
|
+
- **Implementation scope:** Recommendations should be specific enough for a developer to act on, but can span multiple waves if complex.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Orchestration Refinement Proposals — Action Log
|
|
2
|
+
|
|
3
|
+
Actioned proposals moved from PROPOSALS.md (grounded in signals from YYYY-MM-DD). Per standing order, move decided entries here with date and outcome.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ACCEPTED-IMPLEMENTED — YYYY-MM-DD
|
|
8
|
+
|
|
9
|
+
### 1. [Proposal Title]
|
|
10
|
+
|
|
11
|
+
**Outcome:** [Brief description of what was implemented, where code/docs changed, wave number if applicable, and verification of completion.]
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## IN-PROGRESS — YYYY-MM-DD
|
|
16
|
+
|
|
17
|
+
### 1. [Proposal Title]
|
|
18
|
+
|
|
19
|
+
**Status:** IN-PROGRESS (wave-N, [assigned-agent or owner])
|
|
20
|
+
**Rationale:** [Why this proposal was accepted and what timeline/dependencies exist.]
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## DEFERRED — YYYY-MM-DD
|
|
25
|
+
|
|
26
|
+
### 1. [Proposal Title]
|
|
27
|
+
|
|
28
|
+
**Status:** DEFERRED ([reason: blocked-by/low-priority/waiting-for-data])
|
|
29
|
+
**Rationale:** [Why this proposal was deferred, under what conditions it might be reconsidered.]
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## REJECTED — YYYY-MM-DD
|
|
34
|
+
|
|
35
|
+
### 1. [Proposal Title]
|
|
36
|
+
|
|
37
|
+
**Status:** REJECTED (wave-N, [reason])
|
|
38
|
+
**Rationale:** [Explanation of rejection, any alternatives considered, and future implications.]
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## No further action required (MONITORING ONGOING)
|
|
43
|
+
|
|
44
|
+
### 1. [Proposal Title]
|
|
45
|
+
|
|
46
|
+
**Status:** MONITORING
|
|
47
|
+
[Brief note on what is being tracked and how the team will know if action becomes necessary.]
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## How to Use This Template
|
|
52
|
+
|
|
53
|
+
1. **Capture proposals:** When orchestration/monitor/coordinator identifies a potential process or machinery improvement, record it in a staging PROPOSALS.md file with date, source, and rationale.
|
|
54
|
+
2. **Move to log on decision:** Once a proposal is decided (ACCEPTED/REJECTED/DEFERRED), move the entry here with date and outcome.
|
|
55
|
+
3. **Track implementation:** Use status sections (ACCEPTED-IMPLEMENTED, IN-PROGRESS, DEFERRED, REJECTED) to organize proposals by lifecycle stage.
|
|
56
|
+
4. **Preserve history:** Keep all entries, do not delete. This creates an auditable trail of organizational decisions and learning.
|
|
57
|
+
5. **Link to execution:** When implemented, reference the wave number or PR. When in-progress, name the owner.
|
|
58
|
+
|
|
59
|
+
## Key Fields
|
|
60
|
+
|
|
61
|
+
- **Proposal Title:** Concise statement of the proposed change or investigation.
|
|
62
|
+
- **Outcome/Status:** What happened as a result (implemented, rejected, deferred, monitoring).
|
|
63
|
+
- **Rationale:** Why this decision was made, dependencies, or conditions for change.
|
|
64
|
+
- **Wave/PR/Agent:** Link to the work that carried out the decision (if applicable).
|
package/hooks/CLAUDE.md
CHANGED
|
@@ -30,6 +30,57 @@ Runs on `git push` via `.git/hooks/pre-push` symlink or copy.
|
|
|
30
30
|
- See `docs/HOOK-INSTALL.md` for symlink (Linux/macOS/Git Bash) and copy (Windows) methods
|
|
31
31
|
- Test with `bash hooks/pre-push-policy.sh --test` before org distribution
|
|
32
32
|
|
|
33
|
+
## Hook: pre-commit-waveguard.sh
|
|
34
|
+
|
|
35
|
+
Prevents accidental commits to the PRIMARY aesop tree during a wave cycle. Runs on `git commit` via `.git/hooks/pre-commit`.
|
|
36
|
+
|
|
37
|
+
**Purpose**: During orchestrated waves, the orchestrator sets a marker file (`state/.wave-in-flight`) in the PRIMARY tree only. Sibling worktrees do not inherit this marker (separate working trees), so fleet agents commit freely in worktrees while stray commits to the primary tree are rejected.
|
|
38
|
+
|
|
39
|
+
**Mechanism**:
|
|
40
|
+
1. **Marker Contract**: Orchestrator writes `state/.wave-in-flight` in the PRIMARY tree before dispatching wave work. The marker is git-ignored, so sibling worktrees checked out during the wave do NOT carry it.
|
|
41
|
+
2. **Pre-Commit Check**: Hook resolves the marker relative to the CURRENT working tree via `git rev-parse --show-toplevel` (NOT a hardcoded `$HOME/aesop` — that resolved to the primary tree from every worktree and blocked the whole fleet mid-wave, the wave-24 incident). Primary tree (has marker during a wave) → exit 1 (reject); sibling worktree (no marker) → exit 0 (allow).
|
|
42
|
+
3. **Override**: User or orchestrator may delete `state/.wave-in-flight` to manually allow commits to primary tree.
|
|
43
|
+
|
|
44
|
+
**Exit Contract**:
|
|
45
|
+
- Exit 0: Marker absent, commit allowed (normal operation)
|
|
46
|
+
- Exit 1: Marker present, commit blocked with clear error message
|
|
47
|
+
|
|
48
|
+
**Error Message**:
|
|
49
|
+
```
|
|
50
|
+
Error: Wave in flight. Commit from a sibling worktree, or clear <marker_path> to override.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Installation**:
|
|
54
|
+
- Run `bash hooks/install-waveguard.sh` to idempotently install into `.git/hooks/pre-commit`.
|
|
55
|
+
- If a pre-commit hook already exists, installer backs it up (`.git/hooks/pre-commit.waveguard-backup`) and wraps both (waveguard first, then existing hook if present).
|
|
56
|
+
|
|
57
|
+
**Idempotency**:
|
|
58
|
+
- Installer checks if hook already calls waveguard; skips if already installed.
|
|
59
|
+
- Safe to re-run multiple times.
|
|
60
|
+
|
|
61
|
+
## Hook: hooks/claude/force-model-policy.mjs
|
|
62
|
+
|
|
63
|
+
Claude Code hook enforcing subagent Haiku dispatch (cost optimization).
|
|
64
|
+
|
|
65
|
+
**Trigger**: On skill invocation or task delegation from main orchestrator thread. Examines Claude API request and enforces model constraint.
|
|
66
|
+
|
|
67
|
+
**Policy**:
|
|
68
|
+
- **Main orchestrator** (Fable/Opus on primary): no override (uses native model)
|
|
69
|
+
- **Subagent dispatch** (fleet workers): **enforce Haiku** (claude-haiku-4-5-*). Exit 1 on non-Haiku model request.
|
|
70
|
+
- **Specialists** (typed dispatches): Pin model to Haiku in the dispatch call; hook validates + blocks if violated.
|
|
71
|
+
|
|
72
|
+
**Logging**:
|
|
73
|
+
- On policy violation: log to `state/SECURITY-ALERTS.log` with timestamp, model-name, worker-id, and reason.
|
|
74
|
+
- No alerts on compliant requests.
|
|
75
|
+
|
|
76
|
+
**Self-Test**:
|
|
77
|
+
- `node hooks/claude/force-model-policy.mjs --test` validates:
|
|
78
|
+
1. Haiku model allowed on subagents
|
|
79
|
+
2. Non-Haiku (e.g., Opus) blocked on subagents
|
|
80
|
+
3. Orchestrator not subject to policy
|
|
81
|
+
4. JSON logging format is valid
|
|
82
|
+
- Exit 0 = all pass; exit 1 = any fail
|
|
83
|
+
|
|
33
84
|
## Invariants
|
|
34
85
|
|
|
35
86
|
- POSIX sh compatible, CRLF-safe (no line continuations)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -uo pipefail
|
|
3
|
+
|
|
4
|
+
main() {
|
|
5
|
+
local repo_root
|
|
6
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
7
|
+
if [ -z "$repo_root" ]; then
|
|
8
|
+
printf 'Error: Not in a git repository\n' >&2
|
|
9
|
+
return 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
local hooks_dir="$repo_root/.git/hooks"
|
|
13
|
+
local waveguard_src="$repo_root/hooks/pre-commit-waveguard.sh"
|
|
14
|
+
local pre_commit_dest="$hooks_dir/pre-commit"
|
|
15
|
+
local pre_commit_orig="$hooks_dir/pre-commit.waveguard-backup"
|
|
16
|
+
|
|
17
|
+
if [ ! -f "$waveguard_src" ]; then
|
|
18
|
+
printf 'Error: Source hook not found at %s\n' "$waveguard_src" >&2
|
|
19
|
+
return 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
mkdir -p "$hooks_dir"
|
|
23
|
+
|
|
24
|
+
if [ -f "$pre_commit_dest" ]; then
|
|
25
|
+
if grep -q 'pre-commit-waveguard' "$pre_commit_dest" 2>/dev/null; then
|
|
26
|
+
printf 'Info: pre-commit hook already has waveguard; skipping.\n'
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
cp "$pre_commit_dest" "$pre_commit_orig"
|
|
31
|
+
printf 'Info: Existing pre-commit hook backed up to %s\n' "$pre_commit_orig"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
cat > "$pre_commit_dest" <<'HOOK_WRAPPER'
|
|
35
|
+
#!/usr/bin/env bash
|
|
36
|
+
set -uo pipefail
|
|
37
|
+
|
|
38
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
|
39
|
+
if [ -z "$repo_root" ]; then
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
waveguard_hook="$repo_root/hooks/pre-commit-waveguard.sh"
|
|
44
|
+
if [ -f "$waveguard_hook" ]; then
|
|
45
|
+
bash "$waveguard_hook"
|
|
46
|
+
waveguard_exit=$?
|
|
47
|
+
if [ $waveguard_exit -ne 0 ]; then
|
|
48
|
+
exit $waveguard_exit
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
backup_hook="$repo_root/.git/hooks/pre-commit.waveguard-backup"
|
|
53
|
+
if [ -f "$backup_hook" ] && [ -x "$backup_hook" ]; then
|
|
54
|
+
bash "$backup_hook"
|
|
55
|
+
exit $?
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
exit 0
|
|
59
|
+
HOOK_WRAPPER
|
|
60
|
+
|
|
61
|
+
chmod +x "$pre_commit_dest"
|
|
62
|
+
printf 'Info: Installed waveguard pre-commit hook to %s\n' "$pre_commit_dest"
|
|
63
|
+
return 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
|
67
|
+
main "$@"
|
|
68
|
+
fi
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -uo pipefail
|
|
3
|
+
|
|
4
|
+
main() {
|
|
5
|
+
# Resolve the marker relative to the CURRENT working tree, NOT a hardcoded primary path.
|
|
6
|
+
# This is load-bearing: the marker (state/.wave-in-flight) is git-ignored, so a sibling
|
|
7
|
+
# worktree checked out during a wave does NOT carry it — only the PRIMARY tree (where the
|
|
8
|
+
# orchestrator sets it) does. Checking the current tree's own toplevel means primary-tree
|
|
9
|
+
# commits are blocked during a wave while legitimate worktree-agent commits pass. A prior
|
|
10
|
+
# version hardcoded ${AESOP_ROOT:-$HOME/aesop}, which resolved to the primary tree from every
|
|
11
|
+
# worktree and thus blocked the entire fleet mid-wave (wave-24 incident). Do not reintroduce.
|
|
12
|
+
local toplevel
|
|
13
|
+
toplevel=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
|
|
14
|
+
local marker_file="$toplevel/state/.wave-in-flight"
|
|
15
|
+
|
|
16
|
+
if [ -f "$marker_file" ]; then
|
|
17
|
+
printf 'Error: Wave in flight in this tree (%s). Commit from a sibling worktree, or clear the marker to override.\n' "$marker_file" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
exit 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
|
25
|
+
main "$@"
|
|
26
|
+
fi
|