@matt82198/aesop 0.1.0 → 0.3.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 (130) hide show
  1. package/CHANGELOG.md +117 -2
  2. package/README.md +59 -218
  3. package/bin/cli.js +168 -41
  4. package/daemons/run-watchdog.sh +16 -4
  5. package/daemons/selfheal.sh +231 -0
  6. package/docs/ANY-REPO.md +427 -0
  7. package/docs/CONTRIBUTING.md +72 -0
  8. package/docs/DEMO.md +334 -0
  9. package/docs/HOOK-INSTALL.md +15 -56
  10. package/docs/INSTALL.md +74 -4
  11. package/docs/PORTING.md +166 -0
  12. package/docs/README.md +33 -3
  13. package/docs/TEAM-STATE.md +372 -0
  14. package/docs/reproduce.md +33 -3
  15. package/driver/CLAUDE.md +150 -0
  16. package/driver/README.md +383 -0
  17. package/driver/aesop.config.example.json +80 -0
  18. package/driver/agent_driver.py +355 -0
  19. package/driver/backend_config.py +253 -0
  20. package/driver/claude_code_driver.py +198 -0
  21. package/driver/codex_driver.py +673 -0
  22. package/driver/openai_compatible_driver.py +249 -0
  23. package/driver/openai_transport.py +179 -0
  24. package/driver/verification_policy.py +75 -0
  25. package/driver/wave_bridge.py +254 -0
  26. package/driver/wave_loop.py +1408 -0
  27. package/driver/wave_scheduler.py +890 -0
  28. package/hooks/pre-push-policy.sh +131 -33
  29. package/mcp/server.mjs +320 -4
  30. package/monitor/collect-signals.mjs +69 -0
  31. package/package.json +22 -14
  32. package/skills/CLAUDE.md +132 -2
  33. package/skills/buildsystem/SKILL.md +330 -0
  34. package/skills/buildsystem/wave-flat-dispatch.template.mjs +658 -0
  35. package/skills/fleet/SKILL.md +113 -0
  36. package/skills/power/SKILL.md +246 -131
  37. package/state_store/__init__.py +4 -2
  38. package/state_store/api.py +19 -4
  39. package/state_store/coordination.py +209 -0
  40. package/state_store/identity.py +51 -0
  41. package/state_store/projections.py +63 -0
  42. package/state_store/read_api.py +156 -0
  43. package/state_store/store.py +185 -73
  44. package/state_store/write_api.py +462 -0
  45. package/templates/wave-presets/data.json +65 -0
  46. package/templates/wave-presets/library.json +65 -0
  47. package/templates/wave-presets/saas.json +64 -0
  48. package/tools/audit_report.py +388 -0
  49. package/tools/bench_runner.py +100 -3
  50. package/tools/ci_merge_wait.py +256 -35
  51. package/tools/ci_workflow_lint.py +430 -0
  52. package/tools/claudemd_drift.py +394 -0
  53. package/tools/claudemd_lint.py +359 -0
  54. package/tools/common.py +39 -3
  55. package/tools/cost_ceiling.py +166 -43
  56. package/tools/cost_econ.py +480 -0
  57. package/tools/cost_projection.py +559 -0
  58. package/tools/crossos_drift.py +394 -0
  59. package/tools/defect_escape.py +252 -0
  60. package/tools/doctor.js +1 -1
  61. package/tools/eod_sweep.py +188 -26
  62. package/tools/fleet.js +260 -0
  63. package/tools/fleet_ledger.py +209 -7
  64. package/tools/git_identity_check.py +315 -0
  65. package/tools/health-score.js +40 -0
  66. package/tools/health_score.py +361 -0
  67. package/tools/metrics_gate.py +13 -4
  68. package/tools/mutation_test.py +523 -0
  69. package/tools/portability_check.py +206 -0
  70. package/tools/proposals.mjs +47 -2
  71. package/tools/reconcile.py +7 -4
  72. package/tools/reproduce.js +405 -0
  73. package/tools/secret_scan.py +207 -65
  74. package/tools/self_stats.py +20 -0
  75. package/tools/stall_check.py +247 -16
  76. package/tools/stateapi_lint.py +325 -0
  77. package/tools/test_battery.py +173 -0
  78. package/tools/transcript_digest.py +380 -0
  79. package/tools/verify_activity_filter.py +437 -0
  80. package/tools/verify_agent_inspector.py +2 -0
  81. package/tools/verify_cost_panel.py +345 -0
  82. package/tools/verify_dash.py +2 -0
  83. package/tools/verify_dispatch_panel.py +301 -0
  84. package/tools/verify_failure_drilldown.py +188 -0
  85. package/tools/verify_prboard.py +2 -0
  86. package/tools/verify_scorecards.py +281 -0
  87. package/tools/verify_submit_encoding.py +2 -0
  88. package/tools/verify_ui_trio.py +409 -0
  89. package/tools/verify_wave_telemetry.py +268 -0
  90. package/tools/wave_backlog_analyzer.py +490 -0
  91. package/tools/wave_ledger_hook.py +150 -0
  92. package/tools/wave_preflight.py +779 -0
  93. package/tools/wave_resume.py +215 -0
  94. package/tools/wave_templates.py +340 -0
  95. package/ui/agents.py +68 -14
  96. package/ui/collectors.py +81 -55
  97. package/ui/config.py +7 -2
  98. package/ui/cost.py +231 -12
  99. package/ui/handler.py +383 -55
  100. package/ui/quality_scorecard.py +232 -0
  101. package/ui/sse.py +3 -3
  102. package/ui/wave_audit_tail.py +213 -0
  103. package/ui/wave_dispatch.py +280 -0
  104. package/ui/wave_failure.py +288 -0
  105. package/ui/wave_gantt.py +152 -0
  106. package/ui/wave_reasoning_tail.py +176 -0
  107. package/ui/wave_telemetry.py +383 -0
  108. package/ui/web/dist/assets/index-CNQxaiOW.css +1 -0
  109. package/ui/web/dist/assets/index-CP68RIh3.js +9 -0
  110. package/ui/web/dist/index.html +2 -2
  111. package/bin/CLAUDE.md +0 -76
  112. package/daemons/CLAUDE.md +0 -36
  113. package/dash/CLAUDE.md +0 -32
  114. package/docs/archive/README.md +0 -3
  115. package/docs/archive/spikes/tiered-cognition/ACTIVATION.md +0 -125
  116. package/docs/archive/spikes/tiered-cognition/DESIGN.md +0 -287
  117. package/docs/archive/spikes/tiered-cognition/FINDINGS.md +0 -113
  118. package/docs/archive/spikes/tiered-cognition/README.md +0 -27
  119. package/docs/archive/spikes/tiered-cognition/aesop-cognition.example.md +0 -32
  120. package/docs/archive/spikes/tiered-cognition/force-model-policy.merged.mjs +0 -673
  121. package/docs/archive/spikes/tiered-cognition/strip-tools-hook.mjs +0 -434
  122. package/hooks/CLAUDE.md +0 -89
  123. package/mcp/CLAUDE.md +0 -213
  124. package/monitor/CLAUDE.md +0 -40
  125. package/scan/CLAUDE.md +0 -30
  126. package/state_store/CLAUDE.md +0 -39
  127. package/tools/CLAUDE.md +0 -79
  128. package/ui/CLAUDE.md +0 -127
  129. package/ui/web/dist/assets/index-0qQYnvMC.js +0 -9
  130. package/ui/web/dist/assets/index-BdIlFieV.css +0 -1
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: fleet
3
+ description: One-shot fleet snapshot (agents, heartbeats, tracker lanes, orchestrator status).
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Fleet — Snapshot of live fleet state
8
+
9
+ One-line status summary for aesop fleet operations, showing active agents, daemon health, tracker backlog, and orchestrator phase.
10
+
11
+ ## Procedure
12
+
13
+ Run the fleet snapshot tool and parse the JSON output:
14
+
15
+ ```bash
16
+ aesop fleet
17
+ ```
18
+
19
+ ### Output format
20
+
21
+ JSON object with:
22
+
23
+ - **timestamp**: ISO 8601 when snapshot was taken
24
+ - **aesop_root**: Fleet root directory (from AESOP_ROOT env or process.cwd())
25
+ - **heartbeats**: Object with watchdog and monitor pulse status
26
+ - **watchdog**: age_seconds, status (OK/STALE/MISSING), threshold_seconds
27
+ - **monitor**: age_seconds, status (OK/STALE/MISSING), threshold_seconds
28
+ - **agents**: Array of active agents OR unavailable message
29
+ - Each agent: id, project, status, age_s, hint, startedAt, lastActivity, runtimeSeconds, tokensUsed, taskLabel, promptFull
30
+ - **tracker**: Tracker backlog counts by lane OR unavailable message
31
+ - total_items: sum of all items
32
+ - by_lane: object mapping lane names to counts
33
+ - **orchestrator**: Current orchestrator status OR unavailable message
34
+ - activity, phase, timestamp (from state/orchestrator-status.json)
35
+
36
+ ### Graceful degradation
37
+
38
+ All missing state files produce explicit `unavailable: "<reason>"` entries rather than crashing:
39
+
40
+ - watchdog heartbeat missing → `unavailable: "MISSING"`
41
+ - monitor heartbeat missing → `unavailable: "MISSING"`
42
+ - dash-extra.mjs not found → `agents: { unavailable: "dash-extra.mjs not found" }`
43
+ - tracker.json missing/malformed → `tracker: { unavailable: "tracker.json not found or malformed" }`
44
+ - orchestrator-status.json missing → `orchestrator: { unavailable: "orchestrator-status.json not found or malformed" }`
45
+
46
+ ### Example output
47
+
48
+ ```json
49
+ {
50
+ "timestamp": "2024-07-17T14:32:05.123Z",
51
+ "aesop_root": "/path/to/aesop",
52
+ "heartbeats": {
53
+ "watchdog": {
54
+ "age_seconds": 42,
55
+ "status": "OK",
56
+ "threshold_seconds": 300
57
+ },
58
+ "monitor": {
59
+ "age_seconds": 1203,
60
+ "status": "OK",
61
+ "threshold_seconds": 3600
62
+ }
63
+ },
64
+ "agents": {
65
+ "count": 2,
66
+ "agents": [
67
+ {
68
+ "id": "a1b2c3d4e5f6g7h",
69
+ "project": "aesop",
70
+ "status": "running",
71
+ "age_s": 35,
72
+ "hint": "Implement /fleet CLI feature",
73
+ "startedAt": "2024-07-17T14:31:30Z",
74
+ "lastActivity": "2024-07-17T14:32:05Z",
75
+ "runtimeSeconds": 35,
76
+ "tokensUsed": 45000,
77
+ "taskLabel": "FEATURE — fleet skill CLI part",
78
+ "promptFull": "..."
79
+ }
80
+ ]
81
+ },
82
+ "tracker": {
83
+ "total_items": 8,
84
+ "by_lane": {
85
+ "ranked": 3,
86
+ "in-progress": 2,
87
+ "proposed": 3
88
+ }
89
+ },
90
+ "orchestrator": {
91
+ "activity": "dispatching",
92
+ "phase": "wave-14",
93
+ "timestamp": "2024-07-17T14:32:00Z"
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### Integration with orchestrators
99
+
100
+ Orchestrators invoke `aesop fleet` (instead of ad-hoc state reads) to stay coupled to this canonical format. All data is read-only; the command never mutates state.
101
+
102
+ ### Machine-readable parsing
103
+
104
+ Parse the JSON and check for `unavailable` fields at each level:
105
+
106
+ ```javascript
107
+ const fleet = JSON.parse(output);
108
+ if (fleet.heartbeats.watchdog.unavailable) {
109
+ console.log('Watchdog is', fleet.heartbeats.watchdog.unavailable);
110
+ } else {
111
+ console.log('Watchdog is', fleet.heartbeats.watchdog.status, 'age', fleet.heartbeats.watchdog.age_seconds + 's');
112
+ }
113
+ ```
@@ -1,161 +1,276 @@
1
1
  ---
2
2
  name: power
3
- description: Prime the "filesystem brain" load all operating rules, behaviors, the multi-agent dispatch model, memory, and the current project's semantics from disk. Report a compact primed brief with system health and next steps.
4
- version: 1.0.0
3
+ description: Initialize Aesop into ANY repo (first-run) or prime the orchestration brain (already-primed). Loads rules, memory, and project state; writes CLAUDE.md layer for unprimed repos.
4
+ version: 2.0.0
5
5
  ---
6
6
 
7
- # Power — prime the filesystem brain
7
+ # Power — Initialize or Prime the Orchestration Brain
8
8
 
9
- Everything that governs how I should behave lives on disk. This skill reads it, internalizes it,
10
- and reports a compact "primed" summary **without inflating context** (code is read via subagents
11
- that return briefs, not dumped raw).
9
+ This skill makes Aesop work in ANY repository either initializing a new codebase for the first time (init-prime)
10
+ or loading an already-primed project's rules and state.
12
11
 
13
- The principle: **all rules can be abstracted to consistent agent behavior.** Rules are files;
14
- reading them = becoming the agent the files describe.
12
+ **Core principle**: All orchestration rules live on disk as readable `.md` files. This skill reads them, internalizes them,
13
+ and reports a compact "primed" summary — enabling autonomous dispatch under the loaded rules.
15
14
 
16
- **Scope: this is a priming skill for aesop orchestration itself** rules, dispatch model, memory,
17
- machinery health. Running it does NOT imply any particular project is being worked on. Project
18
- state loads only when the session is actually resuming that project (step 3); deferred projects
19
- are rolled up to counts, never item-by-item — and their repos/daemons are left untouched.
15
+ **Prerequisites**: Before running `/power`, the Aesop harness must be installed in the repository. Use the CLI to scaffold:
16
+ ```bash
17
+ # Scaffold the Aesop harness (daemons, skills, tools, hooks) into your repo
18
+ npx @matt82198/aesop . --name "my-project"
19
+ # or run `npx @matt82198/aesop --help` for more options
20
+ ```
21
+ The CLI automatically installs git hooks, copies all necessary directories, and generates initial config files. Then run `/power` to prime the orchestration brain.
20
22
 
21
- ## Procedure (run in order)
23
+ ---
24
+
25
+ ## Quick Start: Two Paths
26
+
27
+ ### Path 1: First Time? (Init-Prime)
28
+ If the target repository has **no CLAUDE.md layer** (i.e., no project-root `CLAUDE.md`, no domain `CLAUDE.md` units):
29
+
30
+ 1. Run `/power` in the repository root (after CLI setup above)
31
+ 2. Aesop will **automatically**:
32
+ - Fan out read-only Haiku explorers to scan the codebase (briefs only, never raw dumps)
33
+ - Write a project-root `CLAUDE.md` (purpose, build/run commands, gotchas)
34
+ - Write smallest self-contained **domain `CLAUDE.md` units** (module layout, key contracts, invariants)
35
+ - Write a `STATE.md` checkpoint seed if warranted
36
+ - Commit + push the new `.md` layer (gated by secret-scan)
37
+ 3. Every future `/power` run then takes the **fast path** (already-primed).
38
+
39
+ ### Path 2: Already Primed?
40
+ If the repository **already has CLAUDE.md layer** (project-root + domain CLAUDE.md files ± STATE.md):
41
+
42
+ 1. Run `/power` in the repository root
43
+ 2. Aesop will **load the persisted semantic memory** from disk (no re-investigation needed)
44
+ 3. Report a compact primed brief with health checks and next steps
45
+ 4. Proceed under the loaded dispatch model
46
+
47
+ ---
48
+
49
+ ## Procedure (in order)
50
+
51
+ ### 1. Detect priming status
52
+
53
+ **Check if this codebase has been primed before:**
54
+ - Does a project-root `CLAUDE.md` exist in the target repository root?
55
+ - Do any domain-scoped `CLAUDE.md` files exist (e.g., `src/CLAUDE.md`, `api/CLAUDE.md`)?
56
+ - Does `STATE.md` or similar checkpoint exist?
57
+
58
+ **If YES** → This codebase is **already primed**. Jump to **Step 2: Already-Primed Fast Path**.
59
+
60
+ **If NO** → This codebase is **unprimed**. Jump to **Step 2: Init-Prime Path**.
61
+
62
+ ---
63
+
64
+ ### 2A. Already-Primed Fast Path
22
65
 
23
- ### 1. Load global rules + dispatch model
66
+ For an already-primed codebase:
67
+
68
+ #### 2A.1 Load global rules + dispatch model
24
69
  Read these directly (they are small and authoritative):
25
- - `~/.claude/CLAUDE.md` — the **cardinal rules** (autonomous continuation, dispatch model,
26
- durable checkpointing, build env). These override defaults.
27
- - Any project-local `CLAUDE.md` in the current working directory and its parents.
28
-
29
- Then run the **brain integrity check**: `~/.claude` should be a git repo (if you version it).
30
- Check `git -C ~/.claude status --porcelain` and ahead/behind vs origin:
31
- - Deliberate uncommitted rule/hook/skill/memory changes → commit + push now (gated by
32
- `python <project-root>/tools/secret_scan.py --staged`);
33
- - **Unexpected** modifications or deletionsreport as alert stop, diff against remote, restore
34
- before continuing (core defense: don't let tooling shadow your rules).
35
-
36
- Also check for unactioned entries in `~/.claude/PROPOSALS.md` or similar and surface a
37
- one-line summary of each in the primed report.
38
-
39
- Internalize the **dispatch model** and apply it for the rest of the session. Document
40
- the dispatch model's key invariants locally (in your private repo's `CLAUDE.md`,
41
- if you version `~/.claude`), for example:
42
- - **Subagents model** (Haiku vs specialists vs orchestrator tier)
43
- - **Parallelism defaults** (e.g., fan out 6–8 Haikus per domain)
44
- - **TDD-first discipline** and checkpointing (STATE.md, BUILDLOG.md)
45
- - **Observable machinery**: every action logged, every cost tracked
46
-
47
- ### 2. Load memory
48
- - Read `~/.claude/memory/MEMORY.md` (the index, or use your configured path).
49
- - For any memory entry whose one-line hook is relevant to the current task/project,
50
- read that memory file.
51
- - Treat recalled memories as background context that reflects when they were written —
52
- **verify any file/flag/path they name still exists before relying on it.**
53
-
54
- ### 3. Load the active project's state
55
- If working inside (or resuming) a known project, read its durable checkpoint first:
56
- - Look for a `STATE.md` and `BUILDLOG.md` in the project root or a handoff folder.
57
- - `STATE.md` is the source of truth for intent, locked decisions, contracts, and **NEXT STEPS**.
58
- - Re-sync from disk (git log, file state) — never trust stale assumptions.
59
-
60
- ### 4. Codebase semantics — already-primed vs first-time init
61
- **First check whether this codebase has been primed before**: does it already carry its scoped
62
- `.md` layer (a project-root `CLAUDE.md`, domain `CLAUDE.md` units, a `STATE.md`)?
63
-
64
- - **Already primed** (has `CLAUDE.md`s and optionally `STATE.md`): do NOT re-investigate the code.
65
- The `.md` layer IS the persisted semantic memory — the project `CLAUDE.md`s are sufficient;
66
- the codebase is simply **ready to work**. Only re-scan a specific domain if its `CLAUDE.md`
67
- is contradicted by on-disk reality (then fix the `.md`, which is the bug).
68
- - **First time / not primed** (no scoped `.md`s found): run **init-prime** — fan out Haiku
69
- subagents to read the whole codebase (briefs only, never raw dumps) and **autonomously write
70
- the scoped `.md` layer it needs**:
71
- - a project-root `CLAUDE.md`: purpose, build/run commands + toolchain, top gotchas
72
- - smallest self-contained **domain `CLAUDE.md` units** (minimal, never lossy) — module layout,
73
- key contracts, domain logic, non-obvious invariants
74
- - a `STATE.md` checkpoint seed if the work warrants it
75
-
76
- Commit + push the new `.md`s (secret-scan gated). Every future `/power` on that codebase
77
- then takes the already-primed fast path.
70
+ - `~/.claude/CLAUDE.md` — the **cardinal rules** (autonomous continuation, dispatch model, durable checkpointing, build env)
71
+ - Project-root `CLAUDE.md` in the current working directory
72
+
73
+ Then run the **brain integrity check**: if you version `~/.claude` as a git repo, check:
74
+ ```bash
75
+ git -C ~/.claude status --porcelain
76
+ ```
77
+ and ahead/behind vs origin. If there are:
78
+ - **Deliberate uncommitted rule/hook/skill/memory changes**commit + push now (gated by `python <project-root>/tools/secret_scan.py --staged`)
79
+ - **Unexpected** modifications or deletions report as alert, stop, diff against remote, restore before continuing
80
+
81
+ **Internalize the dispatch model** document its key invariants locally in your session context (do not modify `.md` files during this step).
78
82
 
79
- ### 5. Report "primed" (compact)
83
+ #### 2A.2 Load memory
84
+ - Read `~/.claude/MEMORY.md` (index) and cross-reference any entries relevant to the active project or session task
85
+ - For each recalled memory entry, read its full file
86
+ - **Verify any file/flag/path they name still exists before relying on it**
87
+
88
+ #### 2A.3 Load the active project's state
89
+ - Look for `STATE.md` or `BUILDLOG.md` in the project root
90
+ - `STATE.md` is the source of truth for intent, locked decisions, contracts, and **NEXT STEPS**
91
+ - Re-sync from disk (`git log`, file state) — never trust stale assumptions
92
+
93
+ #### 2A.4 Report "primed" (compact)
80
94
  Output a short brief, not a wall of text:
81
95
 
82
- - **Drain the UI inbox first** (if configured in `aesop.config.json`): run
83
- `python <project-root>/tools/inbox_drain.py pending`. If items exist, surface each under
84
- a **"QUEUED FROM DASHBOARD"** heading as `[ISO-ts] text` one-liners. Mark them processed
85
- (`python <project-root>/tools/inbox_drain.py mark-all`) after actioning.
86
- - **Health line first**: run `python <project-root>/tools/power_selftest.py` and lead with its
87
- `POWER-SELFTEST:` line (hooks, brain push state, heartbeats, decisions, scanner state).
88
- Bullet anything non-OK.
89
- - **Open decisions**: surface each ACTIVE line of any decisions/proposals file and any
90
- unactioned inbox entries as one-liners (rolled-up counts for deferred items, never item-by-item).
91
- - **Rules loaded**: dispatch model + any project-specific overrides in one or two lines.
92
- - **Project**: name, purpose, status, repo, key paths.
93
- - **Semantics**: the domain model + main flow in a few bullets.
94
- - **Gotchas**: the verified ones that change how I act.
95
- - **Next steps**: from `STATE.md`, ready to execute (autonomously, per the dispatch model).
96
-
97
- ### 6. Project app launch (if applicable)
96
+ - **Drain the UI inbox first** (if configured in `aesop.config.json`): run `python <project-root>/tools/inbox_drain.py pending` and surface each item as a one-liner under a **"QUEUED FROM DASHBOARD"** heading
97
+ - **Health line first**: run `python <project-root>/tools/power_selftest.py` and lead with its `POWER-SELFTEST:` line (hooks, brain push state, heartbeats, decisions, scanner state). Bullet anything non-OK.
98
+ - **Open decisions**: surface each ACTIVE line from any decisions/proposals file and unactioned inbox entries as one-liners
99
+ - **Rules loaded**: dispatch model + any project-specific overrides in one or two lines
100
+ - **Project**: name, purpose, status, repo, key paths
101
+ - **Semantics**: the domain model + main flow in a few bullets
102
+ - **Gotchas**: the verified ones that change how you act
103
+ - **Next steps**: from `STATE.md`, ready to execute (autonomously, per the dispatch model)
104
+
105
+ #### 2A.5 Project app launch (if applicable)
98
106
  After reporting primed, bring the active project's runnable app up in the BACKGROUND if it has one.
99
- Make it **idempotent**: if the port is already serving a healthy instance, skip the start and
100
- just report the URL.
101
-
102
- **Pattern** (project adopters fill in their own):
103
- - Define a `run` skill or script in your project (or use an existing `start`/`run` command).
104
- - Detect if the service is already healthy (e.g., `curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/`).
105
- - If healthy (200), skip startup and just report the URL.
106
- - If not healthy, start the service in the background via Bash with `run_in_background: true`.
107
- - Poll readiness WITHOUT blocking sleep: use curl retries or a cheap probe loop.
108
- - Report the URL + background task ID so it can be stopped on request.
109
-
110
- **DO NOT** hardcode personal app paths (e.g., Spring Boot repos, IDEs, personal tools).
111
- Instead, document in your project's `CLAUDE.md` how to launch the app, and reference that
112
- in your local version of this skill or a project-specific override.
113
-
114
- ### 6b. Machinery health — orchestration monitor (optional)
107
+ Make it **idempotent**: if the port is already serving a healthy instance, skip the start and just report the URL.
108
+
109
+ **Pattern** (documented in your project's `CLAUDE.md`):
110
+ - Detect if the service is already healthy (e.g., `curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/`)
111
+ - If healthy (200), skip startup and just report the URL
112
+ - If not healthy, start the service in the background via Bash with `run_in_background: true`
113
+ - Poll readiness WITHOUT blocking sleep: use curl retries or a cheap probe loop
114
+ - Report the URL + background task ID so it can be stopped on request
115
+
116
+ **DO NOT** hardcode paths. Instead, document in the project's `CLAUDE.md` how to launch the app and reference that in this skill or a project-specific override.
117
+
118
+ #### 2A.6 Machinery health (optional)
115
119
  If your project uses an orchestration monitor, spin up a **background Haiku agent** that:
116
120
  1. Collects health signals (cheap, deterministic)
117
- 2. Reads only that brief and acts per a charter — AUTO-applies safe refinements and stages
118
- rule/behavior changes to a `PROPOSALS.md` for review
121
+ 2. Reads the brief and acts per a charter — AUTO-applies safe refinements and stages rule/behavior changes to `PROPOSALS.md` for review
119
122
  3. Self-paces via ScheduleWakeup and beats a heartbeat
120
123
 
121
124
  Skip if `.monitor-heartbeat` is already <300s old. Announce its task id so it can be paused/stopped.
122
125
 
123
- ### 7. Standing dev loops (optional; only when actively developing)
124
- If the session is doing active development (writing/changing code — not just answering questions),
125
- keep these background loops running continuously. They are the "tail": cheap, persistent,
126
- append-only, and never inflate the main context.
126
+ #### 2A.7 Standing dev loops (optional; only during active development)
127
+ If the session is actively developing (writing/changing code — not just answering questions),
128
+ keep these background loops running continuously. They are the "tail": cheap, persistent, append-only.
127
129
 
128
- **Patterns** (project adopters fill in their own):
130
+ **Patterns** (documented in your project's `CLAUDE.md`):
129
131
 
130
- 1. **Build/status + backup watchdog** (background bash loop) — appends timestamped git/test/build
131
- snapshots. For example, if your project has a `daemons/` folder with a `run-watchdog.sh`,
132
- launch that via Bash with `run_in_background: true` and let it beat a heartbeat so you know
133
- it is live. Stop with TaskStop when development pauses.
132
+ 1. **Build/status + backup watchdog** (background bash loop) — appends timestamped git/test/build snapshots. For example, if your project has a `daemons/` folder with a `run-watchdog.sh`, launch that via Bash with `run_in_background: true` and let it beat a heartbeat
133
+ 2. **Memory keeper** (Haiku, background) as new decisions, contracts, or gotchas emerge, updates relevant memory files compactly
134
+ 3. **Continuous QA loop** after each feature reaches a checkpoint: Haiku reviews → Haiku bugfixes → Haiku lint/format, looping until build is green and review is clean, with Opus orchestrator final-catch before merge
135
+ 4. **Tail-drift alignment** (Haiku, background) periodically checks in-flight agents' output against their domain `CLAUDE.md` contract, realigns if drifting
136
+ 5. **Hung-Haiku watchdog** (orchestrator's main thread, NOT a subagent) — keep watching every spawned Haiku for stalls; on hang: TaskStop it and relaunch from checkpoint
134
137
 
135
- 2. **Memory keeper** (Haiku, background) as new decisions, contracts, or gotchas emerge,
136
- updates the relevant memory files compactly (one fact per memory; never bloat). Re-invoke
137
- via SendMessage to keep context.
138
+ Launch any that aren't already running; **stop them with TaskStop when development pauses**.
138
139
 
139
- 3. **Continuous QA loop** — after each feature reaches a checkpoint: **Haiku reviews →
140
- Haiku bugfixes → Haiku lint/format**, looping until the build is green and review is clean,
141
- with **Opus orchestrator final-catch** before merge. TDD-first: failing tests before code.
140
+ ---
142
141
 
143
- 4. **Tail-drift alignment** (Haiku, background) — periodically checks in-flight agents' output
144
- against their domain `CLAUDE.md` contract, and realigns anything drifting.
142
+ ### 2B. Init-Prime Path
145
143
 
146
- 5. **Hung-Haiku watchdog** (orchestrator's main thread, NOT a subagent) keep watching every
147
- spawned Haiku for stalls. On hang: TaskStop it and relaunch from checkpoint or respawn.
144
+ For an unprimed codebase, run the following steps **in order**:
148
145
 
149
- Launch any that aren't already running; **stop them with TaskStop when development pauses**.
146
+ #### 2B.1 Scan the target repository
147
+
148
+ Fan out read-only Haiku subagents to explore the codebase structure:
149
+ - **Haiku A** (30s): Repository structure, build system, entry points, main languages
150
+ - **Haiku B** (30s): Key libraries, frameworks, external dependencies
151
+ - **Haiku C** (30s): Testing strategy, CI/CD configuration, deployment targets
152
+ - **Haiku D** (30s): Known gotchas, non-obvious invariants, anti-patterns in the codebase
153
+
154
+ Each Haiku returns a **1-page brief** (never raw code dumps). Collect all four briefs.
150
155
 
151
- Mechanism: use background `Agent` (run_in_background) for keeper/drift loops, `Monitor` for
152
- the watchdog, and self-pace longer cycles with `/loop` skill or `ScheduleWakeup`.
153
- Always announce which loops were started (and their task IDs) so they can be paused/stopped.
156
+ #### 2B.2 Synthesize project-root CLAUDE.md
157
+
158
+ Using the four exploration briefs, **write a project-root `CLAUDE.md`** in the target repository.
159
+ Include:
160
+
161
+ - **What**: 1-line project purpose + primary domain/stack
162
+ - **Key commands**: build, test, run, deploy (exactly as documented in the project)
163
+ - **Gotchas**: verified non-obvious facts that affect how you work
164
+ - "Tests must run in isolation (no shared DB state)"
165
+ - "Deploy requires manual approval in Slack"
166
+ - "Docker build takes 5 minutes; cache aggressively"
167
+ - **Domain map**: 2-4 self-contained scopes, each mapping to one potential Haiku (keep it minimal; don't over-partition)
168
+ - **See Also**: pointers to ARCHITECTURE.md, CONTRIBUTING.md, etc. if they exist in the repo
169
+
170
+ **Keep the project-root CLAUDE.md small** (under 50 lines). It's a navigation map, not a textbook.
171
+
172
+ #### 2B.3 Synthesize domain CLAUDE.md units
173
+
174
+ For each domain identified in 2B.2, **write a scoped domain `CLAUDE.md`** in that module's root directory.
175
+
176
+ **Example structure**:
177
+ ```
178
+ my-repo/
179
+ CLAUDE.md <- project-root (what, commands, gotchas, domain map)
180
+ src/
181
+ CLAUDE.md <- domain 1 (API layer: contracts, invariants, entry points)
182
+ api/
183
+ ...
184
+ core/
185
+ CLAUDE.md <- domain 2 (core logic: key abstractions, state machine)
186
+ ...
187
+ tests/
188
+ CLAUDE.md <- domain 3 (test infrastructure: harness, fixtures)
189
+ ...
190
+ ```
191
+
192
+ **Each domain CLAUDE.md includes**:
193
+ - **Module layout**: directory tree, what lives where
194
+ - **Key contracts**: public interfaces, APIs, data model
195
+ - **Domain logic**: state machine, algorithm, non-obvious invariants
196
+ - **Dependencies**: external libraries, internal cross-domain calls
197
+ - **Anti-patterns**: what NOT to do in this domain
198
+ - **Testing**: how to run tests, fixtures, mocks
199
+
200
+ **Keep each domain CLAUDE.md focused and minimal** (10-20 lines; longer ones should split further).
201
+
202
+ #### 2B.4 Write STATE.md checkpoint seed
203
+
204
+ Create a `STATE.md` in the repository root. This is the **durable checkpoint** that survives wipes and outages:
205
+
206
+ ```markdown
207
+ # {{PROJECT_NAME}} — State & Next Steps
208
+
209
+ **Phase**: init-prime (first CLAUDE.md layer written)
210
+
211
+ **Intent**: {{PROJECT_NAME}} is now initialized with aesop orchestration semantics.
212
+ All domain rules live in scoped CLAUDE.md files. Ready to dispatch first work.
213
+
214
+ **Locked decisions**:
215
+ - (none yet; updated as decisions are made)
216
+
217
+ **NEXT STEPS**:
218
+ - [ ] Review + approve the written CLAUDE.md layer
219
+ - [ ] Run `/buildsystem` to execute one example orchestration wave
220
+ - [ ] Iterate domain CLAUDE.md files as you discover invariants
221
+ ```
222
+
223
+ #### 2B.5 Commit + Push
224
+
225
+ Stage all newly written files:
226
+ ```bash
227
+ git add CLAUDE.md STATE.md src/CLAUDE.md core/CLAUDE.md tests/CLAUDE.md
228
+ ```
229
+
230
+ Commit with a clear message:
231
+ ```bash
232
+ git commit -m "init: initialize aesop orchestration semantics — project CLAUDE.md + domain units + STATE.md seed
233
+
234
+ Aesop init-prime: generated project-root CLAUDE.md (purpose, commands, gotchas),
235
+ minimal domain CLAUDE.md units (contracts, invariants), and STATE.md checkpoint seed.
236
+
237
+ Co-Authored-By: Aesop Orchestrator <aesop@example.com>"
238
+ ```
239
+
240
+ Run the **secret-scan gate**:
241
+ ```bash
242
+ python <project-root>/tools/secret_scan.py --staged
243
+ ```
244
+
245
+ **If gate fails** → fix the leaky files and re-commit (never override the gate).
246
+
247
+ **If gate passes** → push to the feature branch:
248
+ ```bash
249
+ git push origin feat/aesop-init-prime
250
+ ```
251
+
252
+ (Or to `main` if you have override permissions, but best practice: feature branch → PR → review → merge.)
253
+
254
+ #### 2B.6 Report init completion
255
+
256
+ After a successful push, report:
257
+ ```
258
+ ✓ Aesop init-prime complete
259
+ - Project-root CLAUDE.md written (60 lines; purpose, commands, gotchas, 3 domains)
260
+ - Domain CLAUDE.md units: src/, core/, tests/
261
+ - STATE.md checkpoint seed created
262
+ - Pushed to origin feat/aesop-init-prime (gated by secret-scan, all clear)
263
+
264
+ NEXT: Review the generated `.md` layer, iterate on domains, then run /buildsystem for your first orchestration wave.
265
+ ```
266
+
267
+ ---
154
268
 
155
269
  ## Notes
156
- - If no project context exists yet, steps 1–2 still apply; skip 3–4 and say so.
157
- - Steps 1–5 always run on `/power`; **step 6 (launch app) runs only if the active project has one**;
158
- **step 6b (monitor) runs only if configured**; step 7 (dev loops) runs only during active development.
159
- - Prefer subagents for anything large; the goal is a *brain*, not a *dump*.
160
- - After priming, proceed under the dispatch model (continue approved work autonomously; stop only
161
- for genuine decisions or unauthorized outward/destructive actions).
270
+
271
+ - **If no project context exists yet** (e.g., you're answering a general question about rules): Steps 1 loads global rules; skip 2 and say so.
272
+ - **Steps 1-5 always run on `/power`**; **step 6 (launch app) runs only if the active project has one**; **step 6b (monitor) runs only if configured**; **step 7 (dev loops) runs only during active development**.
273
+ - **Init-prime** writes `.md` files to the target repository (never personal/system paths). All state lives under the target repo root or configured `state_root` (see `aesop.config.json`).
274
+ - **Prefer subagents for anything large**; the goal is a *brain*, not a *dump*.
275
+ - **After priming** (either path), proceed under the loaded dispatch model (continue approved work autonomously; stop only for genuine decisions or unauthorized outward/destructive actions).
276
+ - **Portability**: all file paths are relative to the target repository or configurable via environment variables / `aesop.config.json` (never hardcoded personal paths). Use `~` notation for home-directory paths (e.g., `~/.claude`, `~/scripts`) for cross-platform compatibility (Windows, macOS, Linux). Config loaders automatically expand `~` at runtime.
@@ -12,13 +12,15 @@ in state_store/CLAUDE.md.
12
12
  from .api import StateAPI
13
13
  from .export import export_tracker
14
14
  from .ingest import ingest_tracker_json
15
- from .projections import project_tracker
16
- from .store import EventStore
15
+ from .projections import project_tracker, project_agent_lifecycle
16
+ from .store import EventStore, ConcurrencyConflict
17
17
 
18
18
  __all__ = [
19
19
  "EventStore",
20
20
  "StateAPI",
21
+ "ConcurrencyConflict",
21
22
  "project_tracker",
23
+ "project_agent_lifecycle",
22
24
  "export_tracker",
23
25
  "ingest_tracker_json",
24
26
  ]
@@ -7,7 +7,7 @@ stream and folds it through the registered projector.
7
7
  from __future__ import annotations
8
8
 
9
9
  from .projections import project_tracker
10
- from .store import EventStore
10
+ from .store import EventStore, ConcurrencyConflict
11
11
 
12
12
  _PROJECTORS = {"tracker": project_tracker}
13
13
 
@@ -18,9 +18,24 @@ class StateAPI:
18
18
  def __init__(self, db_path: str):
19
19
  self._store = EventStore(db_path)
20
20
 
21
- def append(self, stream: str, event_type: str, payload: dict, actor: str = "system") -> int:
22
- """Append one event; return its new per-stream version."""
23
- return self._store.append(stream, event_type, payload, actor)
21
+ def append(
22
+ self,
23
+ stream: str,
24
+ event_type: str,
25
+ payload: dict,
26
+ actor: str = "system",
27
+ expected_version: int | None = None,
28
+ ) -> int:
29
+ """Append one event; return its new per-stream version.
30
+
31
+ Supports optimistic concurrency control via expected_version. See
32
+ EventStore.append for full semantics.
33
+
34
+ Raises:
35
+ ConcurrencyConflict: If expected_version is provided and does not
36
+ match the stream's current max version.
37
+ """
38
+ return self._store.append(stream, event_type, payload, actor, expected_version)
24
39
 
25
40
  def get(self, stream: str) -> list:
26
41
  """Return all events in ``stream`` ascending by version."""