# Autonomy System Audit — 2026-05-26

## Key Definitions (quoted from source)

From `src/workflows/continuous-runner.ts`:
> "Continuous workflows run indefinitely as a daemon, spawning one iteration session at a time and advancing when an iteration ends."
> "Daemons live in-process under `daemons` (a Map keyed by parent runId). They DO NOT survive a gateway restart — restart-resume is v2 work."

From `src/workflows/types.ts`:
> `WorkflowKind = "static" | "continuous"` — discriminates between one-shot workflows and autonomous-control workflows that run continuously across many iterations until stopped.
> `WorkflowRunStatus` includes `"paused" // continuous-only: daemon paused via autonomy pause or user`

From `src/autonomy/manager.ts`:
> Task queue with heartbeat, pause file, hardware checks. Separate system from the continuous-runner daemon map.

---

## Agent Identities Found

Directory `~/.infinibot/agents/`:
- `main` — primary agent
- `coder` — coding agent (used by OpenCode/Claude Code backend)
- `voice` — voice chat agent
- `canvas` — canvas/visual agent

All four identities are real directories with active configs.

---

## System Inventory

### 1. Continuous Workflow Daemon (`src/workflows/continuous-runner.ts`)
- In-memory `Map<string, DaemonHandle>` — survives only for the process lifetime.
- Control surface: `startContinuousWorkflow`, `stopContinuousRun`, `pauseContinuousRun`, `resumeContinuousRun`, `listDaemons`, `getDaemon`.
- Each iteration calls either `sessions.spawn` (gateway mode) or `terminal.spawnAgent` (terminal mode).
- Runs in the background via `void daemonLoop(...)`.

### 2. Workflow Store (`src/workflows/store.ts` + `runner.ts`)
- Workflow definitions stored as YAML-frontmatter markdown files in `<stateDir>/workflows/*.md`.
- Run history stored in `<stateDir>/workflows/runs.json` (capped at 500).
- No `autostart` field in `WorkflowMeta` — workflows cannot declare self-restart on gateway boot.

### 3. Autonomy Manager (`src/autonomy/manager.ts`)
- Separate task queue (`queue.ts`), pause file (`pause.ts`), hardware monitor, audit log, notifications.
- `autonomy:pause` → writes a pause file. No connection to the continuous-runner daemon map.
- Orchestrator (`orchestrator.ts`) drives a closed build→test→assess loop for queued tasks.

### 4. Gateway Cron Service (`src/gateway/server-cron.ts`)
- `CronService` fires scheduled jobs.
- `resolveCronAgent()` resolves a job's `agentId` param against the configured agents list; falls back to `resolveDefaultAgentId()`.
- Works for all four agent identities.

### 5. Gateway Server Methods
- `workflowHandlers` in `server-methods/workflows.ts` — fully wired; CRUD + run + continuous start/stop/pause/resume.
- `autonomyHandlers` in `server-methods/autonomy.ts` — fully wired; queue/audit/pause/hardware.
- No startup hook in `workflowHandlers` — reconciliation of stale "running" runs never happens.

---

## Gap Matrix

Format: **WORKS** / **PARTIAL** / **BROKEN** / **MISSING**

| Autonomy Component | main | coder | voice | canvas | Notes |
|---|---|---|---|---|---|
| Continuous daemon: start/stop/pause/resume API | WORKS | WORKS | WORKS | WORKS | All four control functions exist and are wired |
| Continuous daemon: agentId targeting | WORKS | WORKS | WORKS | WORKS | `agentId` is a plain string; any identity works |
| Continuous daemon: survives gateway restart | MISSING | MISSING | MISSING | MISSING | In-process Map only. Explicitly deferred as "v2" in code comments |
| Continuous daemon: autonomy pause integration | **BROKEN** | **BROKEN** | **BROKEN** | **BROKEN** | `autonomy:pause` writes pause file but never calls `pauseContinuousRun`. Type comment says "daemon paused via autonomy pause or user" but the wiring is absent |
| Stale "running" runs after gateway restart | **BROKEN** | **BROKEN** | **BROKEN** | **BROKEN** | Continuous runs stay status="running" in runs.json after restart with no live daemon — permanently stuck, never cleaned up |
| Test coverage: continuous-runner | MISSING | - | - | - | `runner.test.ts` only covers `validateInputs`, `preflightWorkflow`, `WorkflowRunStore`. Zero continuous-runner tests |
| Cron jobs: agentId targeting | WORKS | WORKS | WORKS | WORKS | `resolveCronAgent()` resolves any configured agentId with main fallback |
| Autonomy orchestrator: agentId targeting | WORKS | WORKS | WORKS | WORKS | `sessions.spawn` with arbitrary agentId param |
| Autonomy pause: propagates to running daemons | **BROKEN** | **BROKEN** | **BROKEN** | **BROKEN** | Same as "autonomy pause integration" row — gap is in the gateway handler wiring |
| Autonomy queue: per-agent task routing | PARTIAL | PARTIAL | PARTIAL | PARTIAL | `prefire.builderAgentId`/`testerAgentId` exist in types but are optional; default orchestrator flow doesn't enforce agent routing |
| Session recovery (restart after crash) | MISSING | MISSING | MISSING | MISSING | No implementation. `src/projects/recovery.ts` exists for project sessions but not for continuous workflows |
| Cancel/pause hook on continuous runs | WORKS | WORKS | WORKS | WORKS | `stopContinuousRun`, `pauseContinuousRun`, `resumeContinuousRun` all exist |
| Gateway boot: runs BOOT.md agent | WORKS | - | - | - | `runBootOnce` fires on main session only |

