@matt82198/aesop 0.1.0-beta.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 +126 -0
- package/LICENSE +21 -0
- package/README.md +333 -0
- package/aesop.config.example.json +47 -0
- package/bin/cli.js +112 -0
- package/daemons/backup-fleet.sh +173 -0
- package/daemons/run-watchdog.sh +37 -0
- package/dash/dash-extra.mjs +184 -0
- package/dash/watchdog-gui.sh +141 -0
- package/docs/CARDINAL-RULES.md +146 -0
- package/docs/DISPATCH-MODEL.md +180 -0
- package/docs/PUBLISHING.md +150 -0
- package/docs/av-resilience.md +246 -0
- package/monitor/CHARTER.md +66 -0
- package/monitor/collect-signals.mjs +137 -0
- package/package.json +50 -0
- package/tools/launch_tui.py +245 -0
- package/tools/secret_scan.py +363 -0
- package/ui/README.md +141 -0
- package/ui/serve.py +806 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Aesop Dispatch Model — Cost & Orchestration Patterns
|
|
2
|
+
|
|
3
|
+
## The fable conceit
|
|
4
|
+
|
|
5
|
+
In Aesop's Fables, Aesop (the narrator) directs the characters (tortoise, hare, fox, etc.) through moral tales. Here, **Aesop (the orchestrator)** directs a fleet of **Fables (the subagents)** toward reliable task completion.
|
|
6
|
+
|
|
7
|
+
The tortoise represents deliberate, resourceful thinking; the hare, quick speed. Together (slow orchestrator + fast subagents), they're unbeatable.
|
|
8
|
+
|
|
9
|
+
## Cost model
|
|
10
|
+
|
|
11
|
+
### Baseline: all-Opus fleet
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
5 tasks × 10 min each × Opus cost (~60 tokens/min for reasoning)
|
|
15
|
+
= 5 × 10 × 60 = 3,000 tokens/session
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Aesop dispatch: Opus + 5 Haiku
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
1 orchestrator (Opus, 5 min thinking) = 5 × 60 = 300 tokens
|
|
22
|
+
5 subagents (Haiku, 2 min each) = 5 × 2 × 20 = 200 tokens
|
|
23
|
+
Total = 500 tokens/session
|
|
24
|
+
= **83% savings** vs all-Opus
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Scaling to 10 parallel domains
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
1 orchestrator (Opus, 8 min) = 480 tokens
|
|
31
|
+
10 subagents (Haiku, 3 min each) = 600 tokens
|
|
32
|
+
Total = 1,080 tokens
|
|
33
|
+
= **90% savings** vs 10 Opus tasks
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Dispatch patterns
|
|
37
|
+
|
|
38
|
+
### Pattern 1: Fan-out (wide parallelism)
|
|
39
|
+
|
|
40
|
+
**Use case**: 10 independent tasks with no dependencies.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Orchestrator
|
|
44
|
+
├─ Haiku-1: Test domain (write test, red → green)
|
|
45
|
+
├─ Haiku-2: Build domain (compile, package)
|
|
46
|
+
├─ Haiku-3: Docs domain (README, examples)
|
|
47
|
+
├─ Haiku-4: Security domain (audit, review)
|
|
48
|
+
└─ Haiku-5: Deployment domain (stage, verify)
|
|
49
|
+
|
|
50
|
+
Total cost: 1 Opus + 5 Haiku ≈ $0.003 per session
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pattern 2: Sequential with handoff
|
|
54
|
+
|
|
55
|
+
**Use case**: Feature across 3 layers (API → DB → UI) with dependencies.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Orchestrator → Phase 1
|
|
59
|
+
├─ Haiku-1: API spec + tests
|
|
60
|
+
└─ (wait for Haiku-1 to complete)
|
|
61
|
+
→ Phase 2
|
|
62
|
+
├─ Haiku-2: DB schema (reads Haiku-1 output)
|
|
63
|
+
└─ (wait)
|
|
64
|
+
→ Phase 3
|
|
65
|
+
├─ Haiku-3: UI (reads both outputs)
|
|
66
|
+
└─ Haiku-4: Integration test
|
|
67
|
+
|
|
68
|
+
Orchestrator reassembles + QA
|
|
69
|
+
Total cost: still 1 Opus + 4 Haiku = **90% savings**
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Pattern 3: Hierarchical (supervisor + workers)
|
|
73
|
+
|
|
74
|
+
**Use case**: Complex codebase review (split into modules, each reviewed in parallel).
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Orchestrator
|
|
78
|
+
├─ Review-Supervisor (Sonnet, splits codebase into 5 modules)
|
|
79
|
+
│ ├─ Haiku-1: auth module
|
|
80
|
+
│ ├─ Haiku-2: api module
|
|
81
|
+
│ ├─ Haiku-3: db module
|
|
82
|
+
│ ├─ Haiku-4: ui module
|
|
83
|
+
│ └─ Haiku-5: config module
|
|
84
|
+
└─ (Sonnet synthesizes reviews into final report)
|
|
85
|
+
|
|
86
|
+
Cost: 1 Opus + 1 Sonnet + 5 Haiku = ~$0.015 (still 75% savings)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Decision flowchart
|
|
90
|
+
|
|
91
|
+
When you have a new task, ask:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Is this task scoped to <5 min reasoning?
|
|
95
|
+
├─ YES → Use Haiku subagent (cost: ~$0.0002)
|
|
96
|
+
├─ NO → Can I decompose it into smaller tasks?
|
|
97
|
+
│ ├─ YES → Fan-out to multiple Haiku (cost: $0.001–0.003)
|
|
98
|
+
│ └─ NO → Use Opus (cost: ~$0.004)
|
|
99
|
+
└─ Does this need final review/synthesis?
|
|
100
|
+
├─ YES → Opus orchestrator does it (cost: ~$0.002)
|
|
101
|
+
└─ NO → Done (subagent output is final)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Anti-patterns
|
|
105
|
+
|
|
106
|
+
### ❌ All-Opus fleet
|
|
107
|
+
|
|
108
|
+
**Cost**: 10× baseline. **Why**: throws CPU power at problems that are scoped.
|
|
109
|
+
|
|
110
|
+
**Fix**: Decompose into Haiku-sized tasks.
|
|
111
|
+
|
|
112
|
+
### ❌ Giant orchestrator context
|
|
113
|
+
|
|
114
|
+
**Cost**: token waste, slower decisions. **Why**: reads full logs, commits, code diffs.
|
|
115
|
+
|
|
116
|
+
**Fix**: Orchestrator reads STATE.md + BUILDLOG.md + git one-liners only. Delegate research to Haiku.
|
|
117
|
+
|
|
118
|
+
### ❌ Silent hangs
|
|
119
|
+
|
|
120
|
+
**Cost**: invisible waste (subagent stalls, orchestrator waits). **Why**: no heartbeat, no timeout.
|
|
121
|
+
|
|
122
|
+
**Fix**: Heartbeat every 60s min. Watchdog respawns >200s stale. Orchestrator never waits>60s (spawn next task).
|
|
123
|
+
|
|
124
|
+
### ❌ Cloud Python execution
|
|
125
|
+
|
|
126
|
+
**Cost**: latency + complexity. **Why**: cloud runners add overhead, partial failure risk.
|
|
127
|
+
|
|
128
|
+
**Fix**: All Python runs locally. Cloud agents spawn only for distributed compute (rare).
|
|
129
|
+
|
|
130
|
+
## Token spend tracking
|
|
131
|
+
|
|
132
|
+
### Log format (FLEET-LEDGER.md)
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
| timestamp | domain | subagent | tokens | result |
|
|
136
|
+
|--|--|--|--|--|
|
|
137
|
+
| 2024-01-15T10:30:00 | auth | haiku | 1200 | SUCCESS |
|
|
138
|
+
| 2024-01-15T10:35:00 | ui | haiku | 950 | SUCCESS |
|
|
139
|
+
| 2024-01-15T10:40:00 | test | haiku | 1100 | FAILED (1r) |
|
|
140
|
+
| 2024-01-15T10:45:00 | test | haiku | 1050 | SUCCESS |
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Cost signals to watch
|
|
144
|
+
|
|
145
|
+
1. **Spend spike** (+20% baseline): investigate domain; may need larger model.
|
|
146
|
+
2. **Respawn loop** (same domain 3+ times): subagent repeatedly failing; escalate to Sonnet or orchestrator review.
|
|
147
|
+
3. **Serial bottleneck** (orchestrator >500 tokens): parallelism breaking down; split smaller or delegate.
|
|
148
|
+
|
|
149
|
+
### Optimization levers
|
|
150
|
+
|
|
151
|
+
- **Fan-out more tasks**: go from 2 Haiku to 5.
|
|
152
|
+
- **Shift to Sonnet**: if 3 Haiku respawns in a row, use Sonnet once for harder task.
|
|
153
|
+
- **Reduce orchestrator context**: read fewer files, shorter CLAUDE.md.
|
|
154
|
+
- **Cache cardinal rules**: use prompt caching on base CLAUDE.md (reused every session).
|
|
155
|
+
|
|
156
|
+
## Orchestrator role (the slow tortoise)
|
|
157
|
+
|
|
158
|
+
- **Reading**: STATE.md (phase + next steps), BUILDLOG.md (latest 10 entries), git one-liners.
|
|
159
|
+
- **Deciding**: which 3–5 Haiku to spawn, what task each gets, how to reassemble results.
|
|
160
|
+
- **Waiting**: while subagents run in parallel, orchestrator uses time to:
|
|
161
|
+
- Brief user on progress.
|
|
162
|
+
- Spot-check one Haiku output.
|
|
163
|
+
- Plan next phase.
|
|
164
|
+
- **Synthesizing**: reads Haiku results, updates STATE.md, decides next phase or declares done.
|
|
165
|
+
|
|
166
|
+
**Golden rule**: Orchestrator thinks 5–10 min per hour of wallclock time (while agents run). Never blocks; never waits idle.
|
|
167
|
+
|
|
168
|
+
## Subagent role (the swift fables)
|
|
169
|
+
|
|
170
|
+
- **Scoped task**: <5 min pure reasoning per task.
|
|
171
|
+
- **Clear acceptance criteria**: from orchestrator brief.
|
|
172
|
+
- **Output**: append to BUILDLOG.md, return result to orchestrator.
|
|
173
|
+
- **Failure**: log reason, don't retry (orchestrator decides retry).
|
|
174
|
+
- **Speed**: finish in <2 min if possible; alert orchestrator if >5 min (break task smaller).
|
|
175
|
+
|
|
176
|
+
**Golden rule**: Subagents stay cheap and fast. If a Haiku takes >10 min, you've scoped too large.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
**Summary**: Aesop's dispatch model trades a small amount of orchestration complexity for **10× cost savings** and **4× speed** (via parallelism). It works by keeping subagents small, orchestrator lean, and feedback constant.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Publishing Aesop to npm
|
|
2
|
+
|
|
3
|
+
This guide explains how to publish new releases of Aesop to npm using GitHub Actions with OIDC trusted publishing (zero tokens, zero OTP prompts).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The publish workflow (`.github/workflows/publish.yml`) is triggered automatically when you **publish a GitHub Release**. It uses npm's OIDC trusted publishing feature to authenticate with npm — no tokens, no 2FA/passkey prompts, no OTP required.
|
|
8
|
+
|
|
9
|
+
## One-Time Setup on npmjs.com
|
|
10
|
+
|
|
11
|
+
### Step 1: Add a Trusted Publisher
|
|
12
|
+
|
|
13
|
+
You must configure GitHub as a trusted publisher for `@matt82198/aesop` on npmjs.com. This tells npm to allow publishes from the GitHub Actions workflow without a token.
|
|
14
|
+
|
|
15
|
+
**Steps:**
|
|
16
|
+
|
|
17
|
+
1. Go to **[npmjs.com/package/@matt82198/aesop/settings](https://www.npmjs.com/package/@matt82198/aesop/settings)** (substitute your actual username/package name).
|
|
18
|
+
- If the package doesn't exist yet, you'll need to create it first (see bootstrap caveat below).
|
|
19
|
+
|
|
20
|
+
2. Click **Settings** → **Access** (or **Publishing** on newer UI).
|
|
21
|
+
|
|
22
|
+
3. Scroll to **Trusted Publishers** (or **Publishing from CI/CD**).
|
|
23
|
+
|
|
24
|
+
4. Click **Add a Trusted Publisher** → Select **GitHub Actions**.
|
|
25
|
+
|
|
26
|
+
5. Enter these values:
|
|
27
|
+
- **Organization:** `matt82198` (your GitHub username or org)
|
|
28
|
+
- **Repository:** `aesop`
|
|
29
|
+
- **Workflow:** `publish.yml`
|
|
30
|
+
|
|
31
|
+
6. Click **Save** or **Add**.
|
|
32
|
+
|
|
33
|
+
npm will now allow publishes from the `publish.yml` workflow in the `aesop` repo without a token.
|
|
34
|
+
|
|
35
|
+
### Step 2: Verify npm Version
|
|
36
|
+
|
|
37
|
+
The workflow automatically ensures npm >= 11.5.1 (OIDC requirement). If you're publishing locally (not recommended), confirm:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm --version # Should be 11.5.1 or higher
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Publishing a Release
|
|
44
|
+
|
|
45
|
+
### Via GitHub Web UI (Recommended)
|
|
46
|
+
|
|
47
|
+
1. Go to your repo's **Releases** page.
|
|
48
|
+
2. Click **Draft a new release**.
|
|
49
|
+
3. **Tag version:** Enter a version (e.g., `0.1.0`, `0.1.0-beta.1`).
|
|
50
|
+
4. **Release title:** E.g., "Aesop 0.1.0".
|
|
51
|
+
5. **Description:** Add release notes (optional).
|
|
52
|
+
6. Click **Publish release**.
|
|
53
|
+
|
|
54
|
+
The workflow will automatically trigger and:
|
|
55
|
+
- Check out your code.
|
|
56
|
+
- Run secret scanning to ensure no credentials are accidentally published.
|
|
57
|
+
- Determine the dist-tag:
|
|
58
|
+
- If version contains a hyphen (prerelease, e.g., `0.1.0-beta.1`): `npm publish --tag beta`
|
|
59
|
+
- Otherwise (stable): `npm publish --tag latest`
|
|
60
|
+
- Publish to npm with no token or passkey prompt.
|
|
61
|
+
|
|
62
|
+
### Via Git CLI
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git tag 0.1.0 -a -m "Release Aesop 0.1.0"
|
|
66
|
+
git push origin 0.1.0
|
|
67
|
+
# Then go to GitHub Releases and click "Create release from tag"
|
|
68
|
+
# (Or use gh CLI: gh release create 0.1.0 --generate-notes)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Dist-Tag Logic
|
|
72
|
+
|
|
73
|
+
The workflow automatically selects a dist-tag based on the version in `package.json`:
|
|
74
|
+
|
|
75
|
+
- **Prerelease versions** (contain hyphen):
|
|
76
|
+
- `0.1.0-beta.1` → `npm publish --tag beta`
|
|
77
|
+
- `0.1.0-rc.1` → `npm publish --tag beta`
|
|
78
|
+
- `1.0.0-alpha` → `npm publish --tag beta`
|
|
79
|
+
|
|
80
|
+
- **Stable versions** (no hyphen):
|
|
81
|
+
- `0.1.0` → `npm publish --tag latest`
|
|
82
|
+
- `1.0.0` → `npm publish --tag latest`
|
|
83
|
+
|
|
84
|
+
Users installing with `npm install @matt82198/aesop` will get the latest stable version by default. Beta versions can be installed with `npm install @matt82198/aesop@beta`.
|
|
85
|
+
|
|
86
|
+
## Secret Scanning
|
|
87
|
+
|
|
88
|
+
Before every publish, the workflow runs:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
python3 tools/secret_scan.py .
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This scans the entire package tree for credentials (API keys, tokens, PEM files, etc.). **If any are found, the publish will fail.** This is a safety gate — credentials should never appear in published npm packages.
|
|
95
|
+
|
|
96
|
+
If a false positive occurs (e.g., pattern documentation), add the pragma to the file's first 10 lines:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
# secretscan: allow-pattern-docs
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See `tools/secret_scan.py` for full pragma documentation.
|
|
103
|
+
|
|
104
|
+
## Bootstrap Caveat: First Publish
|
|
105
|
+
|
|
106
|
+
**Important:** npm trusted publishing requires the package to already exist on npmjs.com before you can attach a trusted publisher.
|
|
107
|
+
|
|
108
|
+
- **If the package does NOT yet exist:** You'll need a **one-time bootstrap publish** using either:
|
|
109
|
+
1. An npm **Automation token** (kept in a personal CI/CD secret, used once, then deleted).
|
|
110
|
+
2. The npm web flow (manual login with passkey, generate temporary token).
|
|
111
|
+
|
|
112
|
+
After the first publish, the package exists and the trusted publisher is active for all future releases.
|
|
113
|
+
|
|
114
|
+
- **If the package already exists:** Skip this step. The workflow will publish directly with no token or passkey.
|
|
115
|
+
|
|
116
|
+
**To bootstrap (one-time only):**
|
|
117
|
+
|
|
118
|
+
1. Generate an npm Automation token at [npmjs.com/settings/tokens](https://npmjs.com/settings/tokens) (API type, pub access).
|
|
119
|
+
2. Add it as a GitHub secret (e.g., `NPM_TOKEN`).
|
|
120
|
+
3. Update the workflow `env` section to use it:
|
|
121
|
+
```yaml
|
|
122
|
+
env:
|
|
123
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
124
|
+
```
|
|
125
|
+
4. After the first publish succeeds, **remove the secret from the workflow** (revert to `NODE_AUTH_TOKEN: ${{ secrets.npm_token || '' }}`).
|
|
126
|
+
5. The trusted publisher is now active for future releases.
|
|
127
|
+
|
|
128
|
+
## Troubleshooting
|
|
129
|
+
|
|
130
|
+
### Publish fails: "No permission to publish"
|
|
131
|
+
|
|
132
|
+
→ **Trusted publisher not configured.** Go to npmjs.com settings and add it (step 1 above).
|
|
133
|
+
|
|
134
|
+
### Publish fails: "npm: command not found"
|
|
135
|
+
|
|
136
|
+
→ Shouldn't happen (actions/setup-node handles this). Check Node 22 is installed.
|
|
137
|
+
|
|
138
|
+
### Publish fails: "Secret scan found credentials"
|
|
139
|
+
|
|
140
|
+
→ Run `python3 tools/secret_scan.py .` locally to identify the leak. Remove or redact it, re-commit, then re-release.
|
|
141
|
+
|
|
142
|
+
### I'm using a passkey-only account and get OTP prompts
|
|
143
|
+
|
|
144
|
+
→ That's exactly why we use OIDC. Trusted publishing bypasses passkey+OTP entirely. Ensure the workflow and trusted publisher are correctly configured.
|
|
145
|
+
|
|
146
|
+
## References
|
|
147
|
+
|
|
148
|
+
- [npm OIDC Trusted Publishing](https://docs.npmjs.com/generating-and-authenticating-with-deploy-tokens#authenticating-with-oidc)
|
|
149
|
+
- [GitHub Actions: Setting up Node.js](https://github.com/actions/setup-node)
|
|
150
|
+
- [Secret Scanning Tool](../tools/secret_scan.py)
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Antivirus (AV) Resilience & Behavioral Engine Design Patterns
|
|
2
|
+
|
|
3
|
+
## Problem Statement
|
|
4
|
+
|
|
5
|
+
Antivirus and behavioral-analysis engines (SentinelOne, Windows Defender, etc.) may:
|
|
6
|
+
- **Delete tool binaries mid-session** when they trigger heuristic alerts (Claude Code, git-bash, node, python)
|
|
7
|
+
- **Suspend processes** during scanning, causing temporary hangs or failed tool invocations
|
|
8
|
+
- **Block file writes** to protected directories, silently failing git operations or log appends
|
|
9
|
+
- **Trigger false positives** on legitimate automation workflows (agent dispatch, secret scanning, process spawning)
|
|
10
|
+
|
|
11
|
+
This is not a security breach—it's a *compatibility* problem with legitimate automation tools. Aesop must survive these disruptions gracefully.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Design Principles
|
|
16
|
+
|
|
17
|
+
### 1. **Continuous Push Discipline**
|
|
18
|
+
- **Every cycle completes a push**, not just a stash or backup. If one file blocks due to AV, push the rest.
|
|
19
|
+
- **Philosophy**: A partially-pushed state (some repos synced, some stalled) is **better** than no push at all.
|
|
20
|
+
- **Implementation**:
|
|
21
|
+
- `daemons/backup-fleet.sh` processes repos independently; one BLOCKED repo doesn't stall others.
|
|
22
|
+
- `state/FLEET-BACKUP.log` logs each outcome (CLEAN, PUSHED, SNAPSHOTTED, BLOCKED) per repo.
|
|
23
|
+
- Watchdog cycle completes successfully even if 1 of 10 repos fails.
|
|
24
|
+
|
|
25
|
+
### 2. **Surgical Single-File Recovery**
|
|
26
|
+
- **Minimize scope of recovery**. If a binary is deleted, re-fetch only that one binary, not the whole toolchain.
|
|
27
|
+
- **Pattern**:
|
|
28
|
+
- Monitor tool invocation failures: `python: command not found`, `git: not found`.
|
|
29
|
+
- Trigger minimal reinstall: `apt-get install python`, `git clone <tool>`.
|
|
30
|
+
- Resume immediately without waiting for manual intervention.
|
|
31
|
+
- **Why**: Batch slowdowns (full OS update, system restart) are much worse than targeted tool recovery.
|
|
32
|
+
|
|
33
|
+
### 3. **Avoid Mass Process Kills**
|
|
34
|
+
- **Anti-pattern**: Restarting the orchestrator, killing all agents, wiping state when one process hangs.
|
|
35
|
+
- **Pattern**:
|
|
36
|
+
- Use tight heartbeat timeouts (300s for watchdog, 3600s for monitor).
|
|
37
|
+
- Kill **only** the stalled agent by its PID; main orchestrator stays alive.
|
|
38
|
+
- Log the kill and its reason (e.g., "heartbeat age 450s > threshold 300s").
|
|
39
|
+
- Resume the agent or defer to next cycle.
|
|
40
|
+
|
|
41
|
+
### 4. **Idempotent Push Refs**
|
|
42
|
+
- **Backup refs are date-stamped, not timestamped**: `backup/wip-20260711`, not `backup/wip-20260711-134521`.
|
|
43
|
+
- **Why**: Multiple backup attempts on the same day write to the same ref, so git naturally deduplicates them.
|
|
44
|
+
- **Recovery**: If a push partially fails and the process dies, the next cycle tries again to the same ref and succeeds without creating orphaned branches.
|
|
45
|
+
|
|
46
|
+
### 5. **Tracked-Files-Only Secret Scanning**
|
|
47
|
+
- **Scan only modified files**, not the entire repo (which includes .gitignored binaries, node_modules, build artifacts).
|
|
48
|
+
- **Pattern**:
|
|
49
|
+
```bash
|
|
50
|
+
git diff --name-only HEAD | xargs secret_scan.py
|
|
51
|
+
git ls-files -m | xargs secret_scan.py
|
|
52
|
+
```
|
|
53
|
+
- **Why**:
|
|
54
|
+
- AV engines sometimes block access to large binary files during scanning. Scanning a 500MB node_modules triggers false-positive rate.
|
|
55
|
+
- Scanning only tracked changes is **faster** and **more reliable** than full-directory scans.
|
|
56
|
+
- Mirrors what a push gate would actually check (`git diff` before commit).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Failure Modes & Recovery
|
|
61
|
+
|
|
62
|
+
### Scenario 1: Binary Deletion Mid-Cycle
|
|
63
|
+
|
|
64
|
+
**What happens:**
|
|
65
|
+
```bash
|
|
66
|
+
$ bash /path/to/aesop/daemons/backup-fleet.sh
|
|
67
|
+
[2026-07-11 10:30:15] === cycle start ===
|
|
68
|
+
[2026-07-11 10:30:16] SentinelOne deletes git.exe (false positive on automation)
|
|
69
|
+
$ git fetch -q
|
|
70
|
+
bash: git: command not found
|
|
71
|
+
[2026-07-11 10:30:17] BLOCKED: my-project-repo
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Recovery:**
|
|
75
|
+
1. Watchdog detects BLOCKED outcome → logs to FLEET-BACKUP.log.
|
|
76
|
+
2. Next cycle (150s later) detects repo is touched (uncommitted files remain).
|
|
77
|
+
3. Retry `is_touched()` → re-fetch → get `command not found` again.
|
|
78
|
+
4. **Manual intervention path**: User or admin runs:
|
|
79
|
+
```bash
|
|
80
|
+
# Re-install git (OS-specific)
|
|
81
|
+
winget install Git.Git # Windows
|
|
82
|
+
apt-get install git # Linux
|
|
83
|
+
```
|
|
84
|
+
5. Next watchdog cycle succeeds (binary is back).
|
|
85
|
+
|
|
86
|
+
**Design choice**: Watchdog logs but doesn't auto-repair missing binaries (policy: fail safely, log clearly, resume on next cycle).
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Scenario 2: Process Suspension During Secret Scan
|
|
91
|
+
|
|
92
|
+
**What happens:**
|
|
93
|
+
```bash
|
|
94
|
+
$ python secret_scan.py file1.py file2.py file3.py
|
|
95
|
+
# AV engine suspends python.exe during scan of large file
|
|
96
|
+
[waiting indefinitely or timing out after 30s]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Recovery:**
|
|
100
|
+
1. `scan_tracked_files()` runs with implicit timeout (scan completes or fails).
|
|
101
|
+
2. If timeout, `scan_tracked_files` returns non-zero exit code → BLOCKED outcome.
|
|
102
|
+
3. Files remain uncommitted (no push attempt).
|
|
103
|
+
4. **Recommendation**: Tune secret-scan to scan **only staged files** (via `git diff --cached`), reducing scope.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### Scenario 3: Orchestrator Heartbeat Stale
|
|
108
|
+
|
|
109
|
+
**What happens:**
|
|
110
|
+
```bash
|
|
111
|
+
# Watchdog daemon hung (AV locked python.exe; backup-fleet.sh stalled)
|
|
112
|
+
# Heartbeat timestamp = 1720680915 (5 minutes ago)
|
|
113
|
+
$ cat state/.watchdog-heartbeat
|
|
114
|
+
1720680915
|
|
115
|
+
$ date +%s
|
|
116
|
+
1720681215 # Current time, 300s later
|
|
117
|
+
# Dashboard threshold = 300s for watchdog
|
|
118
|
+
# Status: STALE
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Recovery:**
|
|
122
|
+
1. Dashboard shows `STALE (age: 300s+)` in red.
|
|
123
|
+
2. User checks logs (`state/FLEET-BACKUP.log`) to diagnose why daemon stalled.
|
|
124
|
+
3. Options:
|
|
125
|
+
- **If binary is missing**: Reinstall and restart watchdog.
|
|
126
|
+
- **If process is hung**: Kill it (`pkill -f run-watchdog.sh`) and restart.
|
|
127
|
+
- **If it's a false stall**: Wait for next cycle (watchdog usually recovers).
|
|
128
|
+
4. Restart watchdog:
|
|
129
|
+
```bash
|
|
130
|
+
bash $AESOP_ROOT/daemons/run-watchdog.sh
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Best Practices
|
|
136
|
+
|
|
137
|
+
### For Aesop Operators
|
|
138
|
+
|
|
139
|
+
1. **Monitor the dashboard**. STALE heartbeats are your canary in the coal mine.
|
|
140
|
+
2. **Review FLEET-BACKUP.log** weekly for BLOCKED outcomes. Investigate patterns.
|
|
141
|
+
3. **Keep security scanner narrow**: Scan only staged files (`--staged`), not entire repos.
|
|
142
|
+
4. **Whitelist Aesop tools in your AV**:
|
|
143
|
+
- `C:\Program Files\Git\bin\bash.exe`
|
|
144
|
+
- `python.exe` (if Python is on PATH)
|
|
145
|
+
- Node.js (if using orchestration monitor)
|
|
146
|
+
- The Aesop root directory itself (read-only, no execution harm)
|
|
147
|
+
|
|
148
|
+
### For Aesop Developers
|
|
149
|
+
|
|
150
|
+
1. **Test against real AV**. Spin up a test VM with SentinelOne or similar and deliberately trigger false positives.
|
|
151
|
+
2. **Log all process invocations**. Include PID, command, exit code, stderr (first 100 chars).
|
|
152
|
+
3. **Use tight timeouts** (30s for secret scans, 5s for git commands). Fail fast, don't hang.
|
|
153
|
+
4. **Implement single-instance guards** via heartbeat files. Don't spawn multiple instances of the same daemon.
|
|
154
|
+
5. **Design for recovery**: Every log entry should make it obvious to a human or script *why* something failed and *what to do next*.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Configuration & Tuning
|
|
159
|
+
|
|
160
|
+
### Heartbeat Thresholds (seconds)
|
|
161
|
+
|
|
162
|
+
Tune these based on your infrastructure speed:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
get_hb_threshold() {
|
|
166
|
+
local name="$1"
|
|
167
|
+
case "$name" in
|
|
168
|
+
*monitor*) echo 3600 ;; # Orchestration monitor: 1 hour (low-frequency signal collection)
|
|
169
|
+
*watchdog*) echo 300 ;; # Watchdog daemon: 5 minutes (tight, frequent backup cycles)
|
|
170
|
+
*backup*) echo 300 ;; # Backup script: 5 minutes (same cadence as watchdog)
|
|
171
|
+
*) echo 300 ;; # Default: 5 minutes
|
|
172
|
+
esac
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **Increase thresholds** if your infrastructure is slow (slow disks, high load, slow network).
|
|
177
|
+
- **Decrease thresholds** if you want faster failure detection (but risk false STALE alerts during heavy AV scans).
|
|
178
|
+
|
|
179
|
+
### Secret Scanning Scope
|
|
180
|
+
|
|
181
|
+
Change `scan_tracked_files()` to scan only staged files instead of modified files:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
scan_tracked_files() {
|
|
185
|
+
local repo="$1"
|
|
186
|
+
# Alternative: scan only files staged for commit
|
|
187
|
+
git diff --cached --name-only 2>/dev/null | xargs -r secret_scan.py
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Logging Detail
|
|
192
|
+
|
|
193
|
+
Increase logging verbosity to diagnose AV conflicts:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
log "git fetch returned $?: $(cd $repo && git fetch -q origin 2>&1 | head -c 100)"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Testing & Validation
|
|
202
|
+
|
|
203
|
+
### Test Case: Simulate Binary Deletion
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# 1. Start watchdog in background
|
|
207
|
+
bash $AESOP_ROOT/daemons/run-watchdog.sh &
|
|
208
|
+
WATCHDOG_PID=$!
|
|
209
|
+
|
|
210
|
+
# 2. Simulate git deletion
|
|
211
|
+
mv /c/Program\ Files/Git/bin/git.exe /tmp/git.exe.bak
|
|
212
|
+
|
|
213
|
+
# 3. Wait for next cycle
|
|
214
|
+
sleep 160
|
|
215
|
+
|
|
216
|
+
# 4. Check logs
|
|
217
|
+
tail -20 $AESOP_ROOT/state/FLEET-BACKUP.log
|
|
218
|
+
|
|
219
|
+
# 5. Restore git
|
|
220
|
+
mv /tmp/git.exe.bak /c/Program\ Files/Git/bin/git.exe
|
|
221
|
+
|
|
222
|
+
# 6. Next cycle should recover
|
|
223
|
+
sleep 160
|
|
224
|
+
tail -10 $AESOP_ROOT/state/FLEET-BACKUP.log
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Expected Output
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
[2026-07-11 10:30:15] === cycle start ===
|
|
231
|
+
[2026-07-11 10:30:16] BLOCKED: my-repo
|
|
232
|
+
[2026-07-11 10:30:16] === cycle end ===
|
|
233
|
+
[2026-07-11 10:32:16] === cycle start ===
|
|
234
|
+
[2026-07-11 10:32:17] CLEAN: my-repo # (or PUSHED if changes made during downtime)
|
|
235
|
+
[2026-07-11 10:32:17] === cycle end ===
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## References
|
|
241
|
+
|
|
242
|
+
- [SentinelOne Behavioral Engine False Positives](https://community.sentinelone.com/)
|
|
243
|
+
- [Windows Defender ASR Rules](https://learn.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/attack-surface-reduction)
|
|
244
|
+
- [Git Best Practices for Automation](https://git-scm.com/docs/git-var)
|
|
245
|
+
- Aesop docs/STATE-MACHINE.md — Durable checkpointing recovery
|
|
246
|
+
- Cardinal Rule 3 (main CLAUDE.md) — "Reliability core: Inputs ALWAYS produce outputs"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Aesop Orchestration Monitor — Charter
|
|
2
|
+
|
|
3
|
+
A standing background monitor (Haiku subagent) that watches how the multi-agent system operates and acts on points of refinement. It improves the *machinery*, never the *mission*.
|
|
4
|
+
|
|
5
|
+
## FIXED GOAL (never changes)
|
|
6
|
+
|
|
7
|
+
> Maintain your projects correctly, cheaply, and autonomously under your cardinal rules.
|
|
8
|
+
> The monitor's job is to make that machine run better — it must NOT redirect, re-scope,
|
|
9
|
+
> or invent new project goals. If it ever thinks the goal should change, it writes a note
|
|
10
|
+
> in `PROPOSALS.md` and stops there.
|
|
11
|
+
|
|
12
|
+
## What it watches
|
|
13
|
+
|
|
14
|
+
1. **Heartbeat health** — are loop daemons alive? (watchdog ~300s, monitor ~3600s)
|
|
15
|
+
2. **Security alerts** — any high/medium severity issues in the fleet?
|
|
16
|
+
3. **Log rotation** — do append-only logs need archiving?
|
|
17
|
+
4. **Memory staleness** — are project notes up to date?
|
|
18
|
+
5. **Orchestration drift** — are agents following the cardinal rules?
|
|
19
|
+
|
|
20
|
+
## Action tiers
|
|
21
|
+
|
|
22
|
+
- **AUTO (apply immediately, then log to `ACTIONS.log`):**
|
|
23
|
+
- Heartbeat freshness checks (read-only).
|
|
24
|
+
- Log rotation (invoke `rotate_logs.py` if available).
|
|
25
|
+
- Persist project memory updates (if applicable).
|
|
26
|
+
- Quarantine defunct scripts (if a quarantine directory exists).
|
|
27
|
+
|
|
28
|
+
- **PROPOSE (write to `PROPOSALS.md`, do NOT apply — needs user approval):**
|
|
29
|
+
- Changes to cardinal rules or agent configuration.
|
|
30
|
+
- Alterations to agent behavior or model choice.
|
|
31
|
+
- Deletions of anything outside the monitor's own quarantine.
|
|
32
|
+
- Any change to orchestration policy.
|
|
33
|
+
|
|
34
|
+
## Hard guardrails (inviolable)
|
|
35
|
+
|
|
36
|
+
- Preserve the FIXED GOAL. Improve rules; never rewrite intent.
|
|
37
|
+
- **Subagents are ALWAYS Haiku** (cost optimization). Orchestrator on main thread only.
|
|
38
|
+
- NEVER push destructively or to restricted remotes as an AUTO action.
|
|
39
|
+
- Idempotent + additive: safe to run repeatedly; prefer append over overwrite.
|
|
40
|
+
- Stay cheap: read the signal brief, not raw logs. One tight cycle, then sleep.
|
|
41
|
+
|
|
42
|
+
## Outputs
|
|
43
|
+
|
|
44
|
+
- `BRIEF.md` — human-readable status snapshot.
|
|
45
|
+
- `SIGNALS.json` — machine-readable metrics and findings.
|
|
46
|
+
- `ACTIONS.log` — append-only record of AUTO actions taken (timestamped).
|
|
47
|
+
- `PROPOSALS.md` — staged changes awaiting user approval.
|
|
48
|
+
- `quarantine/` (optional) — parked suspect scripts + manifest.
|
|
49
|
+
|
|
50
|
+
## Operating it
|
|
51
|
+
|
|
52
|
+
1. **Deploy the monitor** under `/power` or as a continuous background task.
|
|
53
|
+
2. **Arm the charter**: customize this file with your project names, repos, and thresholds.
|
|
54
|
+
3. **Review PROPOSALS.md** periodically; approve changes or reject them.
|
|
55
|
+
4. **Check ACTIONS.log** to see what was automated.
|
|
56
|
+
5. **Archive BRIEF.md snapshots** if you want a historical record.
|
|
57
|
+
|
|
58
|
+
## Customization
|
|
59
|
+
|
|
60
|
+
Edit `collect-signals.mjs` to:
|
|
61
|
+
- Add your own repo paths and project names.
|
|
62
|
+
- Define custom signal collectors (e.g., junk-script detection).
|
|
63
|
+
- Set heartbeat thresholds appropriate to your workflow.
|
|
64
|
+
- Integrate security scanners or compliance checks.
|
|
65
|
+
|
|
66
|
+
See comments in `collect-signals.mjs` for extension points.
|