# CI/CD flow and reliability — incremental execution

audience: AI coding agents first.

status: ACTIVE
task IDs: #8–#10, recovery #1
source request: Execute the reviewed CI/CD redesign without Factory. Use `/claudex-workflow`; coding agents `gpt-5.6-terra/high`; diff reviewers `gpt-5.6-sol/low`; additional specialist review for critical auth/security/money/publication seams. Develop non-conflicting streams in parallel. Land and deploy each independently as soon as it is complete. Generate five image variations per `/ci` mockup concept in one ask-gpt prompt and present them in browser before UI-specific visual implementation. Owner scope expansion: this MUST be a reusable per-project delivery system, not Overdeck-only. First dogfood projects: Overdeck, multideal, and zync.is. Onboarding a project MUST install the standard queue, WIP preservation, validation receipt, deployment event, stall supervision, and observability primitives; Overdeck `/ci` MUST aggregate and filter authoritative state per project.

Recovery request (2026-08-14): Resume conversation `ci-cd-incremental`. Main thread MUST recover every task, work receipt, changed file, commit/ref, branch/worktree, verification result, blocker, and next action from ground-truth conversation records plus current repository/runtime state. Reconcile stale paths and contradictory receipts. Then create one durable agent-facing recovery document. No subagents, delegation, or inferred completion.

## Owner decision 2026-08-14 — narrow to felt latency, freeze machinery

Owner reviewed the program and ruled: days of infrastructure work produced no felt relief; execute the
measured fast path only.

1. **DO NOW** — the two S5 diff-derived guards (docs-only deploy short-circuit in
   `packaging/deploy-local.sh`; `docs/plans/*.md` reclassified to the prompt-class docs lane in
   `finish-branch.sh`). Main session executes directly.
2. **NEXT** — remove remaining full-gate ceremony (export/import + pnpm install + typecheck) for
   landings whose diff touches no code. Main session executes after 1.
3. **FROZEN** — S8 controller retirement, immutable-bundle/deployment track, S6 scheduler retirement,
   Tekton delivery contract. Parked, not deleted; one subagent reconciles statuses and preserves
   evidence. Re-open only on post-fix measurement.

Rationale: gate runs no tests (`TESTCMD='true'`); cost is ceremony + unconditional ~6min deploy on
100% of landings; docs lane covers only 2% of tickets while ~15% qualify.

### Item 1 receipt (2026-08-14)

Both guards verified already landed AND installed (byte-match worktree/deploy clone/installed copy)
but inert: deploy stamp `~/.local/share/overdeck/deploy/.git/harness-deployed-sha` ABSENT and deploy
clone HEAD (9b84c1b0) behind origin/main — the short-circuit never fired. Fix = one full deploy to
write the stamp; running in background at time of writing. Proof of done: stamp file exists with
40-hex sha; a docs-only landing exits `deployed-docs-only` in <1s with no service restarts.

### Item 2 closed by measurement (2026-08-14) — no lander change

Measured in a fresh tree (no `node_modules`, warm pnpm store): `pnpm install --prefer-offline
--silent` = 4s (406MB via hardlinks). A docs-lane ticket's residual cost is candidate git
construction + 4s install + gitleaks/markdownlint (seconds); typecheck and tests are already
skipped. Full-gate median ~897s is typecheck plus queue wait, not install. With `docs/plans/*.md`
reclassified (item 1), no-code landings already route to the docs lane. Cutting further would touch
correctness-critical isolation to save ~4s — not justified. Remaining docs-ticket latency source is
FIFO queue wait behind full tickets (5 queued landings observed at measurement time); any future
work targets queue scheduling, only on post-fix measurement.

## Goal

Deliver useful relief continuously, beginning with hours-long blocking, stalled capacity, and authoritative `/ci` visibility. Never hold finished work until the whole program completes.

## Canonical design

`docs/specs/2026-08-12-ci-cd-reliability-design.md`

## Orchestration contract

- Use `/claudex-workflow` only. NEVER launch Factory.
- Coder: `gpt-5.6-terra`, effort `high`.
- Reviewer: `gpt-5.6-sol`, effort `low`, report-only.
- Security/publication/destructive/secret seam: additional independent `security-guard` or `gpt-5.6-sol/medium` review.
- Verification agent: mechanical low-cost coder-family model, never reviewer model.
- One task = bounded contract + exact file list + exact acceptance.
- Same-wave tasks own strictly disjoint files.
- Each stream uses an isolated worktree and branch.
- Each completed vertical slice: focused tests → review → installed/runtime proof → land → deploy → owner-visible receipt. Do not wait for sibling streams.
- Shared-file conflict: later stream rebases after current owner lands. Never copy files across bases and never overwrite WIP.

## Stream registry

### S1 — Flow unblock and live events

Priority: FIRE.

Outcome: no invisible queue/lock/stall retains capacity for hours; authoritative event source exists for `/ci`.

Initial slice boundaries:

1. Deploy progress/state schema and read-only status command/API. Integrate with existing deploy-queue-coalescing work; do not duplicate its queue files.
2. Per-step deploy timing and heartbeat emission.
3. Stall supervision and bounded process-tree cancellation with recovery preservation.
4. Short queue/publication transitions; long work outside management locks.

Ship each boundary independently when useful.

### S2 — Existing `/ci` page replacement

Priority: FIRE.

Outcome: `/ci` becomes authoritative and usable.

Visual implementation waits only for mockup direction. Data plumbing may proceed against existing route/API contract. Incremental slices:

1. Live queue/running/stalled board.
2. Work detail: holder, wait reason, last progress, logs/diff/recovery.
3. Validation detail: expected/executed/omitted/failed and reasons.
4. Release detail: source, artifact, deployed version, rollback.