---

## Prioritized Gaps

### BROKEN (must fix)
1. **Autonomy pause not propagated to continuous daemons** — `autonomy:pause`/`autonomy:resume` gateway handlers don't call `pauseContinuousRun`/`resumeContinuousRun`. The type doc explicitly names this as intended behavior.
2. **Stale "running" status after gateway restart** — Continuous runs marked "running" in `runs.json` have no live daemon after restart. UI shows them as still running when they're dead.

### MISSING-critical (should fix)
3. **No tests for continuous-runner** — All three control paths (stop, pause, resume) plus the daemon loop logic are production code with zero test coverage.

### MISSING-nice-to-have (out of scope for this run)
4. No `autostart` field on workflow meta for auto-restart on gateway boot.
5. No full session recovery (auto-restart) for continuous workflows after restart.
6. No editor UI for `kind`/`continuous` config — users edit YAML frontmatter directly.
7. Autonomy queue doesn't enforce per-agent routing by default.

---

## Fixed in this run (2026-05-26)

### Fix 1 — Autonomy pause not propagated to continuous daemons (BROKEN → WORKS)

**File**: `src/gateway/server-methods/autonomy.ts`

Added imports for `listDaemons`, `pauseContinuousRun`, `resumeContinuousRun` from `../../workflows/continuous-runner.js`. In the `autonomy:pause` handler, after calling `autonomy.pause()`, all non-stopped, non-paused daemons are now paused with `requestedBy="autonomy.pause"`. In the `autonomy:resume` handler, after calling `autonomy.resume()`, all non-stopped, paused daemons are resumed with `requestedBy="autonomy.resume"`.

Note: This propagation covers all active daemons. If a daemon was user-paused independently, it will also be resumed by `autonomy:resume` — which is consistent with the intent (pause/resume all autonomous activity).

### Fix 2 — Stale "running" continuous runs after gateway restart (BROKEN → WORKS)

**Files**: `src/workflows/continuous-runner.ts`, `src/workflows/index.ts`, `src/gateway/server-methods/workflows.ts`

Added `reconcileStaleContinuousRuns(stateDir: string): number` to `continuous-runner.ts`. This function scans `runs.json` for records with `kind: "continuous"` and `status: "running"` or `"paused"` that have no live daemon in the `daemons` Map, and marks them `status: "failed"` with `stopRequestedBy: "gateway_restart"`.

The function is exported from `workflows/index.ts` and called lazily-once from both `workflows.run.list` and `workflows.continuous.list` gateway handlers via a `reconcileOnce()` guard. This ensures stale runs are cleaned up on the first UI load after a gateway restart, without adding any cold-start latency to the gateway process itself.

### Fix 3 — No tests for continuous-runner (MISSING → WORKS)

**File**: `src/workflows/continuous-runner.test.ts` (new, 19 tests)

Added tests covering:
- `startContinuousWorkflow`: returns correct run record, throws on missing/wrong-kind workflows, registers daemon in the daemons Map, runs to iteration_limit and writes correct final status
- `stopContinuousRun`: returns false for unknown runId, stops a running daemon and removes it from the Map
- `pauseContinuousRun` / `resumeContinuousRun`: return false for unknown runId, pause/resume a live daemon
- `listDaemons`: returns an array, includes newly started daemons
- Error streak auto-stop: daemon stops and marks status="failed" after threshold consecutive failures
- `reconcileStaleContinuousRuns`: does nothing with no runs, marks orphaned running/paused runs failed, leaves completed/static/live-daemon runs untouched

All 19 tests pass. Existing 15 tests in `runner.test.ts` continue to pass (34 total).

Build: `pnpm build` exits cleanly with zero new TypeScript errors.
