1111 lines
71 KiB
Markdown
1111 lines
71 KiB
Markdown
# DECOMP-OPUS — Adversarial Task Decomposition (ROBUSTNESS SIDE)
|
||
|
||
**Author:** `planner-opus` (web1), robustness/pessimist lane.
|
||
**Inputs read:** `MISSION.md`, `MACP-WIRING-SCOUT.md`, `BOARD.md`, plus first-hand inspection of
|
||
`/src/mosaic-stack` (`packages/{macp,coord,db,queue,forge,mosaic}`, `apps/`,
|
||
`packages/mosaic/framework/tools/orchestrator-matrix/controller/mosaic_orchestrator.py`),
|
||
`~/.config/mosaic/tools/{git,tmux,lease-broker}/`, and
|
||
`jarvis-brain/docs/scratchpads/postmortem/REMEDIATION-DISCUSSION-STATE.md`.
|
||
**Not read (by charter):** `DECOMP-SOL.md`.
|
||
**Output:** 38 tasks, 8 dissents.
|
||
|
||
---
|
||
|
||
## 0. Operating premise of this decomposition
|
||
|
||
Three assumptions drive every ordering choice below. All three are load-bearing; if you reject one,
|
||
the DAG changes.
|
||
|
||
**A1 — Every gate is inert until proven otherwise.** Gate-6 (`ci-queue-wait.sh`) passed green
|
||
fleet-wide while classifying `unknown` → `exit 0`, and its fix recursed the same defect. I verified
|
||
the defect is still live at `~/.config/mosaic/tools/git/ci-queue-wait.sh:282-288`:
|
||
|
||
```
|
||
terminal-success|terminal-failure|unknown) exit 0 # "unknown" == pass
|
||
*) echo "unrecognized state ... proceeding conservatively"; exit 0
|
||
```
|
||
|
||
`no-status` also exits 0 unless `--require-status` is passed, and nothing passes it
|
||
(`pr-merge.sh:98`). A gate whose failure path is unreachable is worse than no gate: it manufactures
|
||
evidence. **Therefore: no task in this plan may introduce a gate without a registered
|
||
must-exit-nonzero negative control, and the registry check itself is a gate (R-002).**
|
||
|
||
**A2 — Fail-open is the resting state of this system.** Confirmed instances, all live today:
|
||
|
||
| Site | Failure mode |
|
||
| ----------------------------------------- | --------------------------------------------------------------- |
|
||
| `ci-queue-wait.sh:282,286` | unknown/unrecognized CI state → proceed |
|
||
| `send-message.sh:111` | indeterminate submission → `✓ sent`, exit 0 |
|
||
| `packages/forge/src/cli.ts:13-31` | stub `TaskExecutor` reports completion with **empty gate list** |
|
||
| `lease-broker/mutator-gate.py:140` | broker socket unset → `return 0` (all tool calls allowed) |
|
||
| `lease-broker/revoke-lease.py:57` | broker socket unset → `return 0` (lease never revoked) |
|
||
| `lease-broker/receipt-observer-client.py` | entire client replaced by a `sys.exit(0)` stub |
|
||
|
||
Four of six are _silent_. Every task below names its fail-closed point explicitly (§4).
|
||
|
||
**A3 — The substrate is real, but "wire it in" understates the work.** `packages/db` already has
|
||
`missions`, `tasks`, `mission_tasks`, `agents`, `interaction_{sessions,inbox,outbox,checkpoints,handoffs}`
|
||
with idempotency-key unique indexes and a compaction-epoch column. `packages/queue` has a
|
||
`QueueAdapter` with bullmq + local adapters. That is genuinely more than the charter credits.
|
||
But see §6 — roughly a third of this plan is greenfield regardless of the slogan.
|
||
|
||
---
|
||
|
||
## 1. Phase map (ordered; hard barriers between phases marked ⛔)
|
||
|
||
```
|
||
P0 Make gates provable + stop the fleet re-bricking R-001..R-005
|
||
⛔ barrier: no gate-introducing task may merge before R-002
|
||
P1 Durable spine (PG) R-010..R-013
|
||
⛔ barrier: no migration may merge before R-010
|
||
P2 Single choke-point executor (MACP wire-in) R-020..R-030
|
||
⛔ barrier: R-025 (no-second-path) must land in the same milestone as R-020
|
||
P3 Rotation lifecycle (Mission Control Plane, finished) R-040..R-045
|
||
⛔ barrier: R-045 (delete /compact guidance) may not merge before R-042 is live
|
||
P4 Comms service R-050..R-055
|
||
⛔ barrier: R-060 (one socket per host) must precede R-052 identity addressing
|
||
P5 Retirements, hygiene, conformance R-060..R-065
|
||
```
|
||
|
||
**Deliberate divergence from `MISSION.md` sequencing:** the charter starts at "spine + choke-point
|
||
(builds 1+2)". I insert P0 ahead of it. Rationale in Dissent D1.
|
||
|
||
---
|
||
|
||
## 2. Master task table
|
||
|
||
`tier` = suggested runtime. `codex` = mechanical, spec is unambiguous. `sonnet` = normal feature work.
|
||
`opus` = security/integrity/design-ambiguity or cross-cutting invariant.
|
||
|
||
| id | title | build | depends_on | est tok | tier |
|
||
| ----- | ---------------------------------------------------------------------------------------- | ----- | -------------------------- | ------- | ------ |
|
||
| R-001 | Reproducible checkout + pre-push gate that fails on code, not env | 5 | — | 60k | codex |
|
||
| R-002 | Gate registry + negative-control CI check (anti-inert-gate harness) | 5 | R-001 | 120k | opus |
|
||
| R-003 | Queue-guard fail-closed rework (`unknown`/`no-status`/malformed) | 1 | R-002 | 100k | sonnet |
|
||
| R-004 | Break-glass replaces the three silent `MOSAIC BYPASS` fail-opens | 1 | R-005, R-002 | 120k | opus |
|
||
| R-005 | Activation/version coherence: block launch on host↔repo skew, fail SAFE | 5 | R-001 | 140k | sonnet |
|
||
| R-010 | Fix the Drizzle postgres-tier first-install migration defect | 2 | R-001 | 90k | sonnet |
|
||
| R-011 | Orchestration spine schema (macp_tasks, attempts, gate_results, ledger, claims) | 2 | R-010 | 160k | opus |
|
||
| R-012 | Spine client with fail-closed connection semantics (no silent PGlite in prod) | 2 | R-011 | 80k | sonnet |
|
||
| R-013 | Transactional outbox tables + reconciliation sweeper (PG-first) | 2 | R-012 | 140k | opus |
|
||
| R-020 | Production `TaskExecutor` backed by `@mosaicstack/macp` | 1 | R-012, R-002 | 220k | opus |
|
||
| R-021 | Gate-runner hardening: structured `GateEntry`, `fail_on`, timeout, empty-gate-set = fail | 1 | R-020 | 120k | sonnet |
|
||
| R-022 | Hash-chained append-only MACPEvent ledger in PG; lifecycle EventType extension | 1 | R-020, R-011 | 160k | opus |
|
||
| R-023 | `packages/coord` submits work through the executor (retire direct spawn) | 1 | R-020 | 140k | sonnet |
|
||
| R-024 | `mosaic yolo\|claude\|codex\|pi` launch path records a typed Task + events | 1 | R-020, R-022 | 160k | sonnet |
|
||
| R-025 | No-second-path gate: terminal status writable only by the executor | 1 | R-020, R-022 | 140k | opus |
|
||
| R-026 | Delete the Forge stub executor; Forge submits through the real one | 1 | R-020 | 90k | codex |
|
||
| R-027 | Seat identity from `MOSAIC_AGENT_NAME` + mandatory tri-state write outcomes | 1 | R-020 | 150k | opus |
|
||
| R-028 | Typed state claims (source/confidence/TTL) with HMAC integrity, fail-closed | 1 | R-011, R-020 | 170k | opus |
|
||
| R-029 | Contract-hash binding; stale generation loses mutation authority mechanically | 1 | R-020, R-028 | 180k | opus |
|
||
| R-030 | Retire the Python controller + `plugins/macp` island (island count 3 → 1) | 1 | R-023, R-024, R-025, R-026 | 110k | codex |
|
||
| R-040 | Durable compaction/token sensor (per-runtime thresholds, PreCompact event) | 3 | R-022, R-029 | 130k | sonnet |
|
||
| R-041 | Typed checkpoint writer (structured claims, not transcript) + digest | 3 | R-028, R-040 | 150k | opus |
|
||
| R-042 | Rotation daemon: watch → checkpoint → kill → relaunch → rehydrate | 3 | R-041, R-023 | 240k | opus |
|
||
| R-043 | Rehydration attestation gate: refuse to act on an incomplete claim set | 3 | R-041 | 130k | opus |
|
||
| R-044 | Fault-injected rotation soak (100× incl. kill-at-worst-moment) | 3 | R-042, R-043 | 180k | sonnet |
|
||
| R-045 | Delete `/compact and continue` from the persistent-seat path (substitution) | 3 | R-042, R-044 | 60k | codex |
|
||
| R-050 | `comms/v1` envelope + protocol-version negotiation, LOUD reject | 4 | R-011 | 140k | opus |
|
||
| R-051 | Comms service: PG state machine PENDING→RECEIVED→CONSUMED→DEAD-LETTER | 4 | R-050, R-013 | 200k | opus |
|
||
| R-052 | tmux transport adapter behind the service; durable retry before cursor advance | 4 | R-051, R-060 | 160k | sonnet |
|
||
| R-053 | Per-class coalescing + supersede (stale-consumed-as-live fix) | 4 | R-051 | 130k | sonnet |
|
||
| R-054 | Redis hot path + provenance guard (Redis may never be read as authority) | 4 | R-051, R-013 | 170k | opus |
|
||
| R-055 | Retire direct tmux sends; only the service may write a pane | 4 | R-052, R-053 | 100k | codex |
|
||
| R-060 | One roster-owned socket/host, quarantine unmanaged, stale-session GC | 5 | R-005 | 150k | sonnet |
|
||
| R-061 | Auto-sync allowlist (never auto-stage unknown paths) + worktree isolation | 5 | R-002 | 110k | sonnet |
|
||
| R-062 | Flat-file orchestration tracking → DB cutover, with rollback artifact | 2/5 | R-024, R-028, R-042 | 200k | opus |
|
||
| R-063 | Conformance harness: fault injection of the six live failure classes | 5 | R-044, R-051, R-062 | 260k | opus |
|
||
| R-064 | Fleet-wide inert-gate audit against the R-002 registry | 5 | R-002 | 120k | sonnet |
|
||
| R-065 | Retirement proof: CI asserts all three retirements are complete and stay complete | 5 | R-030, R-055, R-062 | 90k | codex |
|
||
|
||
**Totals:** 38 tasks, ~5.3M estimated worker tokens. Critical path (longest chain):
|
||
`R-001 → R-010 → R-011 → R-012 → R-020 → R-022 → R-029 → R-040 → R-041 → R-042 → R-044 → R-062 → R-063`
|
||
= 13 tasks, ~2.2M tokens. Everything else parallelizes against it.
|
||
|
||
---
|
||
|
||
## 3. Task detail
|
||
|
||
Acceptance criteria are written **diff-blind**: each is a command a reviewer can pre-register and run
|
||
against the merged branch without having read the implementation. `⇒0` / `⇒≠0` denote required exit
|
||
codes. Every gate-introducing task carries at least one **must-fail** case — that is the whole point.
|
||
|
||
### P0 — Make gates provable
|
||
|
||
---
|
||
|
||
**R-001 — Reproducible checkout + pre-push gate that fails on code, not env**
|
||
_build 5 · depends: — · 60k · codex_
|
||
|
||
The mosaic-stack checkout on web1 cannot push: `pre-push` typecheck fails on `Cannot find module
|
||
commander/@mosaicstack/*` because deps were never installed. A gate that reds on environment is
|
||
indistinguishable from a gate that reds on defect, so the fleet learns to bypass it. This is the
|
||
mission's own TASK-0 and it blocks literally every other PR.
|
||
|
||
- AC1: from a clean clone in the CI container, `pnpm install --frozen-lockfile && pnpm -w typecheck` ⇒0.
|
||
- AC2: `scripts/preflight.sh` (or equivalent named in the PR) run with node_modules removed ⇒≠0 with a
|
||
message containing `run pnpm install`, and ⇒0 after install. **Negative control: the missing-deps
|
||
case must be distinguishable by exit code or a machine-greppable marker from a real type error.**
|
||
- AC3: introduce `export const x: number = "s"` in any package → gate ⇒≠0. Revert → ⇒0.
|
||
- AC4: `git status --porcelain` in a fresh clone after a full build is empty (no generated residue).
|
||
- Dogfood: live finding 2026-07-31 — "pre-push gate red on env not code"; `.bak`/`.mosaic-bak` and
|
||
untracked `apps/coordinator` residue in the checkout.
|
||
- Fail-closed point: none introduced; this restores the _ability_ to fail closed.
|
||
|
||
---
|
||
|
||
**R-002 — Gate registry + negative-control CI check (anti-inert-gate harness)** ★ keystone
|
||
_build 5 · depends: R-001 · 120k · opus_
|
||
|
||
The generalization of P-QUEUE-001. A machine-readable registry (`gates.manifest.json` or equivalent)
|
||
where every deterministic gate in the repo declares: invocation, the input classes it must
|
||
distinguish, and for **each** class the required exit code. A CI job runs every declared case and
|
||
fails if (a) any must-fail case exits 0, (b) any gate in the tools tree is absent from the registry,
|
||
or (c) a gate has zero must-fail cases registered.
|
||
|
||
- AC1: `pnpm gate:verify` ⇒0 on main.
|
||
- AC2: mutate any registered gate's failure branch to `exit 0` → `pnpm gate:verify` ⇒≠0 naming that
|
||
gate. **This is the meta-negative-control; a reviewer must run it.**
|
||
- AC3: add a new executable under the gates path without a registry entry → ⇒≠0 with `unregistered gate`.
|
||
- AC4: registry entry with zero must-fail cases → ⇒≠0 with `no negative control`.
|
||
- AC5: the check runs in CI on every PR, not only on gate-file changes (an inert gate is usually made
|
||
inert by a change _elsewhere_, e.g. an env var going unset).
|
||
- Dogfood: gate-6 inert fleet-wide; #1019 fix (PR #1023 @f6334080) recursed the same defect —
|
||
rev-974 review id 58 finding (3): "tests don't assert exit outcomes."
|
||
- Fail-closed point: **unregistered gate ⇒ build fails.** Not "warn".
|
||
- Secretly greenfield: yes, small. Accept it — it is the cheapest insurance in the plan.
|
||
|
||
---
|
||
|
||
**R-003 — Queue-guard fail-closed rework**
|
||
_build 1 · depends: R-002 · 100k · sonnet_
|
||
|
||
`ci-queue-wait.sh` must stop treating `unknown`, unrecognized, and `no-status` as pass. Spec is
|
||
rev-974 review id 58: (1) `unknown` must not exit 0; (2) payload via stdin/temp file, never argv
|
||
(ARG_MAX rc126 at ~150 KiB); (3) tests assert exit outcomes.
|
||
|
||
- AC1: synthetic status payloads for each class ⇒ `pending` (loops then times out ⇒124),
|
||
`terminal-success` ⇒0, `terminal-failure` ⇒≠0, `no-status` ⇒≠0, `malformed JSON` ⇒≠0,
|
||
`unknown` ⇒≠0. Each is a registered R-002 case.
|
||
- AC2: a 150 KiB status payload ⇒ not 126, and classification unchanged.
|
||
- AC3: `--force-proceed` (if provided) requires a reason, emits a WARN to stderr, and is a registered
|
||
break-glass per R-004 — never a silent default.
|
||
- AC4: `pr-merge.sh` invokes the guard with `--require-status`; removing that flag ⇒ R-002 fails.
|
||
- Dogfood: gate-6 inert + #1019 recursion.
|
||
- Fail-closed point: **every non-green classification blocks the merge.**
|
||
- ⚠ **Authority conflict:** #1019/PR #1023 is explicitly PARKED under Mos ("delivery stack still
|
||
parked"), while Build 1 absorbs QUEUE and HOLD is lifted for this workstream. The orchestrator must
|
||
resolve ownership with Mos _before dispatch_, or two lanes will rework the same branch. Flagged, not
|
||
assumed.
|
||
|
||
---
|
||
|
||
**R-004 — Break-glass replaces the three silent `MOSAIC BYPASS` fail-opens**
|
||
_build 1 · depends: R-005, R-002 · 120k · opus_
|
||
|
||
Convert `mutator-gate.py:140`, `revoke-lease.py:57`, and the `receipt-observer-client.py` stub from
|
||
permanent silent fail-open into LOUD + AUDITED + TEMPORARY break-glass: explicit opt-in token with an
|
||
expiry timestamp, a WARN on stderr every invocation, an audit line written on every use, and refusal
|
||
once expired.
|
||
|
||
- AC1: with break-glass unset and the broker absent → gate ⇒≠0 (denies) with `GATE_UNAVAILABLE`.
|
||
- AC2: with a valid unexpired break-glass token → ⇒0, **and** an audit record exists containing seat,
|
||
reason, expiry, and invocation count.
|
||
- AC3: with an expired token → ⇒≠0. (Registered must-fail case.)
|
||
- AC4: `grep -rn "MOSAIC BYPASS" ~/.config/mosaic/tools/` returns nothing after deploy; a CI check
|
||
fails if the marker reappears without a matching registry entry.
|
||
- AC5: the Stop-hook client never blocks a turn — observational hooks exit 0 on transport failure
|
||
**but record a delivery-failure event**; silence is the bug, not the exit code.
|
||
- Dogfood: Pi brick (fail-closed gate whose recovery needed the gate it was blocked by) + the
|
||
2026-07-22 fleet incident that motivated all three bypasses.
|
||
- Fail-closed point: **broker absent + no break-glass = deny.**
|
||
- ⚠ **Ordering hazard (severe):** merging R-004 before R-005 re-bricks the fleet exactly as on
|
||
2026-07-22. The bypasses exist because the broker daemon was never _deployed_ on this host, not
|
||
because the code was wrong. Removing the fail-open before deployment coherence is real recreates the
|
||
incident. **Hard edge: R-005 → R-004.**
|
||
|
||
---
|
||
|
||
**R-005 — Activation/version coherence: block launch on host↔repo skew, fail SAFE**
|
||
_build 5 · depends: R-001 · 140k · sonnet_
|
||
|
||
P-ACTIVATION-001. Transactional install of CLI + hooks + broker + version; launch refuses to start a
|
||
seat whose deployed framework version disagrees with the repo/roster expectation. "Fail SAFE" per
|
||
Jason's caveat = clear message + exact fix command, not a bare hard stop.
|
||
|
||
- AC1: with matching versions, `mosaic claude` launches ⇒0.
|
||
- AC2: with a deliberately skewed deployed version, launch ⇒≠0 and stdout contains both versions and a
|
||
copy-pasteable remediation command. (Registered must-fail case.)
|
||
- AC3: interrupted install (kill mid-way) leaves the previous version fully functional — no
|
||
half-installed hook set. Verified by: install, `kill -9` at a scripted point, then run the previous
|
||
version's smoke test ⇒0.
|
||
- AC4: `mosaic doctor` reports broker deployed/undeployed **honestly** — the label must change when
|
||
the daemon is stopped. (Direct fix for P-RECOVERY-001's "honest capability labeling".)
|
||
- Dogfood: the 2026-07-22 incident root cause ("#828 lease-broker daemon + launch-time env injection
|
||
were never deployed on this host"); the messy-checkout finding.
|
||
- Fail-closed point: **version skew blocks launch.**
|
||
|
||
### P1 — Durable spine
|
||
|
||
---
|
||
|
||
**R-010 — Fix the Drizzle postgres-tier first-install migration defect** ★ hidden blocker
|
||
_build 2 · depends: R-001 · 90k · sonnet_
|
||
|
||
`packages/db/src/migrate.ts:30-38` carries a TODO admitting that **postgres-tier first-install fails
|
||
today**: Drizzle wraps all migrations in one transaction (breaking 0009's `ALTER TYPE ADD VALUE` →
|
||
`SET DEFAULT` sequence) and `drizzle/meta/_journal.json` orders 0009 before 0008, which the
|
||
postgres-js migrator silently skips by `created_at < folderMillis`. The PGlite path sidesteps both —
|
||
which means the spine has only ever been proven on the embedded tier.
|
||
|
||
- AC1: against a **fresh empty** Postgres 16 container, `runMigrations()` ⇒0 and every table in
|
||
`schema.ts` exists (assert by count and by name list).
|
||
- AC2: `drizzle.__drizzle_migrations` contains one row per migration file, in file order.
|
||
- AC3: re-running `runMigrations()` on the migrated DB ⇒0 and is a no-op (row count unchanged).
|
||
- AC4: a deliberately corrupted journal ⇒≠0 (must not silently skip). Registered must-fail case.
|
||
- Dogfood: synthetic — no live incident yet, because nobody has first-installed the PG tier. That is
|
||
precisely the danger: the first real deploy is the discovery event.
|
||
- Fail-closed point: **skipped migration ⇒ error, never silent skip.**
|
||
- ⚠ **Ordering hazard:** every later task that adds a migration (R-011, R-013, R-022, R-028, R-050,
|
||
R-051) silently depends on this. Without it they pass on PGlite in CI and fail on the real deploy.
|
||
|
||
---
|
||
|
||
**R-011 — Orchestration spine schema**
|
||
_build 2 · depends: R-010 · 160k · opus_
|
||
|
||
New tables, **not** overloads of existing ones: `macp_tasks` (typed MACP `Task`), `task_attempts`,
|
||
`gate_results`, `macp_events` (append-only, monotonic sequence, `prev_hash`/`hash` chain),
|
||
`state_claims` (typed claim with source/confidence/TTL + HMAC), `seat_identities`,
|
||
`contract_bindings`. Reuse `missions`/`mission_tasks` for mission linkage.
|
||
|
||
- AC1: migration applies to fresh PG ⇒0 (per R-010 AC1).
|
||
- AC2: `UPDATE macp_events SET ...` or `DELETE FROM macp_events` ⇒ error (append-only enforced by
|
||
trigger/rule or role grants, not convention). Registered must-fail case.
|
||
- AC3: inserting an event whose `prev_hash` does not match the current head ⇒ error.
|
||
- AC4: a verification function reports gap/tamper on an artificially altered chain ⇒≠0.
|
||
- AC5: `state_claims` rows carry a non-null TTL; a query helper returns expired claims as _expired_,
|
||
never as valid.
|
||
- Dogfood: P-AUDIT-001's duplicate ledger (`.mosaic/orchestrator/events.ndjson` written by
|
||
`mosaic_orchestrator.py:129-133` and only _read_ by `plugins/mosaic-framework`).
|
||
- Fail-closed point: **ledger mutation attempt = hard DB error.**
|
||
- Design note (deliberate divergence): the existing `events` table (`schema.ts:264`) is a generic
|
||
`type/title/description/date` record — a calendar-shaped table. Overloading it as the audit ledger
|
||
would defeat append-only enforcement. Separate table.
|
||
|
||
---
|
||
|
||
**R-012 — Spine client with fail-closed connection semantics**
|
||
_build 2 · depends: R-011 · 80k · sonnet_
|
||
|
||
`packages/db` exposes both `createDb` (postgres) and `createPgliteDb` (embedded). If production can
|
||
ever silently land on PGlite, two seats will happily write to two different "systems of record."
|
||
|
||
- AC1: with `NODE_ENV=production` and no `DATABASE_URL`, client construction ⇒ throws (not a fallback).
|
||
Registered must-fail case.
|
||
- AC2: with `DATABASE_URL` set to an unreachable host, the first operation ⇒ throws within the
|
||
configured timeout; no in-memory substitute is created.
|
||
- AC3: PGlite construction in production requires an explicit `MOSAIC_ALLOW_EMBEDDED_DB=1`; absent it,
|
||
⇒ throws.
|
||
- AC4: a health endpoint/CLI reports which tier is in use, and the string differs between tiers.
|
||
- Dogfood: synthetic (the split-brain this prevents has not happened yet _because_ the spine is not
|
||
wired — it would happen on week one of Build 2).
|
||
- Fail-closed point: **no DB ⇒ no work.** The choke point must not degrade to local files.
|
||
|
||
---
|
||
|
||
**R-013 — Transactional outbox + reconciliation sweeper**
|
||
_build 2 · depends: R-012 · 140k · opus_
|
||
|
||
PG commit first, then enqueue. A sweeper deterministically re-enqueues rows that committed in PG but
|
||
never reached the hot queue. `interaction_outbox` already has the right shape
|
||
(`idempotency_key` unique per session, status enum, `content_digest`) — reuse it rather than adding a
|
||
fourth queue concept.
|
||
|
||
- AC1: kill the process between PG commit and enqueue → sweeper re-enqueues within N seconds; the
|
||
consumer sees the message exactly once (idempotency key enforced).
|
||
- AC2: enqueue the same idempotency key twice → one delivery, one PG row. Registered must-fail case is
|
||
the inverse: a duplicate delivery ⇒ test fails.
|
||
- AC3: with Redis unavailable, writes still commit to PG and are marked `pending`; nothing is lost and
|
||
nothing reports delivered.
|
||
- AC4: sweeper is idempotent — running it twice concurrently produces no duplicates (advisory lock).
|
||
- Dogfood: MACP scout notification BOUNCE ("tmux target not found") — never RECEIVED, no retry, found
|
||
only by manual liveness check.
|
||
- Fail-closed point: **an un-acked message is never marked delivered.**
|
||
|
||
### P2 — The single choke point
|
||
|
||
---
|
||
|
||
**R-020 — Production `TaskExecutor` backed by `@mosaicstack/macp`** ★ keystone
|
||
_build 1 · depends: R-012, R-002 · 220k · opus_
|
||
|
||
One Node executor: validate a typed `Task` (schemas exist at `packages/macp/src/schemas`), call
|
||
`resolveCredentials`, run the process, call `runGates`, emit `MACPEvent`s, persist a `TaskResult`.
|
||
Exposed as a programmatic API **and** a CLI. Note the scout's observation that there is no exported
|
||
programmatic `submit` — that is this task.
|
||
|
||
- AC1: submitting a Task that fails schema validation ⇒≠0 before any process spawn; assert no child
|
||
process was created and no event row exists. Registered must-fail case.
|
||
- AC2: a Task whose provider credentials cannot be resolved ⇒≠0 with `CredentialError`; **assert the
|
||
credential value never appears in stdout, stderr, the event ledger, or the task row.**
|
||
- AC3: a successful Task produces, in order: `task.assigned`, `task.started`, `rail.check.*` per gate,
|
||
`task.completed` — all in `macp_events`, chain-valid.
|
||
- AC4: a Task whose gate fails is recorded `failed`/`gated`, never `completed`.
|
||
- AC5: killing the executor mid-run leaves the task in a non-terminal state that the sweeper can
|
||
reclaim — **never** `completed`.
|
||
- AC6: two executors racing on the same task id → exactly one runs (row-level claim).
|
||
- Dogfood: the entire "stranded MACP" finding — zero production calls to
|
||
`runGates`/`emitEvent`/`resolveCredentials`.
|
||
- Fail-closed points: schema-invalid ⇒ reject; credentials unresolved ⇒ reject; crash ⇒ non-terminal.
|
||
- ⚠ **I dissent from the scout's chosen wire-in point.** See Dissent D2 — wiring into
|
||
`mosaic_orchestrator.py::run_single_task` puts the choke point inside a controller that is
|
||
`"enabled": false`. Wire the live path instead (R-023/R-024) and delete the Python rail (R-030).
|
||
|
||
---
|
||
|
||
**R-021 — Gate-runner hardening**
|
||
_build 1 · depends: R-020 · 120k · sonnet_
|
||
|
||
The Python rail runs raw string gates (`mosaic_orchestrator.py:213-235`) with no `type`/`fail_on`
|
||
support. The package runner supports structured `GateEntry`. Hardening: honor `fail_on`, enforce
|
||
per-gate timeouts distinct from the task timeout, and **treat an empty gate list as failure for
|
||
gate-required task types** rather than as vacuous success.
|
||
|
||
- AC1: `quality_gates: []` on a `coding` task ⇒ task not `completed`; error names `no gates
|
||
configured`. Registered must-fail case — this is the Forge-stub bug generalized.
|
||
- AC2: a gate exceeding its timeout ⇒ `timed_out: true` and `passed: false` (never `passed: true`).
|
||
- AC3: `fail_on: blocker` with 3 non-blocking findings ⇒ pass; with 1 blocker ⇒ fail.
|
||
- AC4: a gate command that does not exist (rc 127) ⇒ fail, not skip.
|
||
- Dogfood: `packages/forge/src/cli.ts:13-31` stub executor "immediately reports completion with empty
|
||
gates" — an inert gate by construction.
|
||
- Fail-closed point: **absent/unrunnable gates fail the task.**
|
||
|
||
---
|
||
|
||
**R-022 — Hash-chained MACPEvent ledger + lifecycle EventType extension**
|
||
_build 1 · depends: R-020, R-011 · 160k · opus_
|
||
|
||
Emitter targets PG (via R-011), not a caller-supplied NDJSON path. Extend `EventType` from
|
||
task-centric to lifecycle-first-class: `session.launched`, `mission.generated`,
|
||
`compaction.detected`, `session.rotated`, `recovery.started`, `breakglass.used`,
|
||
`inbox.received`, `inbox.consumed`, `terminal.disposition`. Executor-emitted, so lines are identical
|
||
across Claude/Codex/Pi.
|
||
|
||
- AC1: run the same Task under two runtimes → event sequences are byte-identical modulo
|
||
`event_id`/`timestamp`/`source`. **This is the runtime-neutrality claim; assert it, don't assume it.**
|
||
- AC2: chain verification ⇒0 on a healthy ledger, ⇒≠0 after any row alteration. Registered must-fail case.
|
||
- AC3: an emit that fails to persist ⇒ the operation it describes fails (no fire-and-forget logging on
|
||
the authority path).
|
||
- AC4: `grep -rn "events.ndjson"` in production paths returns nothing (the duplicate island is gone —
|
||
enforced permanently by R-065).
|
||
- Dogfood: P-AUDIT-001 duplicate Python ledger; `#1018` reconstruction required manual archaeology.
|
||
- Fail-closed point: **unpersistable event ⇒ operation fails.** (Yes, this trades availability for
|
||
auditability. That is the decision on record.)
|
||
|
||
---
|
||
|
||
**R-023 — `packages/coord` submits through the executor**
|
||
_build 1 · depends: R-020 · 140k · sonnet_
|
||
|
||
`packages/coord/src/runner.ts:397-427` spawns a child process directly and tracks tasks in
|
||
`docs/TASKS.md` + `.mosaic/orchestrator/mission.json`. Replace the spawn with an executor submit;
|
||
keep coord's session-lock/crash-recovery, which are good and already durable.
|
||
|
||
- AC1: `mosaic coord run` on a fixture mission produces `macp_tasks` + `macp_events` rows; no direct
|
||
`spawn` of the runtime remains in `runner.ts` (assert by AST/grep in the R-025 check).
|
||
- AC2: crash recovery still works — kill mid-task, resume, and the task is reclaimed exactly once.
|
||
- AC3: coord no longer _writes_ `docs/TASKS.md` as authority (read-only mirror at most, per R-062).
|
||
- Dogfood: coord/macp "disconnected islands" finding.
|
||
- Fail-closed point: **coord cannot complete a task the executor did not run.**
|
||
|
||
---
|
||
|
||
**R-024 — Launch path records a typed Task**
|
||
_build 1 · depends: R-020, R-022 · 160k · sonnet_
|
||
|
||
`packages/mosaic/src/commands/launch.ts:730-843,1102-1167` dispatches `mosaic yolo|claude|codex|pi`
|
||
straight to harness launch with no Task, no gates, no events. This is the path humans and the fleet
|
||
actually use — leaving it outside the choke point leaves the choke point decorative.
|
||
|
||
- AC1: `mosaic yolo <runtime> …` creates a `macp_tasks` row and `session.launched` event before the
|
||
harness starts.
|
||
- AC2: the launch is refused if the spine is unreachable (per R-012). Registered must-fail case.
|
||
- AC3: seat identity is bound at launch and present on every subsequent event from that seat.
|
||
- AC4: an interactive/ephemeral launch class is supported explicitly and is _labeled_ as ungated —
|
||
honest capability labeling, not a silent hole.
|
||
- Dogfood: "direct `mosaic yolo|claude|codex|opencode|pi` also bypasses it" (scout, §2).
|
||
- Fail-closed point: **no spine ⇒ no launch** (for managed seats).
|
||
|
||
---
|
||
|
||
**R-025 — No-second-path gate** ★ the invariant that makes R-020 mean anything
|
||
_build 1 · depends: R-020, R-022 · 140k · opus_
|
||
|
||
A choke point is only a choke point if nothing else can write a terminal outcome. Two layers:
|
||
(1) DB — only the executor's role may insert/transition to terminal status; (2) CI — a static check
|
||
that fails the build if any file outside the executor writes terminal task status, appends to the
|
||
ledger, or spawns a runtime process directly.
|
||
|
||
- AC1: a test that connects as a non-executor role and attempts to set `status='completed'` ⇒ error.
|
||
Registered must-fail case.
|
||
- AC2: add a file that writes terminal status → CI ⇒≠0 naming the file. **A reviewer must actually add
|
||
such a file and watch it fail.**
|
||
- AC3: the check enumerates its own coverage (which paths it scanned) so an empty scan is visible —
|
||
an inert _checker_ is the same disease.
|
||
- AC4: known-legitimate exceptions live in an explicit allowlist with a reason string; an empty-reason
|
||
entry ⇒≠0.
|
||
- Dogfood: three parallel MACP islands, each able to declare a task complete.
|
||
- Fail-closed point: **unknown writer ⇒ build fails / DB rejects.**
|
||
- ⚠ **Ordering hazard:** if R-025 lands a milestone _after_ R-020, the intervening period is when a
|
||
fourth island gets built, and R-025 then arrives as a large adversarial refactor nobody wants to
|
||
merge. Ship them together.
|
||
|
||
---
|
||
|
||
**R-026 — Delete the Forge stub executor**
|
||
_build 1 · depends: R-020 · 90k · codex_
|
||
|
||
- AC1: `mosaic forge run` on a fixture pipeline produces real gate results; a fixture with a failing
|
||
gate ⇒≠0. Registered must-fail case (today it would exit 0 with empty gates).
|
||
- AC2: `grep -n "stub" packages/forge/src/cli.ts` returns nothing; type-only MACP imports become value
|
||
imports.
|
||
- Dogfood: `packages/forge/src/cli.ts:13-31,167,185`.
|
||
- Fail-closed point: **no executor injected ⇒ forge refuses to run** (not "runs with a stub").
|
||
|
||
---
|
||
|
||
**R-027 — Seat identity + mandatory tri-state write outcomes**
|
||
_build 1 · depends: R-020 · 150k · opus_
|
||
|
||
Two halves of P-WRAPPER-001. (a) Identity derives from `MOSAIC_AGENT_NAME` at launch, survives
|
||
respawn, resolved through the executor's credential binding, fail-closed. (b) Every write/send
|
||
wrapper returns exactly one of `written+verified` / `written+unverified` / `not-written`, with distinct
|
||
exit codes. Today `send-message.sh:111` prints `✓ sent … (submission state indeterminate)` and
|
||
**exits 0** — a written+unverified reported as success, the discarded-measurement anti-pattern.
|
||
|
||
- AC1: respawn a seat pane; the seat's identity is unchanged and `MOSAIC_GIT_IDENTITY` resolves
|
||
without being inlined by hand. (rev-974's respawn dropped it → token-not-found → could not post.)
|
||
- AC2: with identity unresolvable, the wrapper ⇒≠0 and performs no write. Registered must-fail case.
|
||
- AC3: force an indeterminate submission (target pane exists, no verification possible) ⇒ exit code
|
||
distinct from both success and hard failure, and the string `unverified` on stderr.
|
||
- AC4: a caller that treats any nonzero as success is caught by an R-002 registry case.
|
||
- AC5: safe target metadata (repo / id / head SHA) is named in the wrapper contract and verified by
|
||
construction — a review posted to the wrong SHA ⇒≠0.
|
||
- Dogfood: rev-974 identity drop (OpenBrain 33bef845); pepper written+verified; the
|
||
written+unverified silent-success class.
|
||
- Fail-closed point: **cannot verify ⇒ never report success.**
|
||
|
||
---
|
||
|
||
**R-028 — Typed state claims with HMAC integrity**
|
||
_build 1 · depends: R-011, R-020 · 170k · opus_
|
||
|
||
Replace the prose-blob checkpoint with typed claims: `{key, value, source, confidence, ttl,
|
||
refreshed_at}`, signed. A corrupt or unsigned claim set refuses to rehydrate and forces refresh.
|
||
|
||
- AC1: write a claim set, flip one byte, attempt rehydrate ⇒≠0 with `integrity`. Registered must-fail case.
|
||
- AC2: an expired claim is returned as expired and cannot satisfy a required-claim check.
|
||
- AC3: an unsigned claim set (legacy/prose) ⇒ refuse, with a named migration path.
|
||
- AC4: the HMAC key is resolved through the credential path and never appears in any output.
|
||
- Dogfood: `MOS-ORCHESTRATION-BOARD-LIVE.md` is the model-maintained manual prototype of this;
|
||
Jason's "assuming data not corrupted" is the requirement being mechanized.
|
||
- Fail-closed point: **corrupt checkpoint ⇒ refuse-rehydrate + force refresh.**
|
||
|
||
---
|
||
|
||
**R-029 — Contract-hash binding; stale generation loses authority**
|
||
_build 1 · depends: R-020, R-028 · 180k · opus_
|
||
|
||
Session binds a hash of (Constitution + AGENTS + runtime contract + skill set) at launch **and** at
|
||
rotation. On policy change or compaction-detected, the seat must re-attest before acting. The executor
|
||
refuses mutations carrying a stale generation.
|
||
|
||
- AC1: submit a task with a stale contract hash ⇒ rejected with `stale_generation`. Registered must-fail case.
|
||
- AC2: change any directive file → the next submit from an unrefreshed seat ⇒ rejected.
|
||
- AC3: after re-anchor, the same submit ⇒0.
|
||
- AC4: the rejection is _mechanical_ — assert it happens with the seat's LLM removed from the loop
|
||
(a scripted client reproduces it).
|
||
- Dogfood: the postmortem's opening incident — a compacted orchestrator that kept acting after its
|
||
directives rotted; this very mission's `CLAUDE.md` context-rot guard is the prose version.
|
||
- Fail-closed point: **stale hash ⇒ mutations rejected.**
|
||
- Note: this is the single most likely task to produce fleet-wide breakage on rollout. Ship behind a
|
||
report-only mode first (log rejections without enforcing) for one measured window, then flip.
|
||
Report-only mode must itself be time-boxed and expire — see D5.
|
||
|
||
---
|
||
|
||
**R-030 — Retire the Python controller + `plugins/macp` island**
|
||
_build 1 · depends: R-023, R-024, R-025, R-026 · 110k · codex_
|
||
|
||
Delete `mosaic_orchestrator.py`'s exec/gate/event block and the redefined types in
|
||
`plugins/macp/src/macp-runtime.ts:43-77`; repoint the OpenClaw ACP backend at the Node executor. Also
|
||
removes the dangling reference to `tools/macp/dispatcher/pi_runner.ts`, which does not exist in this
|
||
checkout.
|
||
|
||
- AC1: island count is 1 — `grep -rn "def emit_event\|def append_event" packages/` returns nothing.
|
||
- AC2: `plugins/macp` integration test drives a task end-to-end through the Node executor.
|
||
- AC3: `.mosaic/orchestrator/{tasks,state}.json` and `events.ndjson` are no longer written by any code
|
||
path (assert by running a full mission and checking mtimes).
|
||
- AC4: a re-added duplicate emitter ⇒ R-025/R-065 check fails.
|
||
- Dogfood: "three parallel islands, none wired."
|
||
- Fail-closed point: n/a — this is a deletion; its guard is R-065.
|
||
|
||
### P3 — Rotation lifecycle
|
||
|
||
---
|
||
|
||
**R-040 — Durable compaction/token sensor**
|
||
_build 3 · depends: R-022, R-029 · 130k · sonnet_
|
||
|
||
Nothing durable tracks compactions today (the Claude compaction hook "revokes leases only"). Add a
|
||
per-seat token/compaction counter persisted to the spine, with per-runtime thresholds
|
||
(Claude ~200k, Codex/Pi ~372k) as configuration, not constants in code.
|
||
|
||
- AC1: driving a seat past the configured threshold emits `compaction.pending` with the observed count.
|
||
- AC2: an actual harness compaction emits `compaction.detected` — assert by triggering a real compaction,
|
||
not by unit-mocking the hook. (A mocked sensor is an inert sensor.)
|
||
- AC3: sensor failure (hook not installed) ⇒ the seat is reported _unmonitored_, and managed seats
|
||
refuse to start unmonitored. Registered must-fail case.
|
||
- AC4: counters survive a seat respawn.
|
||
- Dogfood: this orchestrator lineage itself — a compacted session that kept operating.
|
||
- Fail-closed point: **no sensor ⇒ seat not managed ⇒ launch refused.**
|
||
|
||
---
|
||
|
||
**R-041 — Typed checkpoint writer**
|
||
_build 3 · depends: R-028, R-040 · 150k · opus_
|
||
|
||
Structured essentials only — goal, current state, completed, blocked, next steps, constraints
|
||
(mission-control FR-5) — as signed claims. **Never the transcript.** Atomic write + digest, into
|
||
`interaction_checkpoints` (which already has `checkpoint_id`, `content_digest`, `compaction_epoch`).
|
||
|
||
- AC1: checkpoint written; digest verifies; `compaction_epoch` increments monotonically.
|
||
- AC2: kill the writer mid-write → no partial checkpoint is readable (atomic rename or tx). Registered
|
||
must-fail case: a torn checkpoint must be rejected, not repaired-by-guess.
|
||
- AC3: the checkpoint contains no transcript text — assert by size bound and by a content check that
|
||
rejects raw conversational turns.
|
||
- AC4: required claim classes are declared in a manifest; a checkpoint missing one is _invalid at write
|
||
time_, not at read time.
|
||
- Dogfood: the abandoned coordinator PoC "compacted THE CHAT (the anti-pattern) not canonical mission
|
||
state."
|
||
- Fail-closed point: **incomplete claim set ⇒ checkpoint rejected.**
|
||
|
||
---
|
||
|
||
**R-042 — Rotation daemon** ★ the build-3 deliverable
|
||
_build 3 · depends: R-041, R-023 · 240k · opus_
|
||
|
||
The missing piece: a deterministic program (no LLM in the loop) that watches the sensor, checkpoints
|
||
atomically, kills the old session, launches a fresh one, and rehydrates. Reuse coord's
|
||
`buildContinuationPrompt`, session lock, `writeAtomicJson`, and crash recovery. The abandoned
|
||
`apps/coordinator` PoC stalled exactly here: `_spawn_agent()` was a stub and `_check_context()` only
|
||
_logged_ "rotation needed" and never called `trigger_rotation()`.
|
||
|
||
- AC1: a seat crossing threshold rotates without operator action; the new seat reports the same mission
|
||
and next action.
|
||
- AC2: **`_check_context`-equivalent regression test:** a test asserts that threshold-crossing actually
|
||
_invokes_ rotation (not merely logs it). This is the specific historical failure — assert the call,
|
||
not the log line. Registered must-fail case: stub out the rotation call → test ⇒≠0.
|
||
- AC3: no LLM call occurs in the rotation path — assert by running the daemon with network/model access
|
||
denied; rotation still completes.
|
||
- AC4: rotation is idempotent under double-trigger (only one new session).
|
||
- AC5: if checkpoint or rehydration fails, the daemon does **not** kill the old session.
|
||
- Dogfood: `apps/coordinator` residue (Jan–Feb 2026, deliberately abandoned).
|
||
- Fail-closed points: **checkpoint failure ⇒ no kill**; **rehydration failure ⇒ no promote** (R-043).
|
||
- ⚠ Secretly greenfield: the daemon does not exist in any form. `packages/coord` has primitives, not a
|
||
daemon. Budget accordingly.
|
||
|
||
---
|
||
|
||
**R-043 — Rehydration attestation gate**
|
||
_build 3 · depends: R-041 · 130k · opus_
|
||
|
||
The pessimist's core objection to rotation: an incomplete rehydration produces a clean-looking session
|
||
with a silent gap — worse than a compacted one, because nothing signals the loss. A fresh seat must
|
||
attest that every required claim class is present and digest-valid _before_ it is permitted to act.
|
||
|
||
- AC1: with a complete claim set, the new seat's first mutation ⇒0.
|
||
- AC2: with one required claim class removed, the new seat's first mutation ⇒ rejected with
|
||
`rehydration_incomplete`, and the seat is quarantined, not silently continued. Registered must-fail case.
|
||
- AC3: attestation is enforced executor-side (R-029 machinery), so a seat cannot self-certify.
|
||
- AC4: the operator sees a distinct, greppable state for `rehydrated-degraded`.
|
||
- Dogfood: this session lineage's hand-run `mosaic-context-refresh` "fail-closed residency attestation"
|
||
— the prose version of exactly this gate.
|
||
- Fail-closed point: **incomplete rehydration ⇒ seat may not mutate.**
|
||
|
||
---
|
||
|
||
**R-044 — Fault-injected rotation soak**
|
||
_build 3 · depends: R-042, R-043 · 180k · sonnet_
|
||
|
||
The "100 rotations lossless" bar, made adversarial. 100 clean rotations prove almost nothing; the
|
||
failure mode is a kill at the worst moment.
|
||
|
||
- AC1: 100 consecutive rotations, claim-set diff empty at every boundary (machine-compared, not
|
||
eyeballed).
|
||
- AC2: fault matrix, each ≥10 iterations: kill during checkpoint write; kill after checkpoint before
|
||
old-session kill; kill after kill before relaunch; DB unavailable during checkpoint; corrupt
|
||
checkpoint; two daemons racing; contract hash changed mid-rotation. **Each must end in a defined
|
||
state — either fully rotated or fully not; never half.**
|
||
- AC3: any iteration ending `rehydrated-degraded` fails the suite.
|
||
- AC4: the suite runs against real runtime artifacts (real seats, real spine), not mocks —
|
||
P-CONFORMANCE-001 criterion (1).
|
||
- Dogfood: all of P-LIFECYCLE-001; the harness is the P-CONFORMANCE down payment.
|
||
- Fail-closed point: **any half-state ⇒ suite red ⇒ rotation does not ship.**
|
||
|
||
---
|
||
|
||
**R-045 — Delete `/compact and continue` from the persistent-seat path**
|
||
_build 3 · depends: R-042, R-044 · 60k · codex_
|
||
|
||
Removal = substitution. Keep it for ephemeral seats (one doctrine per session class).
|
||
|
||
- AC1: `grep -rn "compact and continue"` in orchestrator/persistent guidance returns nothing.
|
||
- AC2: ephemeral-seat guidance still contains it, and a test asserts the two session classes are
|
||
distinguished by a machine-readable attribute, not by prose.
|
||
- AC3: a persistent seat attempting the old flow is redirected to rotation by the daemon.
|
||
- Dogfood: P-GUIDE-001; the "live irony" that the deciding session was itself a hand-re-anchored
|
||
compacted orchestrator.
|
||
- Fail-closed point: n/a (guidance change) — but see the hazard.
|
||
- ⚠ **Ordering hazard (severe):** merging this before R-042/R-044 are _live on the fleet_ removes the
|
||
coping mechanism while leaving the failure in place. Hard edge, and the orchestrator should verify
|
||
deployment, not just merge.
|
||
|
||
### P4 — Comms service
|
||
|
||
---
|
||
|
||
**R-050 — `comms/v1` envelope + protocol-version negotiation**
|
||
_build 4 · depends: R-011 · 140k · opus_
|
||
|
||
Version the protocol, not the participants. Envelope carries `comms/v1`; receiver rejects unsupported
|
||
versions LOUDLY; N-version compatibility window; framework version is diagnostic-only (avoids the N²
|
||
matrix). This is the first breaking comms change, so the negotiation must exist before anything rides
|
||
on it.
|
||
|
||
- AC1: a `comms/v1` envelope round-trips; digest and correlation id preserved.
|
||
- AC2: a `comms/v99` envelope ⇒ rejected with a machine-readable `unsupported_protocol_version` and a
|
||
visible operator message. Registered must-fail case.
|
||
- AC3: a malformed/unsigned envelope ⇒ rejected (never best-effort parsed).
|
||
- AC4: the supported-version window is data, not code branches; shrinking it in config immediately
|
||
rejects the dropped version.
|
||
- Dogfood: P-AUTHORITY-001; "bare tmux = comms P0/PoC" with no version field at all.
|
||
- Fail-closed point: **unknown protocol version ⇒ reject, loudly.**
|
||
|
||
---
|
||
|
||
**R-051 — Comms service: PG state machine**
|
||
_build 4 · depends: R-050, R-013 · 200k · opus_
|
||
|
||
Sole-path service. `PENDING → RECEIVED → CONSUMED → DEAD-LETTER`, ack cursor, per-recipient filter.
|
||
`interaction_inbox` already provides the unique `(session_id, idempotency_key)` index and the
|
||
status enum — extend rather than fork.
|
||
|
||
- AC1: a message with no consumer remains `PENDING` and is retried; it is never reported delivered.
|
||
- AC2: a bounced transport attempt does not advance the cursor. Registered must-fail case: force a
|
||
bounce, assert the cursor is unchanged and a retry occurs.
|
||
- AC3: after N failed attempts the message goes `DEAD-LETTER` and raises a visible operator signal —
|
||
dead-lettering silently is the same bug in a new hat.
|
||
- AC4: `RECEIVED` and `CONSUMED` are distinct and both observable; a manual liveness check is never
|
||
required to learn a message's fate.
|
||
- Dogfood: the MACP scout bounce ("tmux target not found") — never RECEIVED, no retry, discovered only
|
||
by hand.
|
||
- Fail-closed point: **un-acked ⇒ not consumed ⇒ retried.**
|
||
|
||
---
|
||
|
||
**R-052 — tmux transport adapter behind the service**
|
||
_build 4 · depends: R-051, R-060 · 160k · sonnet_
|
||
|
||
tmux becomes the first dumb adapter, not the authority. Durable retry before cursor advance.
|
||
|
||
- AC1: sending to a nonexistent pane ⇒ message stays `PENDING`, retried, and the sender receives
|
||
`not-written` (R-027 tri-state), not `✓`.
|
||
- AC2: sending to a busy pane ⇒ `written+unverified` until the receipt is observed, then `RECEIVED`.
|
||
- AC3: adapter failure never loses the message — kill the adapter mid-send, assert redelivery.
|
||
- AC4: the adapter cannot mark anything `CONSUMED`; only the recipient's ack can. Registered must-fail case.
|
||
- Dogfood: scout-bounce; `send-message.sh:71` (`tmux target not found` → exit 1, no retry) and `:111`.
|
||
- Fail-closed point: **transport cannot self-certify delivery.**
|
||
- ⚠ Ordering: requires R-060 — identity addressing is meaningless while seats are split across the
|
||
`default` and `mosaic-fleet` sockets (that split _was_ the bounce).
|
||
|
||
---
|
||
|
||
**R-053 — Per-class coalescing + supersede**
|
||
_build 4 · depends: R-051 · 130k · sonnet_
|
||
|
||
The `#1018` case: pepper's "awaiting your word" arrived _after_ the PR had merged — stale-consumed-as-live.
|
||
|
||
- AC1: two messages of the same class for the same subject where the later supersedes → the recipient
|
||
sees one, the newer, with the older marked superseded (not deleted — auditability).
|
||
- AC2: a message whose subject has reached a terminal state is delivered _marked stale_, never as live
|
||
actionable. Registered must-fail case: assert an actionable-class stale message does not present as
|
||
actionable.
|
||
- AC3: ordering within a class is monotonic per sender.
|
||
- AC4: coalescing never drops a message with no successor.
|
||
- Dogfood: `#1018` stale-consumed.
|
||
- Fail-closed point: **ambiguous freshness ⇒ mark stale, never present as live.**
|
||
|
||
---
|
||
|
||
**R-054 — Redis hot path + provenance guard**
|
||
_build 4 · depends: R-051, R-013 · 170k · opus_
|
||
|
||
Redis Streams + PEL as a derived, rebuildable accelerator. PG remains the protected tier.
|
||
|
||
- AC1: flush Redis entirely → the sweeper rebuilds the hot queue from PG with zero message loss and
|
||
zero duplicates. This is the acceptance test that matters.
|
||
- AC2: every read from Redis carries a provenance flag; a code path that returns Redis-derived data as
|
||
authoritative ⇒ CI check fails (registered with R-025's machinery). Registered must-fail case: add
|
||
such a path and watch the build go red.
|
||
- AC3: Redis unavailable ⇒ service degrades to PG-only and _says so_; it does not fail open to
|
||
"assume delivered".
|
||
- AC4: split-brain injection (Redis has a message PG does not) → reconciliation resolves toward PG and
|
||
emits an event.
|
||
- Dogfood: synthetic. Justification: the moment a fast tier exists, someone reads it as truth — this
|
||
guard is cheap now and impossible later.
|
||
- Fail-closed point: **Redis is never authority.**
|
||
|
||
---
|
||
|
||
**R-055 — Retire direct tmux sends**
|
||
_build 4 · depends: R-052, R-053 · 100k · codex_
|
||
|
||
- AC1: a CI check fails on any direct `tmux send-keys` to an agent pane outside the adapter.
|
||
- AC2: `agent-send.sh` becomes a thin client of the service; its exit codes remain tri-state per R-027.
|
||
- AC3: existing fleet scripts are migrated; the check enumerates what it scanned (no silent empty scan).
|
||
- Dogfood: the whole bare-tmux P0 comms substrate.
|
||
- Fail-closed point: **out-of-band send ⇒ build fails.**
|
||
|
||
### P5 — Retirements, hygiene, conformance
|
||
|
||
---
|
||
|
||
**R-060 — One roster-owned socket/host, quarantine, stale GC**
|
||
_build 5 · depends: R-005 · 150k · sonnet_
|
||
|
||
- AC1: every roster seat resolves to the single roster-owned socket; a seat launched on another socket
|
||
is quarantined and reported. Registered must-fail case.
|
||
- AC2: `fleet status` lists unmanaged sessions distinctly; count is nonzero in a test that plants one.
|
||
- AC3: stale-session GC reaps or rotates by max-age/max-context, with the decision recorded as an event.
|
||
- AC4: GC never reaps a session holding an active mission claim.
|
||
- Dogfood: scout-bounce root cause — "seats split default vs mosaic-fleet socket."
|
||
- Fail-closed point: **unmanaged seat ⇒ quarantined, not addressed.**
|
||
|
||
---
|
||
|
||
**R-061 — Auto-sync allowlist + worktree isolation**
|
||
_build 5 · depends: R-002 · 110k · sonnet_
|
||
|
||
`brain-sync.sh:99` does `git add -- "$@"`; `session-end.sh:48` stages `data/ views/ domains/ docs/ '*.md'`.
|
||
The measured incident (517bd5c26) staged agent-authored `docs/postmortem-spec/site/*` mid-write.
|
||
|
||
- AC1: staging is allowlist-driven; a path outside the allowlist is never staged. Registered must-fail
|
||
case: place an unknown path, run sync, assert it is untouched **and** reported.
|
||
- AC2: a half-written agent-authored file under an agent-owned path is never swept (lease/worktree check).
|
||
- AC3: the sync reports what it declined to stage (silence on decline is how this gets re-broken).
|
||
- Dogfood: P-WORKFLOW-001, sweep 517bd5c26.
|
||
- Fail-closed point: **unknown path ⇒ not staged, and said out loud.**
|
||
- Note: criterion (1) of P-WORKFLOW-001 is partially obviated by R-062 (DB tracking), but (2)+(3)
|
||
remain for code/docs in project repos. Do not cancel this task on the strength of R-062.
|
||
|
||
---
|
||
|
||
**R-062 — Flat-file orchestration tracking → DB cutover, with rollback artifact** ★ highest data risk
|
||
_build 2/5 · depends: R-024, R-028, R-042 · 200k · opus_
|
||
|
||
Jason's directive: hard cutover, no flat-file interim. I comply on the _write_ path and dissent on the
|
||
unqualified form (D3). Scope: mosaic fleet orchestration state only — missions, agent-task
|
||
assignments, `MOS-ORCHESTRATION-BOARD-{LIVE,LEDGER}`, `docs/TASKS.md`, `.mosaic/orchestrator/*`.
|
||
**jarvis-brain PDA flat files (`data/projects`, `data/tasks`) are explicitly out of scope and must be
|
||
proven untouched.**
|
||
|
||
- AC1: a one-shot importer migrates the current board + ledger + `docs/TASKS.md` into the spine;
|
||
re-running it is idempotent (no duplicate missions/tasks).
|
||
- AC2: **in-flight safety** — a mission with a running task is migrated without the task being lost or
|
||
double-claimed. Test by migrating while a task is mid-execution; assert exactly-once completion.
|
||
- AC3: after cutover, flat files are no longer written (mtime unchanged over a full mission run).
|
||
- AC4: a **read-only export** regenerates a human-readable board from the DB on demand. This is the
|
||
rollback artifact, not a fallback tracking system — it is never read back as authority (enforced by
|
||
R-025's writer check).
|
||
- AC5: documented, tested rollback: restore the pre-cutover snapshot and resume flat-file tracking
|
||
within one operator step. Exercised once in a rehearsal before the real cutover.
|
||
- AC6: `git diff --stat` over `jarvis-brain/data/` across the cutover is empty.
|
||
- Dogfood: the flat-file clobber class (P-WORKFLOW-001); the co-location of fleet state inside the
|
||
brain repo, which the postmortem names as part of the bug.
|
||
- Fail-closed points: **importer aborts on any ambiguity rather than guessing**; **cutover refuses to
|
||
run while a rotation is in flight.**
|
||
|
||
---
|
||
|
||
**R-063 — Conformance harness**
|
||
_build 5 · depends: R-044, R-051, R-062 · 260k · opus_
|
||
|
||
Fault-inject the six live failure classes against real runtime artifacts on the DB substrate:
|
||
compaction/rotation, broker-unavailable, delivery-bounce, identity-drop, stale-hash, queue-inert.
|
||
Flat-file cases become regression guards.
|
||
|
||
- AC1: each of the six classes has an injector, an expected-outcome assertion, and a _demonstrated
|
||
red_ — the harness must be shown failing when the corresponding fix is reverted. **A harness that has
|
||
never been red is an inert gate.**
|
||
- AC2: the six seed incidents are reproduced by id: Pi brick, scout-bounce, gate-6 inert, #1019
|
||
recursion, identity drift, auto-sync sweep, #1018 stale-consumed.
|
||
- AC3: harness runs in CI on a schedule and on release; results land in the event ledger.
|
||
- AC4: harness self-reports coverage (which classes ran); a skipped class is a failure, not a silent pass.
|
||
- Dogfood: it _is_ the dogfood — the automated form of the standing directive.
|
||
- Fail-closed point: **skipped class ⇒ suite red.**
|
||
|
||
---
|
||
|
||
**R-064 — Fleet-wide inert-gate audit**
|
||
_build 5 · depends: R-002 · 120k · sonnet_
|
||
|
||
Apply the R-002 registry to every existing gate in `~/.config/mosaic/tools/` and the repo. Expect
|
||
casualties beyond gate-6.
|
||
|
||
- AC1: every gate is registered; the audit report lists each gate with its must-fail case and the
|
||
observed exit code.
|
||
- AC2: every gate found inert is either fixed in this PR or filed with an issue id in the registry;
|
||
an unexplained inert gate ⇒ CI red.
|
||
- AC3: the report is committed as evidence (this is the "prove it" artifact for P-QUEUE-001's
|
||
interim doctrine: _queue-guard green = zero information until proven otherwise_).
|
||
- Dogfood: gate-6 was inert **fleet-wide** and nobody knew; the base rate of inert gates is unknown and
|
||
that is itself the finding.
|
||
- Fail-closed point: **inert gate without a filed issue ⇒ red.**
|
||
|
||
---
|
||
|
||
**R-065 — Retirement proof**
|
||
_build 5 · depends: R-030, R-055, R-062 · 90k · codex_
|
||
|
||
A CI check asserting the three cross-cutting retirements are complete **and stay complete**: no
|
||
flat-file orchestration tracking writes, exactly one MACP implementation, no silent bypass markers.
|
||
|
||
- AC1: reintroduce each retired pattern in a scratch branch → check ⇒≠0, once per pattern. Registered
|
||
must-fail cases (three of them).
|
||
- AC2: the check enumerates what it scanned.
|
||
- AC3: allowlisted exceptions carry an expiry date; an expired exception ⇒≠0.
|
||
- Dogfood: the entire "built-but-unwired / retired-but-resurrected" disease.
|
||
- Fail-closed point: **resurrection ⇒ build fails.**
|
||
|
||
---
|
||
|
||
## 4. Fail-closed register
|
||
|
||
Every point where this plan chooses safety over availability. If any of these is later softened, the
|
||
corresponding failure class returns.
|
||
|
||
| # | Point | Task | What it costs when it fires |
|
||
| ----- | ------------------------------------------------------------- | ------------ | ------------------------------------------------------- |
|
||
| FC-1 | Unregistered gate ⇒ build fails | R-002 | New gates need a negative control before merge |
|
||
| FC-2 | Unknown/no-status/malformed CI state ⇒ merge blocked | R-003 | Merges wait on flaky status APIs |
|
||
| FC-3 | Broker absent + no break-glass ⇒ deny | R-004 | Requires R-005 first or the fleet bricks |
|
||
| FC-4 | Version skew ⇒ launch blocked | R-005 | Seats refuse to start after a partial upgrade |
|
||
| FC-5 | Skipped migration ⇒ error | R-010 | First-install must be fixed, not worked around |
|
||
| FC-6 | Ledger mutation ⇒ DB error | R-011 | No "cleanup" of bad events; only compensating entries |
|
||
| FC-7 | No DB ⇒ no managed work | R-012, R-024 | Spine outage stops the fleet. Accepted deliberately |
|
||
| FC-8 | Schema-invalid / credentials unresolved ⇒ reject before spawn | R-020 | Bad task definitions surface loudly |
|
||
| FC-9 | Executor crash ⇒ non-terminal state | R-020 | Requires a reclaim sweeper |
|
||
| FC-10 | Empty/unrunnable gate set ⇒ task fails | R-021 | Gate-less task types must be declared explicitly |
|
||
| FC-11 | Unpersistable event ⇒ operation fails | R-022 | Auditability outranks availability |
|
||
| FC-12 | Unknown terminal-status writer ⇒ build/DB rejects | R-025 | Every new writer needs an allowlist entry with a reason |
|
||
| FC-13 | Identity unresolvable ⇒ no write | R-027 | Respawned seats must re-derive identity or stop |
|
||
| FC-14 | Cannot verify ⇒ never report success | R-027 | Callers must handle a third exit code |
|
||
| FC-15 | Corrupt checkpoint ⇒ refuse rehydrate | R-028, R-041 | Forces a refresh cycle |
|
||
| FC-16 | Stale contract hash ⇒ mutations rejected | R-029 | Highest blast radius; ship report-only first |
|
||
| FC-17 | Unmonitored seat ⇒ launch refused | R-040 | Hook install becomes mandatory |
|
||
| FC-18 | Checkpoint failure ⇒ old session not killed | R-042 | Rotation stalls rather than losing state |
|
||
| FC-19 | Incomplete rehydration ⇒ seat may not mutate | R-043 | Quarantined seats need operator attention |
|
||
| FC-20 | Unknown protocol version ⇒ reject loudly | R-050 | Rollout needs the N-version window honored |
|
||
| FC-21 | Un-acked ⇒ not consumed ⇒ retried | R-051 | Duplicate-tolerant consumers required |
|
||
| FC-22 | Transport cannot self-certify delivery | R-052 | tmux "✓" becomes tri-state |
|
||
| FC-23 | Ambiguous freshness ⇒ marked stale | R-053 | Some genuinely-live messages arrive marked stale |
|
||
| FC-24 | Redis never authority | R-054 | Extra PG round-trip on the read path |
|
||
| FC-25 | Unmanaged seat ⇒ quarantined | R-060 | Ad-hoc tmux seats stop being addressable |
|
||
| FC-26 | Unknown path ⇒ not staged, and reported | R-061 | Sync gets noisier, deliberately |
|
||
| FC-27 | Importer aborts on ambiguity | R-062 | Cutover may need manual disambiguation |
|
||
| FC-28 | Skipped conformance class ⇒ suite red | R-063 | No partial conformance claims |
|
||
| FC-29 | Retirement resurrection ⇒ build fails | R-065 | Exceptions must carry expiry dates |
|
||
|
||
---
|
||
|
||
## 5. Ordering hazards (later task silently depends on an earlier invariant)
|
||
|
||
**H-1 — R-004 before R-005 re-bricks the fleet.** The three bypasses are load-bearing _because the
|
||
broker was never deployed_. Remove the fail-open before deployment coherence exists and you reproduce
|
||
2026-07-22 exactly, including the Pi brick. Hardest edge in the DAG.
|
||
|
||
**H-2 — Every migration-adding task silently depends on R-010.** R-011/R-013/R-022/R-028/R-050/R-051
|
||
will all pass in CI on PGlite and fail on the first real Postgres install. The defect is documented in
|
||
a TODO and has never been hit because nobody first-installed the PG tier.
|
||
|
||
**H-3 — R-025 must ship with R-020, not after.** The choke point's only real property is exclusivity.
|
||
The gap between "executor exists" and "executor is the only path" is exactly when island #4 gets built.
|
||
|
||
**H-4 — R-045 before R-042/R-044 are _deployed_ removes the coping mechanism.** Merge ≠ live. The
|
||
orchestrator must confirm rotation is running on the fleet, not just merged.
|
||
|
||
**H-5 — R-052 depends on R-060.** Identity addressing across two tmux sockets is the bounce that
|
||
motivated the whole comms build. Building the adapter first just relocates the bug.
|
||
|
||
**H-6 — R-062 depends on R-042.** Cutting over tracking while rotation is half-built means a rotation
|
||
lands mid-migration with state split across both substrates. R-062 must also refuse to run during an
|
||
in-flight rotation (FC-27).
|
||
|
||
**H-7 — R-029 depends on R-028's claim signing.** A contract hash that is not itself integrity-protected
|
||
is a suggestion. Also: R-029 has the widest blast radius of any task here; report-only first.
|
||
|
||
**H-8 — R-021's "empty gate set = failure" invariant is assumed by R-026.** If R-026 lands first,
|
||
Forge's stub is replaced by a real executor that will happily run zero gates.
|
||
|
||
**H-9 — R-063 assumes every earlier fix is _revertible in a test harness_.** If tasks land without
|
||
feature flags or clean revert points, "demonstrate the harness red" (AC1) becomes impossible and the
|
||
conformance suite silently becomes decorative. **Every P0–P4 task should land with a documented way to
|
||
disable its fix in a test context.** This is a cross-cutting requirement the orchestrator should add to
|
||
the charter, not a per-task note.
|
||
|
||
**H-10 — R-002's registry is only as good as its enumeration.** If the registry is populated by hand
|
||
from a partial list, unregistered gates keep passing. R-002 AC3 (unregistered-gate detection) is the
|
||
load-bearing half; R-064 is its audit.
|
||
|
||
---
|
||
|
||
## 6. Secretly-greenfield register
|
||
|
||
"Finish, don't re-spec" is accurate at the _specification_ level and misleading at the code level. Of
|
||
38 tasks, these have no existing implementation to finish:
|
||
|
||
| Task | Reality |
|
||
| ----------- | --------------------------------------------------------------------------------------------------- |
|
||
| R-002 | Gate registry + negative-control runner — new, no precedent in repo |
|
||
| R-011 | Hash-chained append-only ledger — the existing `events` table is calendar-shaped |
|
||
| R-013 | Outbox sweeper — tables exist, sweeper does not |
|
||
| R-020 | Executor — the package has **no exported programmatic submit** (scout §1); only a CLI placeholder |
|
||
| R-025 | No-second-path enforcement — entirely new |
|
||
| R-028 | HMAC claim integrity — new |
|
||
| R-029 | Contract-hash binding — new |
|
||
| R-040 | Durable compaction sensor — "nothing durable today" |
|
||
| R-042 | Rotation daemon — the PoC's `_spawn_agent()` was a stub; `packages/coord` has primitives, no daemon |
|
||
| R-043 | Rehydration attestation — new |
|
||
| R-050/R-051 | comms/v1 envelope + service — tables exist, protocol and service do not |
|
||
| R-054 | Provenance guard — new |
|
||
| R-063 | Conformance harness — new |
|
||
|
||
**Estimate: ~13 of 38 tasks (≈40% of tokens) are new construction.** What genuinely exists and should
|
||
be _reused, not rebuilt_: `packages/macp` types/credential-resolver/gate-runner/risk-floor;
|
||
`packages/db` schema + drizzle migrations (post-R-010); `packages/queue` `QueueAdapter`;
|
||
`packages/coord` mission/session/capsule/crash-recovery primitives; `interaction_*` tables;
|
||
PG + Redis already running in-stack. That is a real head start — it is just not "wiring."
|
||
|
||
---
|
||
|
||
## 7. Migration & rollback (the hard cutover)
|
||
|
||
Per Jason: hard cutover, no flat-file interim. Concretely, R-062 executes in five steps, each of which
|
||
must be reversible until the last:
|
||
|
||
1. **Rehearsal** (throwaway DB): run the importer against a copy of the current board/ledger/TASKS.md.
|
||
Diff the reconstructed board against the flat original. Any unexplained delta ⇒ stop.
|
||
2. **Freeze window**: no rotation in flight (FC-27), no task mid-execution beyond a known set.
|
||
3. **Snapshot**: tag the flat-file state (git tag + tarball) — this is the rollback artifact.
|
||
4. **Import + flip**: importer runs; writers switch to the spine in one commit; flat files become
|
||
read-only outputs.
|
||
5. **Verification window**: for one measured period, a scheduled job regenerates the human board from
|
||
the DB and diffs it against the frozen snapshot's _shape_ (not content). Divergence in shape ⇒
|
||
investigate. **This is not a dual-write and not an interim tracking system** — it is a
|
||
one-directional export that proves the DB is complete.
|
||
|
||
**Rollback trigger:** any of — importer AC1 idempotency failure, a lost in-flight task (AC2), or spine
|
||
unavailability exceeding the agreed window. **Rollback action:** restore the snapshot, revert the
|
||
writer flip, resume flat-file tracking. Must be exercised once in rehearsal or it is not a rollback
|
||
plan, it is a wish.
|
||
|
||
---
|
||
|
||
## 8. Dissent
|
||
|
||
**D1 — The charter's sequencing starts one phase too late.**
|
||
`MISSION.md` sequences "spine + choke-point first." I insert P0 (R-001…R-005) ahead of it. Reason:
|
||
every task in Builds 1–4 introduces gates, and this fleet has demonstrated — twice, on the same
|
||
defect — that it cannot detect an inert gate. Building the choke point first means its gates are
|
||
unverifiable _by construction_, and we would be shipping the exact class of artifact the postmortem
|
||
indicts. R-001 is also a hard blocker (nothing can be pushed today). Cost of the insertion: ~420k
|
||
tokens, roughly 8% of the plan. I consider it the highest-leverage 8% here.
|
||
|
||
**D2 — I disagree with the scout's chosen wire-in point.** ★ headline dissent
|
||
`MACP-WIRING-SCOUT.md` names the "single integration point" as replacing the exec/gate/event block in
|
||
`mosaic_orchestrator.py::run_single_task` (:126-276). But that controller is **disabled**
|
||
(`.mosaic/orchestrator/config.json:2` — `"enabled": false`) and references a dispatcher path
|
||
(`tools/macp/dispatcher/pi_runner.ts`) that does not exist in this checkout. Wiring the new choke
|
||
point into a disabled rail produces a stranded executor — the identical disease, one layer up, and it
|
||
would look "done" in a PR.
|
||
|
||
The paths that actually carry work today are `packages/mosaic/src/commands/launch.ts` (`mosaic
|
||
yolo|claude|codex|pi`, :730-843, :1102-1167) and `packages/coord/src/runner.ts` (:397-427). **Wire
|
||
those (R-023, R-024) and delete the Python rail (R-030) rather than porting it.** The scout's analysis
|
||
is excellent and its file:line evidence is what let me reach this conclusion — I am disagreeing with
|
||
its recommendation, not its findings. Concrete consequence: R-030 becomes a deletion task, not an
|
||
integration task, and Build 1's acceptance must be measured on a live `mosaic yolo` invocation.
|
||
|
||
**D3 — "Hard cutover, no interim" is right about tracking and wrong about evidence.**
|
||
I comply with the directive: one write path, no flat-file interim, no dual-write. But a hard cutover
|
||
with **no rollback artifact** will, on the balance of this codebase's history, lose an in-flight
|
||
mission. R-062 AC4/AC5 add a one-directional read-only export and a rehearsed rollback. These are not
|
||
an interim tracking system — nothing reads them as authority (enforced by R-025) — they are the
|
||
snapshot you need at 3am. If the orchestrator judges even this to violate the directive, escalate to
|
||
Jason rather than silently dropping it; the difference between "no interim" and "no rollback" is worth
|
||
one question.
|
||
|
||
**D4 — The "100 rotations lossless" bar as written is not a test, it is a demo.**
|
||
100 clean rotations exercise the happy path 100 times. The failure mode is a kill at the worst moment.
|
||
R-044 replaces the bar with a fault matrix and defines "lossless" as a machine-compared claim-set diff.
|
||
I would not accept Build 3 on 100 clean rotations alone, and I would treat any suite that has never
|
||
been observed red as unproven (R-063 AC1).
|
||
|
||
**D5 — Report-only modes must expire, or they become the new fail-open.**
|
||
I recommend report-only rollout for R-029 (contract hash) and R-025 (no-second-path) because their
|
||
blast radius is fleet-wide. But a report-only gate is definitionally inert, and this organization has
|
||
proven it will not notice. **Every report-only mode must carry a hard expiry timestamp after which it
|
||
enforces or refuses to start** — same mechanism as R-004's break-glass. Without that clause I withdraw
|
||
the report-only recommendation and prefer a hard flip with a scheduled window.
|
||
|
||
**D6 — P-QUEUE-001 should not be scheduled inside Build 1, and its ownership is currently ambiguous.**
|
||
The queue guard is a shell script in the shared tools path; it has no dependency on the spine or the
|
||
executor, and it is P0. Scheduling it "inside Build 1" delays a P0 fix behind a 1M-token foundation.
|
||
R-003 sits in P0 instead. Separately: PR #1023 is explicitly **parked under Mos** while HOLD is lifted
|
||
for this workstream — two lanes can legitimately claim it. **The orchestrator must resolve this with
|
||
Mos before dispatching R-003.** A third recursion on this defect would be the postmortem's own
|
||
anti-pattern, performed by the remediation.
|
||
|
||
**D7 — Build 4's adapter fan-out (Matrix/Discord/Slack/Telegram) does not belong in this mission.**
|
||
The comms _service_, the envelope, the state machine, and one adapter (tmux) fix the observed failures.
|
||
Additional adapters are reach, not correctness, and each one adds a delivery-semantics surface that
|
||
R-063 must then cover. I recommend explicitly deferring adapters beyond tmux to a follow-on mission,
|
||
and saying so in the charter so it is a decision rather than a slip.
|
||
|
||
**D8 — FC-7 and FC-11 are real costs, and I want them acknowledged in writing, not discovered.**
|
||
"No DB ⇒ no managed work" and "unpersistable event ⇒ operation fails" mean a Postgres outage stops the
|
||
fleet, where today a flat-file fleet limps on. That is the correct trade for a system whose defining
|
||
failure is _silent_ continuation — but it is a genuine availability regression, and the first time it
|
||
fires at 2am someone will be tempted to add a fallback. **Pre-commit to the answer now:** the fallback
|
||
is "the fleet stops," and the mitigation is spine availability (backups, restart policy, monitoring),
|
||
not a degraded write path. If that is not acceptable to Jason, the right time to say so is before R-012,
|
||
not during the incident.
|
||
|
||
---
|
||
|
||
## 9. Handoff notes for the orchestrator
|
||
|
||
- **Dispatch order for the first wave (parallelizable):** R-001 → then R-002 and R-005 concurrently →
|
||
then R-010 concurrently with R-003; R-004 waits on R-005.
|
||
- **Do not dispatch any P2 task before R-002 is merged.** That is the whole argument of D1; if it gets
|
||
compressed away under schedule pressure, the plan's central claim goes with it.
|
||
- **Pre-registration:** every AC above is written to be committed _before_ the diff is read. They are
|
||
drafts — the reviewer should tighten them per task, but the must-fail cases are non-negotiable.
|
||
- **Charter addendum recommended (H-9):** every P0–P4 task lands with a documented way to disable its
|
||
fix in a test context, so R-063 can demonstrate red.
|
||
- **Three decisions needing a human or Mos:** (1) R-003 ownership vs. parked PR #1023 [D6];
|
||
(2) rollback artifact vs. "no interim" reading [D3]; (3) accepting the FC-7/FC-11 availability
|
||
trade [D8].
|