Every deck-ui change follows `od-ui-dev`; use existing primitives and gallery registration.

### S3 — WIP capture and stale-worker fencing

Priority: 1.

Outcome: accepted work cannot be lost, overwritten, cleaned early, or published by stale workers.

Slices:

1. Recovery capture + restore proof in shadow mode.
2. Fencing token/idempotency schema and stale-side-effect rejection.
3. Crash reconciliation and cleanup authority after drills.

Critical publication/destructive seam requires specialist review.

### S4 — Immutable validation proof

Priority: 2.

Outcome: exact candidate validated once; merge/deploy consume proof.

Slices:

1. Repair dead durable receipt publication.
2. Complete duplicate buildbox caller natural proof and remove one caller.
3. Immutable child receipts and artifact collection.
4. Selector shadow mode; full gate remains authority until zero-miss admission.

### S5 — Build-once targeted deployment

Priority: FIRE (raised 2026-08-14 from 3 — the measurement below identifies this stream as the dominant
delivery cost).

Outcome: deployment never rebuilds and activates only affected ownership units.

#### Measured attribution (2026-08-14)

Owner report: landing a document or settings file takes minutes; a `CLAUDE.md` edit costs ~1.5h of agent
time. Measured from `.git/harness/landq/log`, `.git/harness/land-stamps.jsonl` and the systemd journal:

- 438 land tickets recorded; 429 ran the full gate, 9 took the docs lane (2%).
- 313 first-parent landings in 7 days (~45/day). Queue depth at arrival reaches 9.
- Full-gate ticket latency: median ~897s, max ~1709s.
- `overdeck-deploy.service`: 23 runs/72h, median 363s, max 926s. `overdeck-web.service` restarted 71
  times in 24h although the web build itself is already skipped for most landings.
- `TESTCMD='true'` in `.claude/scripts/ship.sh` — the overdeck gate runs NO project test suite. The cost
  is not tests. It is candidate export/import + `pnpm install` (8s no-op) + typecheck of touched packages
  + an unconditional ~6min deploy, serialized behind one deploy lock.
- The deploy request-file queue DOES coalesce (`packaging/deploy-local.sh` snapshots `$QUEUE`; waiters
  exit `deployed-coalesced`). The fix is to make each deploy cheap, NOT to add coalescing.
- Reclassifying the docs lane alone recovers 48 of 313 landings (~15%); the deploy runs on 100% of them.
  Deploy proportionality is therefore first, classifier second.

#### Executing slice (2026-08-14) — two diff-derived guards, nothing more

1. `packaging/deploy-local.sh`: docs-only short-circuit — when every path changed since the last
   fully-successful deploy is documentation (root `*.md`, `docs/**`), drain the queue and exit without
   installing or restarting anything. Fail closed: absent/unreadable stamp, unknown commit, or one
   non-doc path forces the full deploy; the stamp is written only after a fully successful deploy.
   Deliberately NOT per-step ownership attribution — that is the shadow ownership graph slice 2 defers;
   a hand-maintained step→path map in bash rots silently. Acceptance: a plans-only landing deploys in
   under a second and restarts nothing.
2. `modules/workstation/claude/workflows/lib/finish-branch.sh`: `docs/plans/*.md` is agent-executable
   input in exactly the same sense as `SKILL.md`, which the lane already admits under prompt-class
   (secret scan + frontmatter + lint). Denying one while admitting the other is inconsistent; move
   `docs/plans/*.md` to prompt-class. Update `_docs_lane_classifier_canary` and the corresponding
   `dl_deny` case in `test-finish-branch.sh` deliberately, with the reason recorded. Shared brain —
   Press.zone, zync.is and multideal inherit it; `test-finish-branch.sh` green is non-negotiable.

Explicitly NOT in this slice: a third landing path, a nightly full suite, a shadow ownership graph, any
new daemon or notifier. The owner's complaint is machinery volume; this adds none.

Remaining slices (unchanged, priority 3):

1. Immutable non-code/config payloads.
2. Generated ownership graph and inert/targeted/full classification in shadow.
3. Web artifact built in validation and promoted unchanged.
4. Remove unconditional installer/restart/parity blocks one owner at a time.

### S6 — Remote scheduler retirement

FROZEN 2026-08-14 — see "Owner decision 2026-08-14". Parked, not deleted.

Priority: 4.

Outcome: provider/static dispatch plus immutable workspaces and atomic capacity replace scoring, shared mirrors, and health poisoning.

This stream starts after current scheduler-removal proposal is reconciled; never duplicate it. Each old path retires only after shadow/outage proof.

### S8 — Controller-owned agent retirement

FROZEN 2026-08-14 — see "Owner decision 2026-08-14". Parked, not deleted; evidence below preserved.
No live executor: worktree `.worktrees/ci-cd-reliability` absent as of 2026-08-14.

Priority: FIRE relief without session destruction.

Outcome: agents finish one bounded step, checkpoint durable evidence, submit external work, verify controller acceptance, then retire. Persistent controller—not resident Claude session—waits for gate, landing, review, deployment, or blocker events and launches one fresh bounded worker for the next executable action.

Transferred source: `/home/user/Projects/overdeck/.worktrees/localize-od-skills/docs/plans/2026-08-12-controller-owned-agent-retirement.md` (uncommitted preserved WIP). Owner transferred this lane to this CI/CD control-plane session; FIRE session stopped work on it.

Contract:

