@matt82198/aesop 0.1.0-beta.4 → 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.
Files changed (174) hide show
  1. package/CHANGELOG.md +46 -5
  2. package/README.md +57 -1
  3. package/aesop.config.example.json +8 -1
  4. package/bin/CLAUDE.md +30 -2
  5. package/bin/cli.js +286 -73
  6. package/daemons/CLAUDE.md +4 -1
  7. package/daemons/run-watchdog.sh +5 -2
  8. package/docs/RELEASING.md +159 -0
  9. package/docs/archive/README.md +3 -0
  10. package/docs/{spikes → archive/spikes}/tiered-cognition/README.md +1 -3
  11. package/docs/case-study-portfolio.md +61 -0
  12. package/docs/self-stats-data.json +11 -0
  13. package/docs/templates/FLEET-OPS-ANALYSIS.example.md +27 -0
  14. package/docs/templates/FLEET-OPS-RECOMMENDATIONS.example.md +24 -0
  15. package/docs/templates/PROPOSALS-LOG.example.md +64 -0
  16. package/hooks/CLAUDE.md +23 -0
  17. package/mcp/CLAUDE.md +213 -0
  18. package/mcp/package.json +26 -0
  19. package/mcp/server.mjs +543 -0
  20. package/monitor/CHARTER.md +76 -110
  21. package/monitor/collect-signals.mjs +85 -0
  22. package/package.json +4 -1
  23. package/scan/fleet-scan.example.mjs +292 -0
  24. package/skills/healthcheck/SKILL.md +44 -0
  25. package/state_store/CLAUDE.md +39 -0
  26. package/state_store/__init__.py +24 -0
  27. package/state_store/__pycache__/__init__.cpython-314.pyc +0 -0
  28. package/state_store/__pycache__/api.cpython-314.pyc +0 -0
  29. package/state_store/__pycache__/export.cpython-314.pyc +0 -0
  30. package/state_store/__pycache__/ingest.cpython-314.pyc +0 -0
  31. package/state_store/__pycache__/projections.cpython-314.pyc +0 -0
  32. package/state_store/__pycache__/store.cpython-314.pyc +0 -0
  33. package/state_store/api.py +35 -0
  34. package/state_store/export.py +19 -0
  35. package/state_store/ingest.py +24 -0
  36. package/state_store/projections.py +52 -0
  37. package/state_store/store.py +102 -0
  38. package/tools/CLAUDE.md +30 -149
  39. package/tools/__pycache__/alert_bridge.cpython-314.pyc +0 -0
  40. package/tools/__pycache__/ci_merge_wait.cpython-314.pyc +0 -0
  41. package/tools/__pycache__/fleet_prompt_extractor.cpython-314.pyc +0 -0
  42. package/tools/__pycache__/healthcheck.cpython-314.pyc +0 -0
  43. package/tools/__pycache__/metrics_gate.cpython-314.pyc +0 -0
  44. package/tools/__pycache__/secret_scan.cpython-314.pyc +0 -0
  45. package/tools/__pycache__/self_stats.cpython-314.pyc +0 -0
  46. package/tools/__pycache__/session_usage_summary.cpython-314.pyc +0 -0
  47. package/tools/__pycache__/stall_check.cpython-314.pyc +0 -0
  48. package/tools/__pycache__/transcript_replay.cpython-314.pyc +0 -0
  49. package/tools/__pycache__/transcript_timeline.cpython-314.pyc +0 -0
  50. package/tools/__pycache__/verify_dash.cpython-314.pyc +0 -0
  51. package/tools/__pycache__/verify_submit_encoding.cpython-314.pyc +0 -0
  52. package/tools/alert_bridge.py +449 -0
  53. package/tools/ci_merge_wait.py +121 -8
  54. package/tools/fleet_prompt_extractor.py +134 -0
  55. package/tools/healthcheck.py +296 -0
  56. package/tools/secret_scan.py +8 -1
  57. package/tools/self_stats.py +509 -0
  58. package/tools/session_usage_summary.py +198 -0
  59. package/tools/svg_to_png.mjs +50 -0
  60. package/tools/transcript_replay.py +236 -0
  61. package/tools/transcript_timeline.py +184 -0
  62. package/tools/verify_dash.py +361 -542
  63. package/tools/verify_submit_encoding.py +12 -4
  64. package/ui/CLAUDE.md +57 -41
  65. package/ui/__pycache__/agents.cpython-314.pyc +0 -0
  66. package/ui/__pycache__/collectors.cpython-314.pyc +0 -0
  67. package/ui/__pycache__/config.cpython-314.pyc +0 -0
  68. package/ui/__pycache__/cost.cpython-314.pyc +0 -0
  69. package/ui/__pycache__/csrf.cpython-314.pyc +0 -0
  70. package/ui/__pycache__/handler.cpython-314.pyc +0 -0
  71. package/ui/__pycache__/render.cpython-314.pyc +0 -0
  72. package/ui/__pycache__/serve.cpython-314.pyc +0 -0
  73. package/ui/__pycache__/sse.cpython-314.pyc +0 -0
  74. package/ui/agents.py +9 -5
  75. package/ui/api/__pycache__/submit.cpython-314.pyc +0 -0
  76. package/ui/api/submit.py +44 -5
  77. package/ui/collectors.py +184 -35
  78. package/ui/config.py +9 -0
  79. package/ui/cost.py +260 -0
  80. package/ui/csrf.py +7 -3
  81. package/ui/handler.py +254 -4
  82. package/ui/render.py +26 -6
  83. package/ui/sse.py +16 -1
  84. package/ui/web/.gitattributes +13 -0
  85. package/ui/web/dist/assets/index-2LZDQirC.js +9 -0
  86. package/ui/web/dist/assets/index-D4M1qyOv.css +1 -0
  87. package/ui/web/dist/index.html +14 -0
  88. package/ui/web/index.html +13 -0
  89. package/ui/web/package-lock.json +2225 -0
  90. package/ui/web/package.json +26 -0
  91. package/ui/web/src/App.test.tsx +74 -0
  92. package/ui/web/src/App.tsx +142 -0
  93. package/ui/web/src/CONTRIBUTING-UI.md +49 -0
  94. package/ui/web/src/components/AgentRow.css +187 -0
  95. package/ui/web/src/components/AgentRow.test.tsx +209 -0
  96. package/ui/web/src/components/AgentRow.tsx +207 -0
  97. package/ui/web/src/components/AgentsPanel.css +108 -0
  98. package/ui/web/src/components/AgentsPanel.test.tsx +41 -0
  99. package/ui/web/src/components/AgentsPanel.tsx +58 -0
  100. package/ui/web/src/components/AlertsPanel.css +88 -0
  101. package/ui/web/src/components/AlertsPanel.test.tsx +51 -0
  102. package/ui/web/src/components/AlertsPanel.tsx +67 -0
  103. package/ui/web/src/components/BacklogPanel.test.tsx +126 -0
  104. package/ui/web/src/components/BacklogPanel.tsx +122 -0
  105. package/ui/web/src/components/CostChart.css +110 -0
  106. package/ui/web/src/components/CostChart.test.tsx +144 -0
  107. package/ui/web/src/components/CostChart.tsx +152 -0
  108. package/ui/web/src/components/CostTable.css +93 -0
  109. package/ui/web/src/components/CostTable.test.tsx +165 -0
  110. package/ui/web/src/components/CostTable.tsx +94 -0
  111. package/ui/web/src/components/EventsFeed.css +68 -0
  112. package/ui/web/src/components/EventsFeed.test.tsx +36 -0
  113. package/ui/web/src/components/EventsFeed.tsx +31 -0
  114. package/ui/web/src/components/HealthHeader.css +137 -0
  115. package/ui/web/src/components/HealthHeader.test.tsx +278 -0
  116. package/ui/web/src/components/HealthHeader.tsx +281 -0
  117. package/ui/web/src/components/InboxForm.css +135 -0
  118. package/ui/web/src/components/InboxForm.test.tsx +208 -0
  119. package/ui/web/src/components/InboxForm.tsx +116 -0
  120. package/ui/web/src/components/MessagesTail.module.css +144 -0
  121. package/ui/web/src/components/MessagesTail.test.tsx +176 -0
  122. package/ui/web/src/components/MessagesTail.tsx +94 -0
  123. package/ui/web/src/components/ReposPanel.css +90 -0
  124. package/ui/web/src/components/ReposPanel.test.tsx +45 -0
  125. package/ui/web/src/components/ReposPanel.tsx +67 -0
  126. package/ui/web/src/components/Scorecard.css +106 -0
  127. package/ui/web/src/components/Scorecard.test.tsx +117 -0
  128. package/ui/web/src/components/Scorecard.tsx +85 -0
  129. package/ui/web/src/components/Timeline.module.css +151 -0
  130. package/ui/web/src/components/Timeline.test.tsx +215 -0
  131. package/ui/web/src/components/Timeline.tsx +99 -0
  132. package/ui/web/src/components/TrackerBoard.test.tsx +121 -0
  133. package/ui/web/src/components/TrackerBoard.tsx +107 -0
  134. package/ui/web/src/components/TrackerCard.test.tsx +180 -0
  135. package/ui/web/src/components/TrackerCard.tsx +160 -0
  136. package/ui/web/src/components/TrackerForm.test.tsx +189 -0
  137. package/ui/web/src/components/TrackerForm.tsx +144 -0
  138. package/ui/web/src/lib/api.ts +218 -0
  139. package/ui/web/src/lib/format.test.ts +89 -0
  140. package/ui/web/src/lib/format.ts +103 -0
  141. package/ui/web/src/lib/sanitizeUrl.test.ts +84 -0
  142. package/ui/web/src/lib/sanitizeUrl.ts +38 -0
  143. package/ui/web/src/lib/types.ts +230 -0
  144. package/ui/web/src/lib/useHashRoute.test.ts +60 -0
  145. package/ui/web/src/lib/useHashRoute.ts +23 -0
  146. package/ui/web/src/lib/useSSE.ts +175 -0
  147. package/ui/web/src/main.tsx +10 -0
  148. package/ui/web/src/styles/global.css +179 -0
  149. package/ui/web/src/styles/theme.css +184 -0
  150. package/ui/web/src/styles/work.css +572 -0
  151. package/ui/web/src/test/fixtures.ts +385 -0
  152. package/ui/web/src/test/setup.ts +49 -0
  153. package/ui/web/src/views/Activity.module.css +43 -0
  154. package/ui/web/src/views/Activity.test.tsx +89 -0
  155. package/ui/web/src/views/Activity.tsx +31 -0
  156. package/ui/web/src/views/Cost.css +87 -0
  157. package/ui/web/src/views/Cost.test.tsx +142 -0
  158. package/ui/web/src/views/Cost.tsx +54 -0
  159. package/ui/web/src/views/Overview.css +51 -0
  160. package/ui/web/src/views/Overview.test.tsx +76 -0
  161. package/ui/web/src/views/Overview.tsx +46 -0
  162. package/ui/web/src/views/Work.test.tsx +82 -0
  163. package/ui/web/src/views/Work.tsx +79 -0
  164. package/ui/web/src/vite-env.d.ts +10 -0
  165. package/ui/web/tsconfig.json +22 -0
  166. package/ui/web/vite.config.ts +25 -0
  167. package/ui/web/vitest.config.ts +12 -0
  168. package/ui/templates/dashboard.html +0 -1202
  169. /package/docs/{spikes → archive/spikes}/tiered-cognition/ACTIVATION.md +0 -0
  170. /package/docs/{spikes → archive/spikes}/tiered-cognition/DESIGN.md +0 -0
  171. /package/docs/{spikes → archive/spikes}/tiered-cognition/FINDINGS.md +0 -0
  172. /package/docs/{spikes → archive/spikes}/tiered-cognition/aesop-cognition.example.md +0 -0
  173. /package/docs/{spikes → archive/spikes}/tiered-cognition/force-model-policy.merged.mjs +0 -0
  174. /package/docs/{spikes → archive/spikes}/tiered-cognition/strip-tools-hook.mjs +0 -0
