> ## Documentation Index
> Fetch the complete documentation index at: https://prism.ntecdev.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Spec 070 build plan 2026 05 02

# SPEC-070 build plan — Per-agent persona daemon

**Date:** 2026-05-02
**Status:** active — kicked off after SPEC-070 v0.2 ratification (status=accepted, 14:30:49Z)
**Author:** Donna (Engineering)
**Reviewers:** Texi (Architect), Donna (PO)
**Implementation lanes:** Donna (core + backend), Texi (Codex shim plugin), Porsche (dashboard live wire), Lafonda (install/launcher), Desiree (scribe + doc refresh)

This plan organizes the SPEC-070 implementation into three phases. Each lane has a clear owner; dependencies between lanes are made explicit so peers can ship in parallel without blocking each other.

## Reference artifacts

* **SPEC-070 v0.2** (status=accepted) — normative contract. Cite in every PR.
* **ADR-41** — Reconnect FSM (`DISCONNECTED → RECONCILING → CONNECTED → EMITTING`). Cite in WS reconnect logic.
* **ADR-42** — Project attach lifecycle (persona×project-scoped, no dynamic multi-project attach/detach in v1).
* **ADR-43** — Daemon-via-shim (PENDING; Texi to file). Surface integrations cite once filed.
* **PR #46** — Lafonda's cross-OS launcher matrix. Cite in launcher work.
* **postmortem `0660f88e`** — registry stale-binding. Cite in routing-registry lifecycle code.
* **PR #45 / commit `5df6679`** — Tier-1 BIOS auto-drain rule (the behavioral mitigation this implementation supersedes structurally).

## Lane assignments

| Lane                                             | Owner   | Scope                                                                                                                                                                                                 |
| ------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Backend (registration, routing, drain semantics) | Donna   | `kind=daemon` registration, prism\_status separation, structured-failure on stale targets, daemon heartbeat acceptance                                                                                |
| Daemon binary core                               | Donna   | New `daemon/` Node binary: WS client + local IPC server + lifecycle FSM + plugin loader + metrics + structured logger                                                                                 |
| Codex surface plugin                             | Texi    | Codex shim plugin per Plugin contract (notify\_surface / resume\_surface / status). Off-limits to others per memory `feedback_texi_codex_lane.md`                                                     |
| Claude Code surface plugin                       | Donna   | Claude Code shim plugin (sibling of Codex plugin)                                                                                                                                                     |
| Claude Desktop surface plugin                    | Donna   | Fallback path; degraded delivery to next explicit drain                                                                                                                                               |
| Dashboard live wire                              | Porsche | One-file flip in `dashboard/src/daemons/scraper.ts:tick()` after emit names commit. Slice-3 candidate: drain-rate counter (v0.3 metric)                                                               |
| Install lane                                     | Lafonda | Per-OS launcher integration (LaunchAgent / systemd-user / Task Scheduler logon-trigger), install/uninstall verbs, `prism_persona_create` spawn-daemon call, programmatic linger on Linux, pwsh parity |
| Scribe + doc refresh                             | Desiree | Continuous journaling, inconsistency-watch on every PR, `docs/*` refresh check on every commit per memory `feedback_doc_refresh_on_commit`                                                            |
| PO oversight                                     | Donna   | Phase-boundary sign-offs only                                                                                                                                                                         |

## Phase A — Foundation (parallel)

### A1. Backend daemon registration kind — Donna

**Goal:** Add `kind=daemon` discriminator so daemons register without polluting agent counts or master-eligibility.