1. Every bounded worker writes immutable receipt containing request and task IDs, repo/worktree/ref, commit and artifact identity, verification, pending operation, blocker, next action, and rollback.
2. Worker submits wait operation to existing persistent controller, proves acceptance, and exits. NEVER poll while resident.
3. Controller transitions receipt from authoritative operation events and launches exactly one fresh worker. Lease expiry recovers; idempotency and fencing reject duplicate worker, landing, deployment, and stale effects.
4. Passing gate receipt is reused only when commit, inputs, environment, and gate version match. Session or worker identity change MUST NOT rerun unchanged proof.
5. Heavy non-interactive work runs through existing cluster wrappers with no silent laptop fallback. Interactive owner sessions remain local.
6. External blocker checkpoints immediately. Two progress intervals without new evidence force checkpoint and retirement/reassignment.
7. Existing `/ci` API/UI exposes useful active time, blocked time, exact wait, lease owner, remote node, last receipt, next automatic action, and safe retire/resume.
8. Night mode closes laptop admission for heavy work, bounds cluster concurrency, retires workers automatically, and leaves a morning receipt.
9. Existing WIP remains discoverable. Cleanup requires receipt proving landed, deployed, superseded, and retirement-safe.
10. Reuse existing task registry, land queue, remote build transport, controller, journals, and delivery receipts. NEVER create a parallel controller or queue.

Measured baseline: 46 peer sessions; resident Claude processes 20 GiB, TypeScript processes 8.6 GiB, QuietContext processes 5.1 GiB, and human-session cgroups 30.9 GiB. Current shell landing conductor and timers do not yet prove end-to-end event-driven retirement.

First-slice trace receipt: existing controller SQLite transition/event store is the authoritative seam. Current land caller polls `ticket.verdict` every two seconds; deploy caller polls request files; controller does not consume either completion as a delivery operation or launch a successor worker. Existing controller already has atomic transitions, revision checks, idempotency journal, crash resume, and event log. First implementation targets land completion only: bind land ticket to immutable operation receipt and lease/fencing generation, persist controller acceptance before worker retirement, convert conductor verdict to idempotent controller event, recover lost wakeups on startup, and dispatch exactly one fresh bounded worker through the existing seat mechanism. Deployment integration follows only after this vertical slice is installed and proven. One trace agent failed from prompt-size limits; two independent traces converged on this seam, but no code was produced. Next action is a smaller focused implementation workflow.

First executable slice: trace actual dispatch → bounded work → gate → landing → deploy → wake paths and select the largest resident wait seam. Add receipt transition plus lease at that existing controller seam; prove crash, retry, duplicate rejection, lost-wakeup recovery, fresh-worker reconstruction, installed runtime behavior, and old polling retirement before expanding to the next seam.

### S7 — Rollback and emergency restoration

Priority: 5.

Outcome: every release can return to known-good state through same evidence path. Keep emergency priority lane until all outage drills pass.

## Mockup brief and receipt

One ask-gpt Pro prompt requests 15 images:

- A1–A5: flow board.
- B1–B5: operations timeline/queues/locks/stalls.
- C1–C5: release/check/artifact/rollback/recovery detail.

Images MUST be presented through browser mockup session. Owner selection updates S2 visual contract; backend streams continue meanwhile.

## Acceptance criteria

- Owner sees first useful landed/deployed slice without waiting for whole program.
- Parallel streams have disjoint ownership and independent receipts.
- Every implementation/review agent metadata proves requested GPT model and effort.
- S1 exposes live authoritative queue/wait/progress/stall data before S2 styling completes.
- `/ci` consumes authoritative state, not fabricated or stale derived status.
- No slice weakens test coverage or publication safety.
- Every retired mechanism has replacement invariant, shadow parity, failure drill, rollback window, and installed proof.

## Preserved WIP

- Design worktree: `/home/user/Projects/overdeck/.worktrees/ci-cd-reliability` — ABSENT as of 2026-08-14; dead path, do not chase.
- Existing deploy queue, scheduler removal, CI runtime optimization, and UI WIP remain separate owners. Reconcile before assigning files.
- Shared main checkout remains untouched.

## Current receipt

- Full evidence review and independent advisor review complete.
- Canonical design reorganized into seven independently shippable streams.
- Factory launch canceled by owner instruction.
- Mockup sheet received. Owner correctly identified that A1–A5, B1–B5, and C1–C5 are mostly complementary information modules, not genuine alternative designs. Treat them as a feature inventory, not 15 selectable pages. Consolidate into one `/ci` information architecture: overview, operations, and release detail with progressive drill-down; remove duplicate presentations.
- `/claudex-workflow` execution contract loaded.
- Exact ownership map completed. Existing deploy queue files, controller lifecycle files, and current design docs are excluded from worker edits.
- Tekton/provider-neutral delivery contract FROZEN 2026-08-14 — see "Owner decision 2026-08-14". The
  receipt lines below, through the Task #16 landing, are preserved evidence, not open work. Workflow `wf_dc4a7cc2-bc7` is not
  live; Task #16 landed at `3c94ce5b575cd842df6c3032074caed4b05bb42c`.