@@ -0,0 +1,61 @@
1
+ # Case Study: Portfolio Site Build with Aesop
2
+
3
+ ## What Happened (Timeline)
4
+
5
+ | Time | Event | Duration |
6
+ |------|-------|----------|
7
+ | 17:09 | User prompt: "build me a portfolio site" | — |
8
+ | 17:09–17:20 | 6 discovery scouts (GitHub API, Medium, LinkedIn, Reddit ×2, Playwright fallback, repo deep-dive) run in parallel; 4 ranked decision points surfaced | 11 min |
9
+ | 17:20–17:24 | User answers 4 prioritization questions | 4 min |
10
+ | 17:24–17:36 | 10 Haiku agents (scaffold + 8 section builders on file-disjoint Astro components + integrator) execute | 12 min |
11
+ | 17:36–17:37 | 9th section added from late Reddit data; Astro build green in 914ms | 1 min |
12
+ | 17:37–17:40 | Playwright screenshot proof round 1: CSS 404 detected (styles linked as `/src/` instead of `/dist/`) | 3 min |
13
+ | 17:40–17:42 | Repair agent fixes CSS paths; proof round 2 validates fix | 2 min |
14
+ | 17:42–17:48 | GitHub agent: secret-scan (10,066 files clean), create public repo, merge PR #1 | 6 min |
15
+ | 18:04 | GitHub Pages deployed and live at https://matt82198.github.io/ (verified 200 status) | **55 min total** |
16
+
17
+ ## The Architecture
18
+
19
+ **Parallel discovery with verified facts only**: Six independent scouts (HTTP fallback to Playwright for bot-blocked sites) collected portfolio inspiration, ranked by relevance; each returned structured data on a single contract. No hallucination—every section claim traces to a source.
20
+
21
+ **File-disjoint fan-out**: 8 section agents each owned one `.astro` component with zero file overlap; no merge conflicts, no serialization bottleneck. Integrator assembled in 12 minutes.
22
+
23
+ **Proof-driven QA**: Screenshot verification caught a real ship-blocker—styles in dist/ referenced `/src/` paths that vanished at build time. No visual inspection could have found this; Playwright proved it was broken, repair proved it was fixed.
24
+
25
+ **Gated ship**: Secret-scan gate (10,066 files) ran before public repo creation; zero credentials leaked. PR merged, Pages deploy triggered, liveness confirmed.
26
+
27
+ **Orchestrator final-catch**: Fable verified theme coherence across 9 sections after assembly; caught one color inconsistency and one missing accent variant before ship.
28
+
29
+ ## What Failed (and Was Caught)
30
+
31
+ The site rendered unstyled in production. Astro build succeeded locally (914ms), but the distributed `.astro` component agents—writing styles inline—used `import` statements pointing to source paths. After minification and bundling into `/dist/`, those paths no longer existed. The site shipped with no CSS.
32
+
33
+ Playwright screenshot automation caught this in production-like conditions (rendering against the compiled bundle). A repair agent updated path resolution, regenerated the build, and Playwright proved the fix before merge. This is not a testing infrastructure story—this is proof automation as a first-class gate.
34
+
35
+ ## Numbers
36
+
37
+ - **Wall-clock time**: 55 minutes (first prompt to live site)
38
+ - **Total agents used**: ~30 (6 scouts, 10 builders, 1 repair, 1 GitHub ops, 3 proof/integration, 9 audits)
39
+ - **Build fleet**: 10 Haiku agents (scaffold + 8 section builders + integrator)
40
+ - **Build time**: 12 minutes (including inter-agent coordination)
41
+ - **Sections delivered**: 9
42
+ - **Proof rounds**: 2 (CSS 404 caught in round 1; fix validated in round 2)
43
+ - **Ship-blockers caught**: 1 (CSS 404)
44
+ - **Files secret-scanned**: 10,066 (0 issues)
45
+ - **Subagent tokens**: ~328K
46
+ - **PR**: #1, merged
47
+ - **Deploy**: GitHub Pages, live 200 status
48
+
49
+ ## How to Reproduce
50
+
51
+ 1. Fork this repo or set up Aesop in your environment.
52
+ 2. Run the [buildsystem skill](/skills/CLAUDE.md) for a portfolio project.
53
+ 3. Provide 4–6 source URLs or inspiration topics; Aesop will dispatch 6 discovery scouts.
54
+ 4. Approve the ranked backlog (user decision round).
55
+ 5. Aesop dispatches the 10-agent build workflow in parallel on Astro or your framework.
56
+ 6. Screenshot proof runs automatically; Aesop fixes any visual defects and re-proves.
57
+ 7. Secret-scan gate runs; if clean, Aesop creates the public repo and merges the PR.
58
+ 8. Pages (or your host) deploys automatically.
59
+
60
+ The 55-minute bound is achievable when discovery sources are web-accessible (not gated PDFs) and the component schema is well-defined (Astro's `.astro` files are ideal; monolithic templates require serialization).
61
+
@@ -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,29 @@ 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: hooks/claude/force-model-policy.mjs
34
+
35
+ Claude Code hook enforcing subagent Haiku dispatch (cost optimization).
36
+
37
+ **Trigger**: On skill invocation or task delegation from main orchestrator thread. Examines Claude API request and enforces model constraint.
38
+
39
+ **Policy**:
40
+ - **Main orchestrator** (Fable/Opus on primary): no override (uses native model)
41
+ - **Subagent dispatch** (fleet workers): **enforce Haiku** (claude-haiku-4-5-*). Exit 1 on non-Haiku model request.
42
+ - **Specialists** (typed dispatches): Pin model to Haiku in the dispatch call; hook validates + blocks if violated.
43
+
44
+ **Logging**:
45
+ - On policy violation: log to `state/SECURITY-ALERTS.log` with timestamp, model-name, worker-id, and reason.
46
+ - No alerts on compliant requests.
47
+
48
+ **Self-Test**:
49
+ - `node hooks/claude/force-model-policy.mjs --test` validates:
50
+ 1. Haiku model allowed on subagents
51
+ 2. Non-Haiku (e.g., Opus) blocked on subagents
52
+ 3. Orchestrator not subject to policy
53
+ 4. JSON logging format is valid
54
+ - Exit 0 = all pass; exit 1 = any fail
55
+
33
56
  ## Invariants
34
57
 
35
58
  - POSIX sh compatible, CRLF-safe (no line continuations)
package/mcp/CLAUDE.md ADDED
@@ -0,0 +1,213 @@
1
+ # mcp/ — Read-Only Fleet State MCP Server
2
+
3
+ **Purpose**: Expose Aesop fleet operational status as a Model Context Protocol (MCP) server for Claude Code and other MCP clients.
4
+
5
+ ## Server
6
+
7
+ **File**: `server.mjs` — Stdio-transport MCP server implementing the JSON-RPC 2.0 protocol.
8
+
9
+ **Configuration**:
10
+ - `AESOP_ROOT` environment variable or `--root <path>` command-line flag (no hardcoded paths)
11
+ - `AESOP_STATE_ROOT` environment variable for state directory override (default: `AESOP_ROOT/state`)
12
+ - `AESOP_TRANSCRIPTS_ROOT` environment variable for transcript directory override (default: `~/.claude/projects`)
13
+ - Reads `aesop.config.json` from AESOP_ROOT for optional config file overrides
14
+
15
+ **Launch**:
16
+ ```bash
17
+ node mcp/server.mjs [--root /path/to/aesop]
18
+ ```
19
+
20
+ The server reads from stdin, writes JSON-RPC responses to stdout. Suitable for direct MCP integration or debugging via manual JSON-RPC frames.
21
+
22
+ ## Tools
23
+
24
+ All tools are **strictly read-only**: no state mutations, no file writes, no shell-outs except the minimal dash-extra.mjs passthrough for fleet_agents.
25
+
26
+ ### fleet_status
27
+
28
+ **Description**: Get fleet operational status — daemon heartbeats, monitor heartbeat, orchestrator activity, alert count.
29
+
30
+ **Input**: No arguments.
31
+
32
+ **Output** (JSON):
33
+ ```json
34
+ {
35
+ "watchdog": {
36
+ "alive": "ALIVE" | "STALE",
37
+ "age_seconds": <bucketed>,
38
+ "threshold_seconds": 300
39
+ },
40
+ "monitor": {
41
+ "alive": "ALIVE" | "STALE",
42
+ "age_seconds": <bucketed>,
43
+ "threshold_seconds": 3600
44
+ },
45
+ "orchestrator": <parsed orchestrator-status.json> or null,
46
+ "alerts": {
47
+ "count": <int>,
48
+ "sample_lines": [<last 3 unreviewed lines>]
49
+ }
50
+ }
51
+ ```
52
+
53
+ **Missing files**: If a file doesn't exist, the corresponding field is `null`. The response never crashes; missing state files are reported gracefully with `absent` markers where applicable.
54
+
55
+ ### fleet_agents
56
+
57
+ **Description**: List active Claude agents from transcript directory. Invokes `dash-extra.mjs --json` with environment variables for path portability.
58
+
59
+ **Input**: No arguments.
60
+
61
+ **Output** (JSON):
62
+ ```json
63
+ {
64
+ "absent": <bool>,
65
+ "agents": [
66
+ {
67
+ "project": <string>,
68
+ "taskLabel": <string>,
69
+ "promptFull": <string>,
70
+ "runtimeSeconds": <int>,
71
+ "tokensUsed": <int>,
72
+ "startedAt": <ISO string>,
73
+ "lastActivity": <ISO string>
74
+ },
75
+ ...
76
+ ]
77
+ }
78
+ ```
79
+
80
+ **Design notes**:
81
+ - Passes through `dash-extra.mjs --json` output (no reimplementation).
82
+ - Timeout: 5 seconds. On timeout or error, returns empty agents list.
83
+ - Environment variables (`AESOP_ROOT`, `AESOP_STATE_ROOT`, `AESOP_TRANSCRIPTS_ROOT`) are set before invoking dash-extra to ensure portable transcript discovery.
84
+
85
+ ### fleet_tracker
86
+
87
+ **Description**: Get fleet work items from `tracker.json`, grouped by lane.
88
+
89
+ **Input**: No arguments.
90
+
91
+ **Output** (JSON):
92
+ ```json
93
+ {
94
+ "absent": <bool>,
95
+ "by_lane": {
96
+ "ranked": [
97
+ {
98
+ "id": <string>,
99
+ "title": <string>,
100
+ "priority": "P0" | "P1" | "P2",
101
+ "status": "todo" | "in-progress" | "done",
102
+ "tags": [<string>, ...]
103
+ },
104
+ ...
105
+ ],
106
+ "in-progress": [ ... ],
107
+ "done": [ ... ],
108
+ ...
109
+ }
110
+ }
111
+ ```
112
+
113
+ **Lanes**: Each lane is optional and present only if items exist in it. Common lanes: `ranked`, `proposed`, `in-progress`, `done`.
114
+
115
+ ### fleet_cost
116
+
117
+ **Description**: Get per-model token usage totals from the fleet outcomes ledger (`state/ledger/OUTCOMES-LEDGER.md`).
118
+
119
+ **Input**: No arguments.
120
+
121
+ **Output** (JSON):
122
+ ```json
123
+ {
124
+ "absent": <bool>,
125
+ "by_model": {
126
+ "claude-haiku-4": {
127
+ "tokens_in": <int>,
128
+ "tokens_out": <int>,
129
+ "total_tokens": <int>,
130
+ "count": <int>
131
+ },
132
+ "claude-opus": { ... },
133
+ ...
134
+ },
135
+ "total_tokens_in": <int>,
136
+ "total_tokens_out": <int>
137
+ }
138
+ ```
139
+
140
+ **Design notes**:
141
+ - Parses markdown table rows from the ledger file.
142
+ - Reports **token counts only** — no invented dollar figures.
143
+ - Gracefully handles missing or malformed lines (skips with no error).
144
+
145
+ ## MCP Integration
146
+
147
+ ### Claude Code (claude.json)
148
+
149
+ To register the server in Claude Code:
150
+
151
+ ```json
152
+ {
153
+ "mcp": {
154
+ "aesop-fleet": {
155
+ "command": "node",
156
+ "args": [
157
+ "/path/to/aesop/mcp/server.mjs",
158
+ "--root",
159
+ "/path/to/aesop"
160
+ ],
161
+ "env": {
162
+ "AESOP_ROOT": "/path/to/aesop"
163
+ }
164
+ }
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### Portable Setup
170
+
171
+ For portable configuration (no hardcoded user paths), use environment variables:
172
+
173
+ ```bash
174
+ export AESOP_ROOT=/path/to/aesop
175
+ export AESOP_STATE_ROOT=/path/to/state # optional override
176
+ export AESOP_TRANSCRIPTS_ROOT=/path/to/transcripts # optional override
177
+ node /path/to/aesop/mcp/server.mjs
178
+ ```
179
+
180
+ Or use the `--root` flag:
181
+
182
+ ```bash
183
+ node /path/to/aesop/mcp/server.mjs --root /path/to/aesop
184
+ ```
185
+
186
+ ## Invariants & Guarantees
187
+
188
+ 1. **Strictly read-only**: All tools are read-only. Zero state mutations, zero file writes.
189
+ 2. **No shell execution** (except dash-extra.mjs invocation with timeout): Minimizes surface area for injection or performance issues.
190
+ 3. **Graceful degradation**: Missing state files → empty results with `absent: true` markers, never a crash.
191
+ 4. **No external dependencies**: Plain Node.js (no npm packages). Server.mjs uses only stdlib modules: `fs`, `path`, `os`, `child_process`, `readline`.
192
+ 5. **JSON-RPC 2.0 compliant**: Standard request/response format with proper error codes.
193
+ 6. **Portable paths**: All paths resolved from AESOP_ROOT, AESOP_STATE_ROOT, AESOP_TRANSCRIPTS_ROOT env vars or config file. No hardcoded paths like `/home/user/aesop`.
194
+
195
+ ## Testing
196
+
197
+ See `tests/mcp-fleet.test.mjs` for end-to-end tests:
198
+ - Spawn server over stdio
199
+ - Drive JSON-RPC initialize + tools/list + tools/call round-trips
200
+ - Validate read-only behavior (no state mutations after calls)
201
+ - Verify fixture isolation (temp state root)
202
+
203
+ Run:
204
+ ```bash
205
+ npm test # or
206
+ node tests/mcp-fleet.test.mjs
207
+ ```
208
+
209
+ ## Files
210
+
211
+ - **server.mjs** — Main MCP server implementation (stdio, JSON-RPC 2.0)
212
+ - **package.json** — Minimal metadata; no external dependencies
213
+ - **CLAUDE.md** — This file
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "aesop-fleet-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Read-only MCP server for Aesop fleet state (heartbeats, agents, tracker, costs)",
5
+ "type": "module",
6
+ "main": "server.mjs",
7
+ "bin": {
8
+ "aesop-fleet": "server.mjs"
9
+ },
10
+ "scripts": {
11
+ "start": "node server.mjs",
12
+ "start:root": "node server.mjs --root .."
13
+ },
14
+ "keywords": [
15
+ "mcp",
16
+ "model-context-protocol",
17
+ "orchestration",
18
+ "aesop"
19
+ ],
20
+ "author": "Anthropic",
21
+ "license": "MIT",
22
+ "dependencies": {},
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ }
26
+ }