# v4.10.0 Autonomous Loop Anti-Fragility — Archive

**Status:** Shipped 2026-04-29
**Phases:** 35-38 (4 phases, 4 plans)
**Driving incident:** 2026-04-29 — JSONL `1c58cfbc-…` recorded two distinct cwds (cross-worktree contamination); registry stored `owner_session_id: "unknown"` for every entry; zero `heartbeat.json` files written; one zombie launchd job firing every 15 min into a registry with no entry; one orphan registry entry with no plist.

## Goals

1. Cross-contamination becomes structurally impossible (defense-in-depth, refuse-by-default).
2. Every state mutation is logged to an append-only provenance ledger.
3. Operators get a self-diagnostic GREEN/YELLOW/RED report from inside Claude.
4. Recovery from corrupted state is automatic and idempotent.

All four goals achieved. See `.planning/v4.10.0-MILESTONE-AUDIT.md`.

## Phases

### Phase 35: Provenance Foundation (PROV-01..04)

**Goal:** Append-only `<state_dir>/provenance.jsonl` ledger + 10k-rotated global mirror with schema-versioned events; intent-before-state-write discipline.

**Shipped:**
- `scripts/provenance-lib.sh` (~240 LOC): `emit_provenance(loop_id, event, fields...)`, `rotate_global_provenance`. Cross-platform locking (flock | lockf | POSIX O_APPEND fallback).
- `tests/test-provenance.sh`: 21 assertions / 5 cases (happy, 10-process concurrent writes, rotation, missing state_dir graceful, schema validation).

**Commit:** `124f8bb3 feat(autonomous-loop): add provenance-lib.sh foundation (PROV-01..04)`

### Phase 36: Hook-Time Binding (BIND-01..04)

**Goal:** Move session→loop binding from broken `$CLAUDE_SESSION_ID` env-var to SessionStart hook stdin payload; detect cwd drift on every heartbeat; multi-session same-folder becomes first-class (one owner, N observers).

**Shipped:**
- `hooks/session-bind.sh` (new, ~190 LOC): SessionStart hook with state-machine binding (bind_first / bind_resume / observer / stale_owner_detected).
- `hooks/heartbeat-tick.sh` rewrite: stdin first, two-pass loop match (by session_id then cwd), bound_cwd preserved across `write_heartbeat` via read-before/merge-after pattern.
- `scripts/hook-install-lib.sh` extension: `install_all_hooks` / `uninstall_all_hooks` composites; per-hook installers for SessionStart.
- `skills/start/SKILL.md`: drops `$CLAUDE_SESSION_ID` capture; sets `owner_session_id="pending-bind"`.
- `tests/test-session-bind.sh` (10 assertions / 5 cases) + `tests/test-heartbeat-stdin.sh` (6 assertions / 3 cases).

**Commit:** `76359bfd feat(autonomous-loop): hook-time session-loop binding (BIND-01..04)`

### Phase 37: Waker Hardening + launchd Collision Defense (WAKE-01..05)

**Goal:** Five-check pre-spawn invariant; cd to `dirname(contract_path)` on resume; launchd Label collision detection in `generate_plist`.

**Shipped:**
- `scripts/waker.sh`: `_invariant_check_spawn` runs all 5 checks (UUID validity, heartbeat-from-cwd, bound_cwd matches, launchd Label uniqueness, generation-drift). Refuses with typed `spawn_refused_<which>` provenance + notification on any failure.
- `scripts/launchd-lib.sh`: collision detection in `generate_plist` archives existing loaded plist to `state_dir/orphans/<unixts>/`, calls `launchctl bootout`, regenerates. Emits `label_collision_resolved` event.
- `tests/test-spawn-invariant.sh` (6 cases) + `tests/test-plist-collision.sh` (3 cases via PATH-shimmed `launchctl`).

**Commit:** `8feb534b feat(autonomous-loop): waker hardening + launchd collision defense (WAKE-01..05)`

### Phase 38: Doctor & Self-Heal (DOC-01..04)

**Goal:** `/autonomous-loop:doctor` skill with GREEN/YELLOW/RED report + `--fix` mode; idempotent `heal-self.sh` migration archives stale `unknown`/`pending-bind` entries on every fresh SessionStart.

**Shipped:**
- `scripts/doctor-lib.sh`: `loop_doctor_report` (with `--json` mode) and `loop_doctor_fix` (safe remediations only — never spawns claude, never auto-reclaims).
- `scripts/heal-self.sh`: idempotent migration. Archives entries with `owner_session_id ∈ {unknown, '', pending-bind}` >1h old to `~/.claude/loops/registry.archive.jsonl`. Hash-gated via SHA256(registry.json).
- `skills/doctor/SKILL.md`: user-invocable `/autonomous-loop:doctor`.
- `hooks/session-bind.sh`: invokes `heal-self.sh` automatically on every fresh SessionStart.
- `tests/test-doctor.sh` (7 assertions / 4 cases) + `tests/test-heal-self.sh` (6 assertions / 3 cases).

**Commit:** `b6e7f6a1 feat(autonomous-loop): doctor + self-heal (DOC-01..04)`

## Stats

- Commits (plugin source): 4 (plus 4 plan commits + 4 close commits + 1 milestone-scaffold + 1 audit)
- Files changed: 17 (within plugin)
- LOC: +2684 / -50
- Test scripts added: 7
- Test assertions: 60 across 23 cases — 100% green
- Duration: 1 day (single session)
- shellcheck: clean
- Plugin validation: PASS

## Companion Cleanup

- Unloaded zombie launchctl `com.user.claude.loop.f14e7c6d1ef1` (ff-calendar contract DONE, no registry entry)
- Removed dangling registry entry `c864c05b8010` (irhosdv-harness DONE)
- Pruned 13 `/var/folders/*` test-tmp registry entries
- Login Items panel reduced from 3 `claude-loop-runner` entries to 2 (real loops only)

## Refuse-by-default Outcome

The system now structurally cannot:
- Resume a session without a verified UUID
- Resume from a cwd that drifted out of contract dir
- Resume past a launchd label collision
- Lose track of binding decisions (every one is in the ledger)
- Silently overwrite a loaded plist

Worst observable failure mode: "loop didn't auto-resume" → user notices via `/autonomous-loop:doctor`. Never: "loop resumed wrong session into wrong project" — the failure that produced the original 1c58cfbc JSONL contamination.