- Three-project deployment map complete. Overdeck emits CI receipts and GHCR Factory image digests; multideal deploys Cloudflare Worker bundles with version URL and explicit Wrangler rollback; zync.is rebuilds and sequentially deploys API/WWW/app after DB migration with no machine-readable release manifest or automated compensation. Common Tekton contract will keep project-owned adapters and normalize source digest, artifact URI/digest, provider deployment IDs/URLs, previous IDs, rollback support/status, and receipt URI.
- Tekton installation is blocked by cluster safety, not design: k3s Phase 1 live qualification/backup/restore is still pending; Phase 2 enrollment and a real node canary must precede Phase 4 namespace/RBAC/quota/Pod Security/network-policy/registry substrate. First independently shippable work is the provider-neutral delivery contract, project adapter conformance tests, and Overdeck `/ci` model; actual Tekton controller installation waits for those k3s receipts.
- Delivery-contract implementation commit `ec65397c358e6259a37a52871eb832c328136dde` passed focused mutation/conformance tests, but Sol review rejected it. Blocking false greens: succeeded receipts can include failed checks or no deployment/artifact evidence; desired/observed identities and artifact references are not bound; rollback combinations and timestamps can contradict reality; authoritative JSON Schema is weaker than handwritten validation. Review also found unused declarations in two validator/test files.
- The narrow unused-declaration workflow was stopped before mutation because fixing only warnings would leave the correctness defects intact.
- Claudex workflow `delivery-contract-correctness-fix` (`wf_dc4a7cc2-bc7`) is active under Task #16. Scope is restricted to `schemas/delivery/v1/receipt.schema.json`, `tools/delivery/validate-receipt.mjs`, `tools/delivery/validate-project.mjs`, and `tools/delivery/test-contract.mjs`. Acceptance requires direct authoritative-schema and semantic-validator conformance, negative mutations for all six false-green classes, removal of both unused declarations, focused contract tests, zero warnings, amended delivery-contract commit, and clean Sol/low adversarial review. Next action after the worker returns: inspect exact outputs and metadata; repair any remaining blocker or, only if clean, run owner-side verification and ship.
- Task #16 candidate `3c94ce5b575cd842df6c3032074caed4b05bb42c` is APPROVED by Sol/low with no blocking findings. Independent verification: three fixtures accepted; 17 receipt mutations rejected; project contract accepted; Node syntax clean; both schemas parse; zero runtime warnings; malformed and ambiguous digest identities rejected; prior false-success and schema/semantic cases remain covered; exact 10-file tracked ownership confirmed. Editor `require` warning is false because lines 8–9 consume it. Standalone ESLint is unavailable because this repository has no ESLint v9 configuration. Required pnpm wrapper remains blocked by pre-existing missing `.rb/package-owner.sh`; direct underlying contract command passed.
- Task #16 landed to `origin/main` through the guarded ship controller. Post-land deployment is BLOCKED, not complete: the pristine deploy clone contains another session's uncommitted `_human-session` work, so `deploy-local.sh` correctly refused to overwrite it. A clean-checkout fetch retry also failed on three pre-existing broken `refs/rescued/*` references. Preserve both states untouched. The landed candidate itself remains independently proven by direct contract execution and clean review; installed deployment proof is pending deploy-clone/Git-reference recovery by their owning lanes.

- First executable slice: land-wait retirement candidate now implements immutable receipt acceptance, monotonic lease fencing, verdict-event idempotency, restart lost-wakeup recovery, expired-lease reclamation, and one receipt-fenced `seat-remote` successor dispatch. Focused controller typecheck and 39 focused tests pass. Full workflow suite completed 114 passes with one pre-existing unrelated docs-lane failure. Worktree controller installed; live health and no-land acceptance passed. Candidate commit `09ba3cf18cbeaa1ff2bd8e8d8a9c8d3f87e85ede` had two confirmed blockers: retirement mode reached `flock` before opening `cfd`; successor reconciliation could race or crash after external spawn because pending state was only read before dispatch. Repair commit `d9a3d1009d132bdd21a10ded9cd0f5fcd13a44e6` opens and owns conductor lock in both paths while preserving short lock scope, atomically persists successor claims with expiry and monotonic generation fencing, and carries the immutable dispatch key through the existing seat invocation. Focused controller tests: 13 passed; controller typecheck passed; isolated retired-worker shell regression passed; both changed shell files pass syntax checks; `git diff --check` clean. Full workflow suite was re-run but timed out after four minutes and had already reported 11 unrelated pre-existing failures caused by missing `/home/user/.claude/bin/git` plus its existing docs-lane failures; the new isolated retired-worker regression passed. WIP remains only in this worktree; no reset, stash, or clean. No land or deploy for this slice.

FROZEN 2026-08-14 — the next action below is S8; see "Owner decision 2026-08-14". Live next action for
this plan is the S5 latency slice (main session).

Next action: preserve this local committed candidate for orchestrated review/landing; resolve workflow-suite environment failures in their owning lane before treating full-suite output as green.

## Permanent deploy-source correction

FROZEN 2026-08-14 — see "Owner decision 2026-08-14". Immutable-bundle/deployment track parked, not
deleted; the bundle security findings below remain binding if it re-opens.

Owner request: deployment blocked by another session's uncommitted deploy-clone work MUST receive systematic permanent prevention, not manual cleanup or another bypass.

Canonical design: `docs/specs/2026-08-12-immutable-deployment-input-design.md`.

Decision: retire persistent Git deploy clone from deployment critical path. Land controller produces content-addressed `overdeck.release/v1` bundle from exact landed tree; disposable builder produces exact tested artifacts; fenced activator atomically promotes versioned units. Runtime state, caches, logs, dependencies, and release artifacts live outside source. Dirty WIP and broken unrelated refs become irrelevant to deployment. Legacy deploy-clone WIP MUST be captured and restore-proven before retirement; NEVER stash/reset/clean it.

Acceptance delta:

- Dirty tracked/untracked legacy clone cannot block deployment and remains byte-identical.
- Broken unrelated refs cannot block exact-source acquisition.
- Deployment rebuilds nothing already proven; observed runtime identity matches artifact digest.
- Long build/validation runs outside short per-unit activation locks.
- Migration shadows existing path and requires parity plus rollback, restart, concurrent-land, dirty-clone, broken-ref, and interruption drills before retirement.

## FIRE restoration — blocked deploy queue

status: ACTIVE
owner: main session, Task #20
source request: All deployments blocked; work stalled. Release pressure now without waiting for immutable deployment redesign.

Restoration path:

- Preserve dirty legacy clone `~/.local/share/overdeck/deploy` byte-for-byte. NEVER reset, clean, stash, or overwrite `_human-session` WIP.
- Build/install landed `origin/main` from isolated clean clone `~/.local/share/overdeck/deploy-next` with separate lock and queue.
- Keep existing immutable web rollback release.
- Prove installed collector/web/controller and exact deployed source.
- After proof, atomically make clean clone canonical or reconcile service/unit paths. Do not leave two deployment authorities.

Current receipt: emergency deploy completed successfully from isolated clean clone at landed SHA `01cd4d05`; `overdeck-web`, collector, and controller are active; installed web unit points to clean clone release; HTTP 200 proven. Deploy emitted shim-drift hazards for unrelated worktrees; these are real WIP signals and remain preserved, not ignored or modified. Raw canonical path rename was correctly blocked by main-checkout safety enforcement. Therefore owner runtime is restored, but normal session post-land deployment still requires a tested guarded canonical cutover that archives the dirty clone and switches the clean clone without bypassing safety hooks.

## S8 receiver ledger integration

status: DONE
task IDs: #13, #15
source request: Integrate ledger core commit `e7fec1cec` only into `modules/workstation/claude/bin/seat-remote`, `modules/workstation/claude/lib/seat-remote.mjs`, `modules/workstation/claude/tests/seat-remote.test.mjs`, `controller/src/land-retirement.ts`, and `controller/src/land-retirement.test.ts`. Add `--dispatch-key` and `--accept-only`. Receiver MUST derive canonical fingerprint from seat/account/model/normalized forwarded argv/request digest, accept ledger before launch, replay same key/fingerprint with same identity, reject mismatch, and never attach in accept-only mode. Launch MUST use ledger generation fencing. Any replay after accepted or running MUST NOT launch again. Controller MUST send stable key plus accept-only, mark local dispatch only from structured accepted output, and cover crash after receiver acceptance. Run named receiver, ledger, and controller focused tests plus controller typecheck. Preserve WIP. Commit only named files, terse, no co-author.

### Acceptance criteria

- `--dispatch-key` required for `--accept-only`; `--accept-only` only valid for launch.
- Canonical fingerprint includes seat ID, account, resolved model, normalized forwarded argv, and request digest; same semantic request has one ledger identity.
- Receiver writes acceptance before any launch side effect; a matching replay returns structured accepted identity without a second launch; mismatch fails closed.
- `--accept-only` returns structured acceptance and never attaches or launches.
- Launch claims only accepted receipt under generation/lease fencing, marks running only if still owner, and never runs remote launch after a claim cannot be made.
- Controller invokes accept-only first, parses structured acceptance, and marks local dispatch only after acceptance; restart after acceptance does not make a second launch.
- Named test suites and focused typecheck pass cleanly.

### Constraints

- Ledger core is already committed at `e7fec1cec`; do not modify ledger files.
- Preserve existing worktree WIP. Do not reset, stash, clean, or commit docs/spec WIP.
- Commit only five named source/test files after clean verification.

### Execution steps

1. Read receiver, ledger, controller, and existing focused tests.
2. Extend receiver CLI and launch transition around ledger contract.
3. Add structured accept-only integration to controller successor dispatch.
4. Add receiver replay/fencing tests and controller crash-after-acceptance test.
5. Run requested verification; fix all new signals; locally commit named files.

### Current receipt

- 2026-08-13 corrective request: candidate `c892dfa26` passed its focused checks, but compiler/test diagnostics identify unused `dispatch` in `controller/src/land-retirement.test.ts:173` and unused `SEAT_SANDBOX_SETTINGS`, `opts`, `port`, and `implementerUser` in `modules/workstation/claude/tests/seat-remote.test.mjs`. Task #17 is ACTIVE in worktree `/home/user/Projects/overdeck/.worktrees/ci-cd-reliability`; remove only declarations proven unused, or consume values through existing required assertions; do not expand behavior. This requested correction supersedes the broader `store.ts` diagnostic inspection for this task. Re-run focused controller test and typecheck plus seat-remote Node test. Commit only the two named test files with a terse message and no co-author. Preserved docs/spec WIP remains outside commit scope.
- Receiver now parses `--dispatch-key` and launch-only `--accept-only`, derives an immutable canonical request fingerprint, accepts before any launch side effect, returns structured acceptance for controller use, marks launch running under generation/lease fencing before launch effects, and returns replay identity without re-launching when receipt is accepted/running.
- Controller now sends stable dispatch key plus `--accept-only`, validates structured acceptance JSON before marking local intent accepted, and focused crash-after-receiver-acceptance test proves restart makes no second invocation.
- Verification clean: `node modules/workstation/claude/tests/seat-remote.test.mjs`; `node modules/workstation/claude/tests/seat-dispatch-ledger.test.mjs`; `bun test src/land-retirement.test.ts` (13 pass); `pnpm typecheck`; `git diff --check`.
- Local commit `c892dfa26` (`Fence remote seat dispatch`) contains only the five named receiver/controller source and test files; no co-author.
- Plan/spec WIP, including this receipt, remains preserved and intentionally uncommitted because scope required committing only named files.
- Task #17 removed only proven unused bindings from the two named tests. Deterministic fixture `env` values now isolate affected seat-launch tests from process-inherited model settings, preserving each test's intended contract assertion. `node modules/workstation/claude/tests/seat-remote.test.mjs` passed (two unavailable-sudo fixture skips); `bun test src/land-retirement.test.ts` passed 13/13; `pnpm typecheck` passed; temporary `no-unused-vars` analysis and `git diff --check` passed. Scoped local commit `b8c4ed37d` (`Remove unused test bindings`) contains exactly the two named test files; plan/spec WIP remains uncommitted.
- Installed receipt 2026-08-13: reviewed repair `e6f26487543f9f9d4c7096fd17b8ea829439d2a9` is on `origin/main` through guarded landing `58a3c3a8f`; current deployed merge is `08a0b449c196d0c381dad4f494cc38cb86587113`. Installed `/home/user/.claude/lib/seat-remote.mjs` and `seat-dispatch-ledger.mjs` resolve into `/home/user/.local/share/overdeck/deploy` and byte-match `origin/main`. Controller receiver recovery tests pass 14/14, including expired launching-lease reclaim, crash-before-acceptance immediate retry, invalid/non-running receiver rejection, and exactly-once recovery after acceptance. Ledger tests pass; seat-remote environment-dependent tests retain their two documented unavailable-fixture skips. `overdeck-controller.service` is active from the deployed runtime. No model was invoked. Immutable deployment hardening remained paused and untouched.