* Migration: add `kind` column to `agent_registration` table (or equivalent). Default `agent` for backwards-compat.
* `prism_start` accepts optional `kind=daemon` parameter. Daemon-kind registrations are not master-eligible, do not preempt, do not appear in agent counts.
* `prism_status` separates daemon rows from agent rows in the routing view.
* `prism_signal` rejects sends to wrapped/stale targets with structured failure (per invariant #5 + postmortem `0660f88e`).
* New verb: `prism_signal_status(signal_id)` returning `pending | delivered | drained | expired` (addresses memory `project_signal_vocab_tracking` finding that `delivered=false` at verb-return is meaningless).

**Deliverable:** Single PR. Acceptance criteria 4, 12 partial.

### A2. Daemon binary skeleton — Donna

**Goal:** Establish `daemon/` as a Node TypeScript module sibling of `mcp-node/`. Mirror packaging conventions.

* Directory: `daemon/` at repo root. Node 18+, TypeScript, ESM.
* `package.json`: bin entry `prism-daemon`, dependencies (ws, OS-detect helpers, OTel SDK).
* `src/server.ts`: entry point. Spawns lifecycle FSM, IPC server, WS client, plugin loader.
* `src/fsm.ts`: lifecycle state machine (ADR-41). `DISCONNECTED → RECONCILING → CONNECTED → EMITTING` with explicit transition guards.
* `src/ws.ts`: WS client with reconnect-with-backoff + reconcile-on-reconnect.
* `src/ipc.ts`: Unix domain socket / Windows named pipe local control plane. Verbs: `status`, `notify_surface`, `resume_surface`, `shutdown`.
* `src/plugins.ts`: surface plugin loader (interface only; concrete plugins in `src/surfaces/*`).
* `src/metrics.ts`: OTel-compatible exporter for the 6 metrics in SPEC §Observability.
* `src/logger.ts`: structured logger emitting to OS-native sinks (Lafonda matrix §Logging).
* `src/types.ts`: shared types (`DaemonStatus`, `DoorbellPayload`, `PluginAPI`, etc.).

**Deliverable:** Single skeleton PR with stub implementations + tsc clean + minimal smoke (start/stop without errors).

### A3. Plugin contract scaffolding — Texi

**Goal:** Author the surface plugin SDK as a TypeScript interface set Codex-side and Claude Code-side both consume.

* Plugin API table from SPEC §Plugin contract codified as types.
* Codex shim plugin implementation (talks to existing MCP/Codex shim per ADR-43; daemon supervises shim, shim owns Codex protocol).
* Plugin self-contained: when daemon spawns the plugin, plugin handles its own surface I/O.

**Deliverable:** PR for Codex plugin + plugin SDK types. Cites SPEC-070 §Surface integrations. Off-limits to others.

### A4. Hold positions — Porsche, Lafonda

* **Porsche:** Hold live-wire PR until A2 emits stable wire shape + emit names. Slice-3 (drain-rate counter v0.3) is post-live-wire.
* **Lafonda:** Hold install lane until A2 binary is runnable. Pwsh parity validation needs Windows runner regardless.

### A5. Continuous — Desiree

* Capture each Phase A PR landing as a journal entry.
* Inconsistency-watch on every PR: daemon code MUST cite SPEC-070 v0.2 + relevant ADR. Surface drift immediately.
* `docs/*` refresh check on every observed commit per standing job.

## Phase B — Wire Up (after A1+A2 land)

### B1. Surface plugins (Donna for Claude Code, Texi for Codex completing)

* Claude Code plugin in `daemon/src/surfaces/claude_code.ts`.
* Codex plugin lands and integrates with the daemon binary spawn loop.
* End-to-end: daemon starts → loads plugin per surface arg → WS connects → push triggers `notify_surface` → plugin enqueues doorbell → operator sees the cue.

### B2. Metrics + logging — Donna

* Metrics emitter wired to OTel collector (mirrors `dashboard` consumption pattern).
* Structured logger with cross-OS sinks (`os_log` / `journalctl --user` / Event Viewer).
* Bounded `error_class` enum enforced — daemon refuses unknown classes, falls to `unclassified`.

### B3. Dashboard live wire — Porsche

* Flip `scraper.ts:tick()` from no-op to live OTel scrape.
* Update slice 1 cardinality sheet from 250 series to \~1200 series per SPEC §Cardinality.
* Slice 1 → slice 2 merge cascade after live wire ships.

### B4. Install lane — Lafonda

* `prism_persona_create` invokes the OS-native launcher register verb (`launchctl bootstrap` / `systemctl --user enable` / `schtasks /create`).
* Per-persona unit name: `prism-daemon-<tenant_slug>-<identity>.<ext>`.
* Linger enabled programmatically on Linux during install.
* `prism_persona_destroy` calls `shutdown` IPC verb, waits ≤5s, escalates to launcher process-kill on timeout.
* Pwsh parity validation on Windows runner when feasible.

## Phase C — Integration

### C1. End-to-end smoke

* Mac: daemon spawn → WS → backend push → doorbell → agent drain → metric ticks → panel renders.
* Linux: same on systemd-user-with-linger.
* Windows: same on Task Scheduler logon-trigger; pwsh parity validated.

### C2. Stress + soak

* 50 personas registered concurrently (v1 ceiling sizing target).
* 1200-series cardinality smoke against the dashboard.
* Sleep/wake cycle: drain queues correctly after host suspend, no doorbell storms.
* Stale-bell race: idempotent doorbell-after-drain (Porsche's postmortem-pending invariant).

## Out-of-scope for v1

* LAN listener / remote-control mode (deferred to a future SPEC).
* Dynamic multi-project persona attach/detach (deferred per ADR-42).
* Windows Service launcher (admin-required upgrade path; documented but not v1 default).
* v0.3 drain-rate counter metric (slice-3 candidate, post-live-wire).

## Coordination conventions

* **Branch + worktree pattern:** every lane uses `git worktree add` per memory `feedback_use_worktree_on_shared_apfs`. Branch names: `feat/spec-070-<scope>` or `feat/daemon-<scope>`.
* **Branch verification:** `git branch --show-current` at head of every commit chain per memory `feedback_branch_check_before_commit`.
* **Citation discipline:** every PR cites SPEC-070 v0.2 (spec\_row\_id `f8b17ef1-1657-4bf7-aa19-a909095cbd23`) + relevant ADR + relevant memory entries by id.
* **Engineering authority:** Donna for cross-lane decisions; Texi for Codex specifically; Lafonda for install lane; Porsche for dashboard. Surface tradeoffs that escape your lane to the cross-lane authority.
* **Scribe convention:** signal-the-scribe (Desiree) on every ratification, ADR filing, postmortem land, and binding decision-cluster per `feedback_signal_scribe_on_ratification`.
* **PO escalation:** phase boundaries (Phase A complete, Phase B complete) get Donna sign-off before next phase begins.

## Status board

Updated 2026-05-02 \~16:00Z — autonomous push-through session complete. **Phase A + most of Donna's Phase B lane shipped.**

| Phase              | Lane                                                                | Owner               | State                                                                                     | PR                                                     |
| ------------------ | ------------------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| A1.a               | Backend `agent_sessions.kind` migration                             | Donna               | **shipped**                                                                               | [#52](https://github.com/FrankTewksbury/Prism/pull/52) |
| A1.b               | `kind=daemon` param + skip master election                          | Donna               | **shipped** stacks on #52                                                                 | [#53](https://github.com/FrankTewksbury/Prism/pull/53) |
| A1.c.1             | SessionStore kind + `prism_status` separation                       | Donna               | **shipped** stacks on #53                                                                 | [#54](https://github.com/FrankTewksbury/Prism/pull/54) |
| A1.c.2             | `prism_signal_status` verb v0.1                                     | Donna               | **shipped** stacks on #54                                                                 | [#55](https://github.com/FrankTewksbury/Prism/pull/55) |
| A1.audit           | Routing-registry liveness probe                                     | Donna               | **shipped** independent                                                                   | [#56](https://github.com/FrankTewksbury/Prism/pull/56) |
| A2                 | Daemon binary skeleton + camelCase fold                             | Donna               | **shipped**                                                                               | [#51](https://github.com/FrankTewksbury/Prism/pull/51) |
| A2.live            | Real WS + IPC + register call                                       | Donna               | **shipped** stacks on #51                                                                 | [#57](https://github.com/FrankTewksbury/Prism/pull/57) |
| B2.metrics         | Prometheus `/metrics` endpoint replaces stub                        | Donna               | **shipped** stacks on #57                                                                 | [#58](https://github.com/FrankTewksbury/Prism/pull/58) |
| B2.reconcile       | Reconcile-on-reconnect (FSM `RECONCILING → CONNECTED`)              | Donna               | **shipped** stacks on #58                                                                 | [#59](https://github.com/FrankTewksbury/Prism/pull/59) |
| A3                 | Codex plugin SDK scaffolded in `mcp-node/src/daemon/plugins`        | Texi                | in flight                                                                                 | TBD                                                    |
| A4                 | Dashboard hold                                                      | Porsche             | holding (passive/reactive); slice 1+2 already shipped (PRs #47/#48)                       | —                                                      |
| A4                 | Install hold                                                        | Lafonda             | drafting templates + verb sketches; matrix #46 + cleanup #49 already shipped              | —                                                      |
| A5                 | Scribe + doc refresh                                                | Desiree             | continuous (journals #15–#42+)                                                            | —                                                      |
| B1.codex           | Codex surface plugin                                                | Texi                | **gated on A3 SDK + cross-lane shim coordination**                                        | —                                                      |
| B1.claude\_code    | Claude Code surface plugin                                          | Donna               | **gated on cross-lane mcp-node-IPC channel**                                              | —                                                      |
| B1.claude\_desktop | Claude Desktop fallback plugin                                      | Donna               | **gated on Desktop's surface-side notification path**                                     | —                                                      |
| B2.logs            | OS-native log sinks (`os_log` / `journalctl --user` / Event Viewer) | Lafonda recommended | install-lane lives best with the OS-native logger commands; surfacing the cross-lane move | —                                                      |
| B3                 | Dashboard live-wire PR                                              | Porsche             | gated on Texi emit-name commit (after A3)                                                 | —                                                      |
| B4                 | Install lane (per-OS launcher integration)                          | Lafonda             | gated on E2E daemon-runnable test against backend                                         | —                                                      |
| C1                 | E2E smoke                                                           | all                 | gated on B1 + B4                                                                          | —                                                      |
| C2                 | Stress + soak                                                       | all                 | gated on C1                                                                               | —                                                      |

### Donna lane is materially complete

What's left for Donna in implementation:

* `CONNECTED → EMITTING` FSM transition + actual doorbell-to-plugin dispatch (gated on surface plugin existing — cross-lane)
* v0.2 of `prism_signal_status` (split delivered/drained, add expired) — deferred until forcing function
* v0.2 of metrics emitter if drift surfaces

### Cross-lane coordination still needed

**Surface plugins** are the next gate. The plugin needs a daemon→shim IPC channel — daemon emits a doorbell; shim presents it to the agent. Current `mcp-node` doesn't have a "receive doorbell" endpoint. Texi's A3 plugin SDK is scaffolding the contract; full integration needs a small `mcp-node` change to accept doorbells over local IPC. That's cross-lane work between Donna (daemon) + Texi (mcp-node Codex shim).

This board is the source of truth for build progress. Update via PR to this doc as states change.
