@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.
- package/CHANGELOG.md +117 -2
- package/README.md +59 -218
- package/bin/cli.js +168 -41
- package/daemons/run-watchdog.sh +16 -4
- package/daemons/selfheal.sh +231 -0
- package/docs/ANY-REPO.md +427 -0
- package/docs/CONTRIBUTING.md +72 -0
- package/docs/DEMO.md +334 -0
- package/docs/HOOK-INSTALL.md +15 -56
- package/docs/INSTALL.md +74 -4
- package/docs/PORTING.md +166 -0
- package/docs/README.md +33 -3
- package/docs/TEAM-STATE.md +372 -0
- package/docs/reproduce.md +33 -3
- package/driver/CLAUDE.md +150 -0
- package/driver/README.md +383 -0
- package/driver/aesop.config.example.json +80 -0
- package/driver/agent_driver.py +355 -0
- package/driver/backend_config.py +253 -0
- package/driver/claude_code_driver.py +198 -0
- package/driver/codex_driver.py +673 -0
- package/driver/openai_compatible_driver.py +249 -0
- package/driver/openai_transport.py +179 -0
- package/driver/verification_policy.py +75 -0
- package/driver/wave_bridge.py +254 -0
- package/driver/wave_loop.py +1408 -0
- package/driver/wave_scheduler.py +890 -0
- package/hooks/pre-push-policy.sh +131 -33
- package/mcp/server.mjs +320 -4
- package/monitor/collect-signals.mjs +69 -0
- package/package.json +22 -14
- package/skills/CLAUDE.md +132 -2
- package/skills/buildsystem/SKILL.md +330 -0
- package/skills/buildsystem/wave-flat-dispatch.template.mjs +658 -0
- package/skills/fleet/SKILL.md +113 -0
- package/skills/power/SKILL.md +246 -131
- package/state_store/__init__.py +4 -2
- package/state_store/api.py +19 -4
- package/state_store/coordination.py +209 -0
- package/state_store/identity.py +51 -0
- package/state_store/projections.py +63 -0
- package/state_store/read_api.py +156 -0
- package/state_store/store.py +185 -73
- package/state_store/write_api.py +462 -0
- package/templates/wave-presets/data.json +65 -0
- package/templates/wave-presets/library.json +65 -0
- package/templates/wave-presets/saas.json +64 -0
- package/tools/audit_report.py +388 -0
- package/tools/bench_runner.py +100 -3
- package/tools/ci_merge_wait.py +256 -35
- package/tools/ci_workflow_lint.py +430 -0
- package/tools/claudemd_drift.py +394 -0
- package/tools/claudemd_lint.py +359 -0
- package/tools/common.py +39 -3
- package/tools/cost_ceiling.py +166 -43
- package/tools/cost_econ.py +480 -0
- package/tools/cost_projection.py +559 -0
- package/tools/crossos_drift.py +394 -0
- package/tools/defect_escape.py +252 -0
- package/tools/doctor.js +1 -1
- package/tools/eod_sweep.py +188 -26
- package/tools/fleet.js +260 -0
- package/tools/fleet_ledger.py +209 -7
- package/tools/git_identity_check.py +315 -0
- package/tools/health-score.js +40 -0
- package/tools/health_score.py +361 -0
- package/tools/metrics_gate.py +13 -4
- package/tools/mutation_test.py +523 -0
- package/tools/portability_check.py +206 -0
- package/tools/proposals.mjs +47 -2
- package/tools/reconcile.py +7 -4
- package/tools/reproduce.js +405 -0
- package/tools/secret_scan.py +207 -65
- package/tools/self_stats.py +20 -0
- package/tools/stall_check.py +247 -16
- package/tools/stateapi_lint.py +325 -0
- package/tools/test_battery.py +173 -0
- package/tools/transcript_digest.py +380 -0
- package/tools/verify_activity_filter.py +437 -0
- package/tools/verify_agent_inspector.py +2 -0
- package/tools/verify_cost_panel.py +345 -0
- package/tools/verify_dash.py +2 -0
- package/tools/verify_dispatch_panel.py +301 -0
- package/tools/verify_failure_drilldown.py +188 -0
- package/tools/verify_prboard.py +2 -0
- package/tools/verify_scorecards.py +281 -0
- package/tools/verify_submit_encoding.py +2 -0
- package/tools/verify_ui_trio.py +409 -0
- package/tools/verify_wave_telemetry.py +268 -0
- package/tools/wave_backlog_analyzer.py +490 -0
- package/tools/wave_ledger_hook.py +150 -0
- package/tools/wave_preflight.py +779 -0
- package/tools/wave_resume.py +215 -0
- package/tools/wave_templates.py +340 -0
- package/ui/agents.py +68 -14
- package/ui/collectors.py +81 -55
- package/ui/config.py +7 -2
- package/ui/cost.py +231 -12
- package/ui/handler.py +383 -55
- package/ui/quality_scorecard.py +232 -0
- package/ui/sse.py +3 -3
- package/ui/wave_audit_tail.py +213 -0
- package/ui/wave_dispatch.py +280 -0
- package/ui/wave_failure.py +288 -0
- package/ui/wave_gantt.py +152 -0
- package/ui/wave_reasoning_tail.py +176 -0
- package/ui/wave_telemetry.py +383 -0
- package/ui/web/dist/assets/index-CNQxaiOW.css +1 -0
- package/ui/web/dist/assets/index-CP68RIh3.js +9 -0
- package/ui/web/dist/index.html +2 -2
- package/bin/CLAUDE.md +0 -76
- package/daemons/CLAUDE.md +0 -36
- package/dash/CLAUDE.md +0 -32
- package/docs/archive/README.md +0 -3
- package/docs/archive/spikes/tiered-cognition/ACTIVATION.md +0 -125
- package/docs/archive/spikes/tiered-cognition/DESIGN.md +0 -287
- package/docs/archive/spikes/tiered-cognition/FINDINGS.md +0 -113
- package/docs/archive/spikes/tiered-cognition/README.md +0 -27
- package/docs/archive/spikes/tiered-cognition/aesop-cognition.example.md +0 -32
- package/docs/archive/spikes/tiered-cognition/force-model-policy.merged.mjs +0 -673
- package/docs/archive/spikes/tiered-cognition/strip-tools-hook.mjs +0 -434
- package/hooks/CLAUDE.md +0 -89
- package/mcp/CLAUDE.md +0 -213
- package/monitor/CLAUDE.md +0 -40
- package/scan/CLAUDE.md +0 -30
- package/state_store/CLAUDE.md +0 -39
- package/tools/CLAUDE.md +0 -79
- package/ui/CLAUDE.md +0 -127
- package/ui/web/dist/assets/index-0qQYnvMC.js +0 -9
- 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
|
+
```
|
package/skills/power/SKILL.md
CHANGED
|
@@ -1,161 +1,276 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: power
|
|
3
|
-
description:
|
|
4
|
-
version:
|
|
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 —
|
|
7
|
+
# Power — Initialize or Prime the Orchestration Brain
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- **
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- **
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- **
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
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
|
-
|
|
124
|
-
If the session is
|
|
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** (
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
-
against their domain `CLAUDE.md` contract, and realigns anything drifting.
|
|
142
|
+
### 2B. Init-Prime Path
|
|
145
143
|
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
|
|
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.
|
package/state_store/__init__.py
CHANGED
|
@@ -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
|
]
|
package/state_store/api.py
CHANGED
|
@@ -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(
|
|
22
|
-
|
|
23
|
-
|
|
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."""
|