### Next executable action

S8 receiver ledger integration is landed, deployed, and installed proof is recorded. No next action for this slice.

Security quarantine: NEVER execute `temp-user/k3s-phase3/apply-overdeck-k3s-phase3-existing-cluster.sh`. It verifies only checksums supplied inside the same untrusted package, then executes packaged Python. Require detached signature verification with a pinned trusted key or an out-of-band trusted package digest before extraction or execution. This matches the existing Phase 2 package-authenticity blocker; in-package `SHA256SUMS` is not authenticity.

Bundle security review found a blocking trust-boundary defect in candidate `fcb2ee448`: validator accepts manifest self-declared commit/tree and has no caller-supplied trusted expected identity. A substituted but internally valid bundle for an unapproved Git object can pass. Validation also must not authenticate one mutable path then let consumers reopen changed bytes. Git replacement refs are a separate confirmed identity bypass: with `git replace GOOD EVIL`, creator labels source commit GOOD while archiving EVIL tree/payload, and replacement-aware validator accepts it. Required repair: caller supplies trusted expected commit/tree plus expected bundle digest or detached-signature-verified digest; compare before payload access; disable replacement objects for every Git identity/content operation using a subprocess-scoped fail-closed mechanism; promotion copies/opens the exact verified object into content-addressed immutable storage and consumers use that object; tests cover valid-but-unapproved substitution, malicious replacement refs during creation and validation, and post-validation replacement. Current candidate remains rejected.

Current parallel-wave receipt: all three candidates remain unshipped. Collector commit `351016de8fce5d4542cf82490dff1e0825c0d24e` was rejected because it invented an `events.jsonl` producer contract, hid active/stalled holders behind queued requests, failed to correlate events by operation, and the reviewer observed 35 full-suite failures. Web data made no edits because its governing spec exists only in this design worktree, not the worker base. Bundle commit `929e5a923d33f9e014c3ace8d39a192b4f871c41` was rejected because real-repository creation exceeds `execFileSync` buffering, tracked symlinks are rejected, and its governing spec was absent from the worker tree. Preserve both rejected commits for repair evidence; NEVER land them as-is.

FROZEN 2026-08-14 — bundle repair belongs to the immutable-bundle track; see "Owner decision
2026-08-14". Live next action for this plan is the S5 latency slice (main session).

Next executable actions: repair the bundle using streaming/archive-to-file IO, exact safe symlink preservation, real-repository acceptance, canonical unambiguous digest serialization, collision tests, and fresh security review. Re-dispatch web data with the absolute governing spec in this design worktree. Hold collector landing until the deploy queue owner supplies a real operation-correlated event producer or reduce the adapter contract to claims supported by existing state; never fabricate progress.

## Receiver-ledger core slice

FROZEN 2026-08-14 — S8 descendant; see "Owner decision 2026-08-14".

status: FROZEN (was ACTIVE)
task IDs: unassigned
source request: Implement only `modules/workstation/claude/lib/seat-dispatch-ledger.mjs` and `modules/workstation/claude/tests/seat-dispatch-ledger.test.mjs`. Provide durable atomic dispatch-key acceptance bound to canonical fingerprint, idempotent identity return, mismatch rejection, lifecycle states, generation/lease CAS, expired-launch reclamation, stale-generation rejection, and malformed-receipt fail-closed behavior. Do not edit integration files.

### Acceptance criteria

- State files live under `~/.local/state/overdeck/seat-remote/dispatches/<sha256(dispatchKey)>.json`, with caller-overridable state root for focused tests.
- Receipt writes are atomic: safe hashed name, file sync, rename, and directory sync.
- Concurrent processes produce exactly one accepted identity for one key/fingerprint pair.
- Same key/fingerprint returns that identity; different fingerprint fails closed.
- Valid transitions cover `accepted`, `launching`, `running`, and `failed`; generation and lease compare-and-swap fence stale workers; expired `launching` leases can reclaim.
- Malformed stored receipts fail closed.
- Focused Node tests and syntax checks pass with zero warnings.
- Commit contains only the two requested files, terse message, no co-author.

### Preserved WIP

- Existing untracked CI/CD plans and specs in this worktree remain untouched except this authoritative plan update.
- No `seat-remote.mjs` integration edits in this slice.

### Execution steps

1. Read existing `seat-remote` contract and focused tests.
2. Add ledger core with narrow exports and fail-closed on-disk validation.
3. Add isolated temp-state tests for concurrency, mismatch, reclaim, stale generation, and malformed receipts.
4. Run Node focused tests and syntax checks; resolve every signal.
5. Commit only ledger implementation and test.

### Current receipt

Committed receiver-ledger core as `e7fec1cec` (`Add seat dispatch ledger`), containing exactly `modules/workstation/claude/lib/seat-dispatch-ledger.mjs` and `modules/workstation/claude/tests/seat-dispatch-ledger.test.mjs`; no integration files changed. Focused temp-state concurrency, mismatch, reclaim, stale-generation, lifecycle, and malformed-receipt coverage passes. Both Node syntax checks and focused test pass with zero warnings. Normal command dispatch is externally blocked because `local-gate` cannot index preserved unrelated `temp-user/overdeck/` WIP; direct isolated runner executed exact Node checks successfully. `local-gate --remote-doctor` independently reported unrelated debian1 seat-guard audit and debian3 disk-low failures. All unrelated WIP remains preserved.

