@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.
Files changed (134) hide show
  1. package/CHANGELOG.md +187 -5
  2. package/LICENSE +66 -21
  3. package/README.md +143 -23
  4. package/aesop.config.example.json +14 -1
  5. package/bin/CLAUDE.md +42 -5
  6. package/bin/cli.js +479 -95
  7. package/daemons/CLAUDE.md +5 -2
  8. package/daemons/backup-fleet.sh +209 -51
  9. package/daemons/run-watchdog.sh +211 -62
  10. package/dash/dash-extra.mjs +73 -4
  11. package/dash/watchdog-gui.sh +41 -35
  12. package/docs/ARCHITECTURE.md +296 -0
  13. package/docs/CONCEPTS.md +254 -0
  14. package/docs/CONFIGURE.md +256 -0
  15. package/docs/FIRST-WAVE.md +276 -0
  16. package/docs/INSTALL.md +224 -0
  17. package/docs/README.md +176 -27
  18. package/docs/RELEASING.md +159 -0
  19. package/docs/archive/README.md +3 -0
  20. package/docs/{spikes → archive/spikes}/tiered-cognition/README.md +1 -3
  21. package/docs/autonomous-swe.md +149 -0
  22. package/docs/case-study-portfolio.md +61 -0
  23. package/docs/reproduce.md +121 -0
  24. package/docs/self-stats-data.json +11 -0
  25. package/docs/templates/FLEET-OPS-ANALYSIS.example.md +27 -0
  26. package/docs/templates/FLEET-OPS-RECOMMENDATIONS.example.md +24 -0
  27. package/docs/templates/PROPOSALS-LOG.example.md +64 -0
  28. package/hooks/CLAUDE.md +51 -0
  29. package/hooks/install-waveguard.sh +68 -0
  30. package/hooks/pre-commit-waveguard.sh +26 -0
  31. package/hooks/pre-push-policy.sh +253 -15
  32. package/mcp/CLAUDE.md +213 -0
  33. package/mcp/package.json +26 -0
  34. package/mcp/server.mjs +543 -0
  35. package/monitor/CHARTER.md +76 -110
  36. package/monitor/CLAUDE.md +3 -3
  37. package/monitor/collect-signals.mjs +218 -20
  38. package/package.json +17 -6
  39. package/scan/CLAUDE.md +30 -0
  40. package/scan/fleet-scan.example.mjs +292 -0
  41. package/skills/CLAUDE.md +1 -0
  42. package/skills/healthcheck/SKILL.md +44 -0
  43. package/state_store/CLAUDE.md +39 -0
  44. package/state_store/__init__.py +24 -0
  45. package/state_store/api.py +35 -0
  46. package/state_store/export.py +19 -0
  47. package/state_store/ingest.py +41 -0
  48. package/state_store/projections.py +126 -0
  49. package/state_store/store.py +197 -0
  50. package/tools/CLAUDE.md +37 -149
  51. package/tools/alert_bridge.py +453 -0
  52. package/tools/bench_runner.py +438 -0
  53. package/tools/buildlog.py +4 -7
  54. package/tools/ci_merge_wait.py +308 -14
  55. package/tools/common.py +62 -0
  56. package/tools/cost_ceiling.py +191 -0
  57. package/tools/dash.js +102 -0
  58. package/tools/doctor.js +220 -0
  59. package/tools/fleet_ledger.py +189 -17
  60. package/tools/fleet_prompt_extractor.py +134 -0
  61. package/tools/halt.py +172 -0
  62. package/tools/healthcheck.py +299 -0
  63. package/tools/heartbeat.py +4 -7
  64. package/tools/metrics_gate.py +8 -2
  65. package/tools/orchestrator_status.py +4 -9
  66. package/tools/reconcile.py +277 -0
  67. package/tools/rotate_logs.py +152 -62
  68. package/tools/scanner_selftest.py +28 -3
  69. package/tools/secret_scan.py +367 -91
  70. package/tools/self_stats.py +797 -0
  71. package/tools/session_usage_summary.py +198 -0
  72. package/tools/status.js +187 -0
  73. package/tools/svg_to_png.mjs +50 -0
  74. package/tools/transcript_replay.py +236 -0
  75. package/tools/transcript_timeline.py +184 -0
  76. package/tools/verify_agent_inspector.py +289 -0
  77. package/tools/verify_dash.py +361 -542
  78. package/tools/verify_prboard.py +344 -0
  79. package/tools/verify_submit_encoding.py +12 -4
  80. package/tools/watch.js +49 -0
  81. package/ui/CLAUDE.md +59 -41
  82. package/ui/agents.py +337 -34
  83. package/ui/api/submit.py +44 -5
  84. package/ui/api/tracker.py +15 -7
  85. package/ui/collectors.py +294 -52
  86. package/ui/config.py +31 -2
  87. package/ui/cost.py +355 -0
  88. package/ui/csrf.py +9 -4
  89. package/ui/handler.py +450 -12
  90. package/ui/render.py +26 -6
  91. package/ui/sse.py +114 -13
  92. package/ui/wave_prs.py +230 -0
  93. package/ui/web/dist/assets/index-0qQYnvMC.js +9 -0
  94. package/ui/web/dist/assets/index-BdIlFieV.css +1 -0
  95. package/ui/web/dist/index.html +14 -0
  96. package/monitor/.signal-state.json +0 -3
  97. package/monitor/ACTIONS.log +0 -1
  98. package/monitor/BRIEF.md +0 -24
  99. package/monitor/SIGNALS.json +0 -54
  100. package/tools/__pycache__/buildlog.cpython-314.pyc +0 -0
  101. package/tools/__pycache__/ci_merge_wait.cpython-314.pyc +0 -0
  102. package/tools/__pycache__/ensure_state.cpython-314.pyc +0 -0
  103. package/tools/__pycache__/eod_sweep.cpython-314.pyc +0 -0
  104. package/tools/__pycache__/fleet_ledger.cpython-314.pyc +0 -0
  105. package/tools/__pycache__/heartbeat.cpython-314.pyc +0 -0
  106. package/tools/__pycache__/inbox_drain.cpython-314.pyc +0 -0
  107. package/tools/__pycache__/launch_tui.cpython-314.pyc +0 -0
  108. package/tools/__pycache__/orchestrator_status.cpython-314.pyc +0 -0
  109. package/tools/__pycache__/power_selftest.cpython-314.pyc +0 -0
  110. package/tools/__pycache__/prepublish_scan.cpython-314.pyc +0 -0
  111. package/tools/__pycache__/rotate_logs.cpython-314.pyc +0 -0
  112. package/tools/__pycache__/scanner_selftest.cpython-314.pyc +0 -0
  113. package/tools/__pycache__/secret_scan.cpython-314.pyc +0 -0
  114. package/tools/__pycache__/stall_check.cpython-314.pyc +0 -0
  115. package/tools/__pycache__/verify_dash.cpython-314.pyc +0 -0
  116. package/tools/__pycache__/verify_submit_encoding.cpython-314.pyc +0 -0
  117. package/ui/__pycache__/agents.cpython-314.pyc +0 -0
  118. package/ui/__pycache__/collectors.cpython-314.pyc +0 -0
  119. package/ui/__pycache__/config.cpython-314.pyc +0 -0
  120. package/ui/__pycache__/csrf.cpython-314.pyc +0 -0
  121. package/ui/__pycache__/handler.cpython-314.pyc +0 -0
  122. package/ui/__pycache__/render.cpython-314.pyc +0 -0
  123. package/ui/__pycache__/serve.cpython-314.pyc +0 -0
  124. package/ui/__pycache__/sse.cpython-314.pyc +0 -0
  125. package/ui/api/__pycache__/__init__.cpython-314.pyc +0 -0
  126. package/ui/api/__pycache__/submit.cpython-314.pyc +0 -0
  127. package/ui/api/__pycache__/tracker.cpython-314.pyc +0 -0
  128. package/ui/templates/dashboard.html +0 -1202
  129. /package/docs/{spikes → archive/spikes}/tiered-cognition/ACTIVATION.md +0 -0
  130. /package/docs/{spikes → archive/spikes}/tiered-cognition/DESIGN.md +0 -0
  131. /package/docs/{spikes → archive/spikes}/tiered-cognition/FINDINGS.md +0 -0
  132. /package/docs/{spikes → archive/spikes}/tiered-cognition/aesop-cognition.example.md +0 -0
  133. /package/docs/{spikes → archive/spikes}/tiered-cognition/force-model-policy.merged.mjs +0 -0
  134. /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