← back to Fleet Lifecycle Steward
README.md
53 lines
# fleet-lifecycle-steward — the ACTION tier
The Fleet Lifecycle Officer's mechanical action tier. Pairs with the Claude
subagent `fleet-lifecycle-steward` (judgment tier) and the registry's own
detection tier. Steve's ask: **DW agents never go to sleep unless old & irrelevant.**
## Two-tier fleet reconcile (no overlap, no collision)
| Tier | Where | Job | Plist |
|---|---|---|---|
| **Detect + alert** (read-only) | `~/Projects/_shared/fleet-registry/scripts/run.sh` | refresh manifest → DARK/ZOMBIE/WARN findings → CNCP card + George email. *Never restarts/kills.* | `com.steve.fleet-reconcile` |
| **Act + draft** (this repo) | `fleet-act.mjs` | safe auto-restart (allowlist) + gated memo drafts to `pending-approval` + 14d retirement flags | `com.steve.fleet-act` |
| **Judgment** | Claude subagent `fleet-lifecycle-steward` | ambiguous outages, retirement decisions, anything needing reasoning | (spawned) |
The detect tier owns the registry repo; this tier only **reads** the manifest it
keeps fresh. Never edit the other repo's files or its plist.
## Decisions baked in (DTD 2026-06-24)
- **Q1=A scheduled** (launchd `StartInterval` 900s, "loaded" not a daemon)
- **Q2=A subagent + script**, no new always-on daemon
- **Q3=A 14-day** staleness window (`STALE_DAYS` in fleet-act.mjs; bump to 30 if noisy)
- **Q4=B restricted restart** — DEFAULT-DENY allowlist (`safe-restart-allowlist.json`);
customer-facing / mid-write / Kamatera services are GATED (memo, never auto-bounced)
## Run-health tier (the claude-mail crash-loop fix, 2026-06-25)
`com.steve.claude-mail` crash-looped for HOURS unnoticed: rejected IMAP credential
→ launchd `loaded` between fires but exit-1 every fire. Every liveness/online check
called it healthy. Now closed:
- **Generator** (`_shared/fleet-registry`) records `last_exit` (decoded `launchctl
list <label>` LastExitStatus) + `log_health` (FATAL log-tail scan) per launchd job.
- **`fleet-act.mjs`** adds an **UNHEALTHY** tier: active + expected_up + looks-live
(loaded/online) BUT crash-looping → **gated memo, NEVER auto-restarted** (a rejected
credential / EADDRINUSE / missing module won't fix on a bounce — a kick just hides
the loop). Benign SIGTERM-on-a-running-daemon false positives are filtered.
- **Embedded self-heal complement** for critical jobs: `~/Projects/claude-mail/health.js`
pages Steve via CNCP :3333 + macOS osascript + George :9850 after 3 consecutive fails.
## Run
```sh
node fleet-act.mjs # dry-run — prints findings, touches nothing
node fleet-act.mjs --apply # safe restarts + writes gated memos (idempotent)
node health-scan.mjs # READ-ONLY full health table of every process
node health-scan.mjs --bad # only non-HEALTHY rows
node health-scan.mjs --json # machine-readable
```
Memos land in `~/.claude/yolo-queue/pending-approval/fleet-*.md` — Steve approves.
Nothing customer-facing is ever restarted or retired without sign-off.