## Next executable action

Integrate this narrow ledger from its owning `seat-remote` slice after review; do not alter this committed core without a new focused contract.

## Report-only review — receiver-ledger candidate

status: DONE
task IDs: review `7796bf7fc..b8c4ed37d`
source request: Review `/home/user/Projects/overdeck/.worktrees/ci-cd-reliability` range `7796bf7fc..b8c4ed37d` at high effort, report-only, using one inline pass because Agent fan-out is unavailable.

### Acceptance criteria

- Inspect every range-diff hunk, changed function, caller/callee contract, removed invariant, cleanup angle, and governing instruction.
- Keep only findings backed by concrete failure states; return at most 10 ranked JSON objects.
- Change no candidate implementation; disclose single-pass inline scope.

### Preserved WIP/ref/path

- Path: `/home/user/Projects/overdeck/.worktrees/ci-cd-reliability`.
- Range: `7796bf7fc..b8c4ed37d`.
- Candidate source and unrelated plan/spec WIP preserved; only this authoritative receipt changed.

### Constraints

- No subagents or Agent fan-out.
- Report-only; no source mutation.
- Never execute quarantined untrusted scripts.

### Execution steps

1. Gather range diff, working-tree state, and governing instructions.
2. Inspect hunks and enclosing symbols; trace cross-file state transitions.
3. Check crash, retry, concurrency, fingerprint, lease, generation, accept-only, reuse, simplification, efficiency, altitude, and conventions.
4. Deduplicate and verify concrete failure scenarios.
5. Return ranked findings.

### Current receipt

Single-pass inline review complete without Agent fan-out. Four verified defects remain: controller records accept-only receipt without any later launch; receiver records `running` before launch prerequisites and never recovers resulting failure; stale lock reclamation can delete a successor lock and admit concurrent writers; controller accepts arbitrary receiver state and unsafe negative generation as success. Range whitespace check is clean. Focused tests were reported green with two unavailable-network-namespace fixture skips; tests do not cover these end-to-end failure paths.

### Corrective repair — receiver-ledger review findings

FROZEN 2026-08-14 — S8 descendant; see "Owner decision 2026-08-14". Do not resume the next action below.

status: FROZEN (was ACTIVE)
task IDs: receiver-ledger corrective repair
source request: Repair five verified defects in controller retirement and seat dispatch. Accept-only MUST lead to launch before permanent completion; setup/preflight/auth failure MUST remain retryable; expired `launching` leases MUST be reclaimable; lock reclamation MUST require proof owner PID is dead and MUST NEVER use age alone; controller MUST accept only explicit valid receiver states and reject `failed` or arbitrary states. Preserve generation fencing and legacy behavior. Add deterministic regressions, run focused tests with zero warnings, commit only repair, and do not land or deploy.

#### Acceptance delta

- Controller acceptance is not terminal dispatch completion. Accepted work receives one fenced launch attempt; restart after acceptance cannot strand work or duplicate launch.
- Receiver setup, preflight, and authentication failure cannot leave a permanent `running` or otherwise non-retryable receipt.
- An expired `launching` lease is reclaimed with a higher generation; stale generations remain unable to mutate lifecycle state.
- Lock cleanup requires positive same-host evidence that recorded owner PID no longer exists. Elapsed age alone NEVER authorizes unlinking a lock.
- Controller success parsing uses an explicit allowlist of valid receiver states and valid positive generation. `failed`, unknown, malformed, and arbitrary states fail closed.
- Deterministic tests reproduce each of these five defects and protect legacy non-ledger dispatch behavior.

#### Preserved WIP/ref/path

- Replacement path: `/home/user/Projects/overdeck/.worktrees/ci-cd-reliability-restored` — ABSENT as of 2026-08-14; dead path, do not chase.
- Replacement branch: `wt/ci-cd-reliability-restored`, created from current `origin/main` after prior lane removal.
- Prior candidate commits and rescue refs are historical evidence only; NEVER use them as working bases or copy files from them.
- Existing unrelated WIP remains untouched. Repair commit contains only required source, regression tests, and this authoritative plan receipt.

#### Execution steps

1. Inspect current `origin/main` implementations and focused tests in replacement lane.
2. Add failing deterministic regressions for all five defects.
3. Repair controller acceptance-to-launch flow, receiver failure lifecycle, expired lease reclamation, lock ownership proof, and receiver-state validation while preserving generation fencing and legacy behavior.
4. Run focused receiver, ledger, and controller tests plus controller typecheck with zero warnings.
5. Update receipt with exact results and commit only corrective repair. Do not land or deploy.

#### Current receipt

- 2026-08-13: Replacement lane verified clean at `origin/main`. Governing plan updated before code inspection or edits. Five review findings and acceptance delta recorded; old lane and rescue refs remain historical only.
- Controller now invokes the receiver launch path, marks completion only for explicit `running` with positive safe generation, and rejects `accepted`, `failed`, arbitrary state, zero generation, and negative generation.
- Receiver keeps local setup/preflight/auth/account/archive failures pre-claim, claims immediately before remote launch effects, fences every post-claim failure through `failDispatch`, and marks `running` only after successful doctor proof. Immediate same-clock retry remains possible; expired `launching` and explicit `failed` receipts reclaim with monotonic generation.
- Ledger lock reclamation now requires a valid recorded PID plus `ESRCH` proof that the owner process is dead. Age alone cannot remove a lock; same-file identity still fences replacement races.
- Deterministic regressions cover controller launch completion/state rejection, pre-claim auth retry, expired launching reclaim/stale generation, live-owner lock timeout, and dead-owner lock reclaim.
- Focused verification: ledger Node test passed; receiver Node test passed; controller test passed 14/14; controller typecheck passed; four authoritative `node --check` syntax checks passed; `git diff --check` passed. One parallel receiver run hit transient shared sudo-fixture interference (`seat-scope-sudoers-list`) after two environment skips; immediate isolated rerun and final sequential run passed with no skips or warnings. Editor-only missing-parenthesis diagnostics were stale: the authoritative Node parser accepted all four changed JavaScript files. No dependency change or warning suppression was added.
- Independent correctness re-review found no remaining logic blocker after claim placement and fenced failure repair.

