← 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.