#### Next executable action

Commit exactly the seven corrective source/test/plan files locally. Do not land or deploy.

## Receipt — deploy queue unwedged (2026-08-15)

Status: DONE. Deploys work end to end; queue drained 39 -> 0, web 200,
deployed sha 7cb90cdfc5fbb779f798125c528986311db963eb.

Four independent faults, each blocking every deploy on the machine:

1. Leaked lock descriptor (root cause). `deploy-local.sh` opened the deploy lock
   with `exec {lockfd}>` in its own shell. `pnpm`/`node` children inherited that
   descriptor and outlived killed parents, so the kernel kept the lock held by a
   dead pid forever. Fixed in 2577b5e97: the lock is taken by a dedicated
   `flock --close` parent that re-execs the script, so no deploy child can hold
   it. Waiters whose request marker was drained exit as `deployed-coalesced`.
2. Actions Gateway config missing hard-failed the deploy. Fixed earlier at
   fe0924d5b; the shared main checkout still ran the pre-fix copy and was
   advanced to the landed version.
3. `reaper-notifier.service` restart hard-failed. Guarded in 2577b5e97 so an
   absent unit skips instead of failing.
4. That unit was `bad` — symlinked into a deleted worktree
   (`.worktrees/factory-settings`). Relinked to the deploy clone, matching every
   other unit.

The live deploy clone also held another session's uncommitted requests-intake
work, which correctly blocked deploys. Preserved to origin branch
`wip/deploy-clone-requests-rescue` (16d21c92); nothing discarded; clone returned
to pristine.

Not verified: no full gate suite ran for 2577b5e97 (direct land under the
runtime-first rule). The change is confined to `packaging/deploy-local.sh` and was
proven by four live deploy runs.

## Receipt — gate scoped for documentation-only landings (2026-08-15)

Landed `78bd0fdca` (commit `037a0cea6`), deployed at sha `78bd0fdc`, web 200 on
`http://127.0.0.1:31337`.

`gate0_strict` now classifies the branch diff before running any check. When every
changed path is documentation (`.md`, `.mdx`, `.txt`, `.rst`, `LICENSE*`), it emits a
`gate.complete` event with item `docs-only` and exits success without installing
dependencies, typechecking, building or testing. Everything else runs the full gate.

Fail-closed by construction: the diff base is `merge-base HEAD origin/main`, falling
back to `gate0_task_base`; an unresolvable base, a failed `git diff`, an empty diff, or
any unclassified path all run the full gate. `--no-renames` keeps a code-to-docs rename
from hiding the deleted code path. `GATE0_DOCS_SCOPE=0` disables the short-circuit.

Verification: `modules/harness/lib/test-gates.sh` — 51 pass / 2 fail, against a
baseline of 47 pass / 2 fail on unmodified files; the same two pnpm workspace tests
fail before and after this change and are unrelated to it. Four new cases cover
docs-only skip, code-only run, mixed docs+code run, and code in an earlier branch
commit. `python3 -m pytest modules/harness/factory/tests/ -q` — 246 passed.

Not verified: no real docs-only landing has yet gone through the queue's conductor
since the change deployed; the behavior is proven by fixture repos only.

## Receipt — S1 boundary 1: deploy progress state + status command (2026-08-15)

Landed `8a82b4ef7` and `7a7f299ad` (direct land, queue bypass authorized for FIRE work).
Deployed and live; proof `deploy-status` prints `Idle. Last deploy finished successfully
0s ago: deployed e99f2d4f` at 16:06.

Behavior:
- `packaging/deploy-local.sh` publishes an atomically-written record (tmp+mv) to
  `~/.local/state/overdeck/deploy-status.json`: schema, state
  (`running|finished|failed`), step, plain-language detail, sha, pid, started_at,
  updated_at. Transitions: preparing, dependencies, building-web, installing,
  restarting, and terminal deployed / docs-only / coalesced / failed.
- Only the lock holder writes; queue depth is read from the existing queue directory,
  so no queue files are duplicated.
- A failed status write warns and never aborts a deploy — observability is not a
  delivery step.
- `deploy-status` (installed on PATH via the symlinked `~/.claude/bin`) reports what is
  happening in owner language, detects a deploy whose pid is gone as stalled rather than
  running, and `--json` returns the raw record for the `/ci` page to consume next.

Verification: `tests/os/deploy-status.test.sh` PASS=7 FAIL=0 (fixtures only, no deploy
started); `bash -n` on deploy-local.sh; two live deploys.

Not verified: stall detection has not been observed against a real killed deploy, only
against a fabricated dead pid. Per-step durations are recorded but no reader shows them
yet (that is boundary 2).

Discovered, not fixed here: the shared main checkout `/home/user/Projects/overdeck` is
parked on `7b3f5d188`, whose `deploy-local.sh` predates the deploy-clone hand-off, so a
deploy launched from that checkout runs the old script end to end and publishes no
status. It holds 42 uncommitted files from other sessions, so it is not mine to move.
