diff --git a/.llm/2026-08-08-fable5-remediation-seed-run.md b/.llm/2026-08-08-fable5-remediation-seed-run.md
new file mode 100644
index 0000000000..8b81a1d5c2
--- /dev/null
+++ b/.llm/2026-08-08-fable5-remediation-seed-run.md
@@ -0,0 +1,16 @@
+# 2026-08-08 — Fable 5 remediation roadmap seed run (session record)
+
+Run: `.llm/runs/plan-fable5-remediation-roadmap--seed/` · PR #1347 (draft, never merges itself)
+· branch `plan/fable5-remediation-roadmap` · baseline `fac9e339042c` (unchanged at lock).
+
+One-session planning-only seed run (Fable 5 · high supervisor; PLAN-EVAL/IMPL-EVAL owner-waived;
+Claude Workflows with 25 Opus 5 research/drafting subagents across 3 pre-committed workflow
+scripts, 0 errors, ~3.8M subagent tokens). Produced the complete long-range remediation plan
+under `fable-5-remediation-plan/`: 19-artifact cited corpus, synthesis, master plan with a
+12-fork owner sweep, milestone train (two inserted milestones via house rename), 41 issue
+drafts in three milestone directories, RFC-A/RFC-B drafts, 16 amendment blocks, Wave-7 design,
+implementation handoff. Zero GitHub board mutation; owner ratifies filing later.
+
+Lessons worth promoting (candidates, not promoted here): (1) drafting agents that re-verify
+corpus claims at source caught six corrections a prose-only pass would have shipped; (2) the
+"commit workflow scripts before execution" rule made a 25-agent run auditable from the PR alone.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/context-pack.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/context-pack.md
new file mode 100644
index 0000000000..deddeea9ae
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/context-pack.md
@@ -0,0 +1,32 @@
+# Context Pack — plan-fable5-remediation-roadmap--seed
+
+## Status: COMPLETE (Stage H owner-ratified filing, 2026-08-08).
+
+Planning-only seed run; all charter deliverables landed under
+`.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/`:
+
+- `MASTER-PLAN.md` — product bar, principles, target architecture, DAG, sequencing, **12-fork
+ owner sweep (§7)**, risks, exclusions.
+- `ISSUE-DEDUP-AND-SUPERSESSION.md` — dispositions for every relevant existing issue; 41 new
+ drafts tabulated; Stage-E ledger (§3) with six corpus corrections + ownerless items.
+- `MILESTONE-TRAIN.md` — two-milestone insertion via house rename; complete move ledger.
+- `milestones/{0.0.6,0.0.7,0.0.8}-*/` — 41 complete issue drafts (all DRAFT-marked).
+- `rfcs/RFC-A-sdk-client-composition.md`, `rfcs/RFC-B-command-composition-kit.md`.
+- `EXISTING-ISSUE-AMENDMENTS.md` — 16 quotable amendment blocks.
+- `WAVE7-AND-AGENT-ADOPTION.md` — A/B/C measured smoke design; T7-01 exit-gate draft.
+- `IMPLEMENTATION-HANDOFF.md` — filing gate, first PR groups A–D, harness profiles, brief
+ skeleton.
+- `SYNTHESIS.md` + `research/` — 19-artifact cited corpus (waves, board, repo, external).
+
+## Key facts a resuming session must not re-derive
+
+- Draft PR **#1347** is the commit trail; labels moved research→plan at lock. Never merge it.
+- PLAN-EVAL + IMPL-EVAL **owner-waived** (drift D-2); no evaluator was launched; the waiver does
+ NOT extend to future implementation runs.
+- **GitHub filing completed after owner ratification.** The live board is authoritative; see
+ `fable-5-remediation-plan/FILING-LOG.md` for milestone and issue receipts.
+- Baseline `fac9e339042c` verified unchanged at lock. Workflows (committed pre-execution):
+ `wf_e2194004-808`, `wf_03b88126-e7e`, `wf_ebfe8327-306` — 25 Opus 5 subagents total, 0 errors.
+- Six corpus corrections (dedup §3.1) override the raw research files where they conflict.
+- 41 live issues: #1348–#1388. RFC entry points: #1348 and #1361. Implementation sequencing
+ remains forks F1–F12 in `MASTER-PLAN.md` §7.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/drift.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/drift.md
new file mode 100644
index 0000000000..26a217a8bd
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/drift.md
@@ -0,0 +1,39 @@
+# Drift Log — plan-fable5-remediation-roadmap--seed
+
+Append-only. Newest last.
+
+## D-1 (2026-08-08, minor, owner directive)
+
+Owner overrides `planning_decisions` effort from lane-policy default **low** to **high** for this
+run (long-range meta-framework roadmap). Recorded in `supervisor.md` § overrides.
+
+## D-2 (2026-08-08, significant, owner directive)
+
+Owner explicitly **waives PLAN-EVAL and IMPL-EVAL** for this planning-only seed run. The seed-run
+stage G hard stop (`plan-eval.md = PASS` before any board mutation) is vacuously safe because the
+run performs **zero board mutation** by charter; the owner personally reviews the plan and decides
+on later adversarial passes and filing. No formal evaluator, OpenHands, OpenRouter, or substitute
+evaluator session is launched. Recorded in `supervisor.md` and `worklog.md`.
+
+## D-3 (2026-08-08, minor, owner directive)
+
+Claude Workflows for research/synthesis fan-out run **Opus 5** subagents (owner directive),
+overriding the `claude_workflow` lane default (Opus 4.8 · low). Contributors only, never
+evaluators; supervisor reviews all output before commit. This also invokes the CLAUDE.md
+documentation-authoring-adjacent exception: the workflow lane touches **no `packages/`/`plugins/`
+source** — output is run-dir planning artifacts only.
+
+## D-4 (2026-08-08, minor, charter deviation from seed-run exemplar)
+
+Stage F (adversarial) and stages G–H are not executed in this run by owner direction. The run ends
+at plan-lock + handoff artifacts (stages A–E + I equivalent). The deliverable set is the
+`fable-5-remediation-plan/` subtree requested in the charter, which supersedes the exemplar's
+folder taxonomy (permitted — seed-run.md fixes stage contracts, not folder names).
+
+## D-5 (2026-08-08, significant, owner ratification)
+
+The owner subsequently ratified the locked plan in-turn and explicitly instructed Stage-H board
+filing. This supersedes D-4 only for Stage H: `FILING-MANIFEST.md` was committed before mutation,
+the milestone train was executed, 41 issues were filed, and existing-owner amendments were added.
+PLAN-EVAL and IMPL-EVAL remain owner-waived for this planning/filing run; the waiver does not carry
+into implementation. See `fable-5-remediation-plan/FILING-LOG.md`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/CROSS-RFC-REVIEW.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/CROSS-RFC-REVIEW.md
new file mode 100644
index 0000000000..255fb7587c
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/CROSS-RFC-REVIEW.md
@@ -0,0 +1,114 @@
+# Cross-RFC PLAN-EVAL review — RFC-A (#1390) × RFC-B (#1389)
+
+## CYCLE 2 OUTCOME (2026-08-08): **BOTH APPROVED / PASS** — handoff to root for the Qwen pass
+
+Same evaluator session re-evaluated every cycle-1 finding mechanically against the remediated
+texts and worktree source; independent evidence executed (RFC-A fixture `deno check` exit 0;
+section-level anchor verification for all 17 findings).
+
+| RFC | Accepted content SHA | Branch HEAD at verdict | Verdict artifact |
+| --- | --- | --- | --- |
+| RFC-A (#1390) | **`78a7cecd1d5eaafa7a65bc25a21af497567128dc`** | `14b5c858c` (atop author hygiene `9f45404ac` — run artifacts only, verified) | `plan-eval.md` @ `14b5c858c` |
+| RFC-B (#1389) | **`c98c08adabbd992a557ff7c596deae68b9c9cd62`** | `57b51128f` (atop author handoff `6c2043d91`) | `plan-eval.md` @ `57b51128f` |
+
+All F-A1–F-A10 and F-B1–F-B7 resolved (several beyond the asked bar: RFC-A's epoch/reconnect law
+with a preparation-count-2 fixture; RFC-B's per-provider claim-algorithm table). Lane audit:
+`type:test` added to #1390 (compile-only fixture in `packages/sdk/tests/`); `ci:skip-*` ruled
+still valid on both. Both PRs moved `status:plan-eval` → `status:augment-review`.
+
+**Qwen 3.8 Max adversarial acceptance brief (root-owned, separately authorized):** evaluate both
+RFCs at the accepted content SHAs above, unoriented; the two `plan-eval.md` files carry the full
+cycle-1 findings + cycle-2 resolution evidence; the remaining decision surface is the FCP
+question sets (8 in RFC-A, 4 in RFC-B — all adjudicated policy-safe) plus the cross-RFC
+obligations below (§2 amendments, §3 v2-migration epic). Unresolved-by-design: RFC-A FCP Q5
+wrapper-vs-memo, Q6 GET preserve-vs-retire (v2 RFC), metadata Stage 1b ownership; RFC-B FCP
+Q1–Q4. One cosmetic cross-RFC inconsistency remains (frontmatter `target-milestone` convention —
+harmonize at numbering). No board object was created/mutated by this evaluator in either cycle.
+
+The cycle-1 record below is retained for provenance; its "stop" handoff is superseded.
+
+Evaluator: Claude Fable 5 · high, owner-designated cross-family PLAN-EVAL authority
+(2026-08-08). Verdicts of record: `plan-eval.md` in each RFC run dir — **both
+CHANGES_REQUESTED (FAIL_PLAN cycle 1)**: RFC-A @ `f1a29fe1a` (F-A1–F-A10), RFC-B @ `122301d25`
+(F-B1–F-B7). Deep-dive delegations: workflow `wf_b3416478-edf` (3× Opus 5 · xhigh, read-only,
+script committed pre-execution in this run's `workflows/`). This artifact records the
+cross-RFC obligations; it performs **no board mutation**.
+
+## 1. Composition verdict: the two RFCs compose cleanly
+
+- **No circular dependency.** Shared prerequisite #1350 (0.0.7) is one-way: RFC-A stage 1 and
+ RFC-B stage 0 both consume it; neither RFC depends on the other's implementation. RFC-B's
+ service→database edge is new but acyclic (F-B2 requires it be *declared*).
+- **No duplicated error policy.** RFC-A's preparation failures are local, pre-dispatch, never
+ contract errors; RFC-B's command errors are route-opt-in contract errors. Both defer
+ client-visible typing to #1350's literal-preserving spelling. Coherent — with one shared
+ obligation: whichever lands first establishes that spelling; the other must reuse it (F-B7b).
+- **No duplicated context/telemetry policy.** RFC-A reserves trace headers to the transport;
+ RFC-B persists validated W3C context in rows and uses producer/consumer spans — disjoint
+ layers, consistent with the existing telemetry vocabulary. Both correctly avoid inventing a
+ second correlation scheme; RFC-B's stricter-than-existing attribute redaction (F-B7h) is a
+ vocabulary-cleanup question, not a conflict.
+- **Shared `(family, major)` protocol vocabulary** (RFC-A ↔ #928) does not leak into RFC-B.
+- **One latent intersection to watch at implementation:** RFC-A's `idempotency-key` header
+ allowance and RFC-B's envelope `idempotencyKey` are different layers (transport header vs
+ command input). No conflict today; the future HTTP-idempotency recipe should name which one is
+ authoritative for services that accept both.
+
+## 2. Sequencing vs the filed board (#1348–#1388, PR #1347)
+
+Coherent as filed: RFC trackers #1348/#1361 in 0.0.6 (ratification); RFC-A implementation
+children #1349–#1353 in 0.0.7; RFC-B children #1362–#1364 (+#1363 umbrella) in 0.0.8 with #1350
+(0.0.7) as stage 0. The milestone descriptions carry the authority banner. Frontmatter
+`target-milestone` semantics need one clarifying line in each RFC (RFC-A says 0.0.7 = impl,
+RFC-B says 0.0.6 = ratification — pick one convention; F-B7d).
+
+**Existing children are sufficient — no duplicates needed.** Required amendments (owner-ratified,
+not executed by this evaluator):
+
+- **#1351** — add: `@orpc/opentelemetry` rename decision (already available on v1, F-A8d);
+ exact-pin vs lock-only-pin policy for the family move (F-A8, caret manifests); the GET-dedupe
+ no-op trap as an acceptance row (F-A8c).
+- **#1349** — add: prepared-header channel statement, private-port location + `deno doc`-absence
+ gate, server key-algebra surfaces (F-A2/A6/A7) once the amended RFC lands.
+- **#1350** — stage-0 body reconciliation (metadata initialization + literal-preserving spelling
+ shared by both RFCs) — already planned by RFC-A stage 0; keep.
+- **#1362/#1363** — inherit F-B2's relay-ownership decision and F-B1's claim algorithm once
+ amended; #1363's child table gains the `PrismaTransactionClient` generator deliverable (F-B6).
+- **New reconciliation candidate (owner decision):** `@netscript/queue`'s runtime `ensureSchema`
+ DDL vs RFC-B's no-hidden-migrations doctrine (F-B3) — file only if the owner adopts the rule
+ repo-wide rather than kit-scoped.
+
+## 3. oRPC v2 migration: separate RFC epic — recommended, with exact scope
+
+**Recommendation: yes, one new RFC tracking issue (rfc-form, 0.0.6-adjacent ratification,
+implementation unscheduled)** after RFC-A acceptance — not filed by this evaluator. Verified
+facts anchoring it: v1.15.0 is latest stable (shipped *after* beta.26 — v1 actively maintained);
+v2 wire protocol incompatible; 74 non-test files reference `@orpc/*`.
+
+Scope (RFC-A's gate list + the four evaluator additions): keep-or-drop **GET** decision
+(`allowMethods` + Sec-Fetch-Mode CSRF story — direction corrected per F-A8a); re-implement
+`inferRPCMethodFromContractRouter` (removed in v2); dedupe-effectiveness gate (GET-only filter);
+`defineMeta` migration for `NetScriptProcedureMeta`; `errorStatusMap` split vs #1350 spelling;
+middleware-dedup removal audit across the 74 files; OTel span-topology/double-span proof
+(package rename excluded — that is #1351, v1); serializer/streaming/Fresh/desktop parity;
+TanStack key re-verification; coordinated-rollout vs parallel-endpoints owner decision (RFC-A
+Q9); exact-family pinning discipline; full conformance-suite re-run on the v2 adapter.
+Dependencies: RFC-A accepted + stages 2–3 landed (the ports are the migration boundary); owner
+beta-risk decision; #1351 complete.
+
+## 4. Handoff state (root orchestrator)
+
+- **Both verdicts request changes → per the evaluator contract, this session stops here.** The
+ Qwen 3.8 Max adversarial pass waits until both RFCs reach APPROVED.
+- Resume the **RFC-A Codex generator** (thread `019fe242-2bd9-7ff3-8044-bd9d09585397`) with PR
+ #1390's finding list (F-A1–F-A8 required; F-A9/A10 may ride along). Branch
+ `docs/rfc-sdk-client-contribution`, HEAD `f1a29fe1a` (evaluator commit atop `7be129d80`).
+- Resume the **RFC-B Codex generator** (thread `019fe242-2c45-7e03-a428-eebfb968eda0`) with PR
+ #1389's list (F-B1–F-B4 required; F-B5–F-B7 as batch edits). Branch
+ `docs/rfc-command-composition-kit`, HEAD `122301d25` (evaluator commit atop `62304176f`).
+- Both PRs moved to `status:plan`; restore `status:plan-eval` with the amended handoffs. Cycle 2
+ is the last before escalation (two-FAIL_PLAN limit).
+- Evaluator wrote only: the two `plan-eval.md` files (pushed with explicit refspecs to their own
+ branches), the two PR comments + label moves, this artifact, and the seed-run worklog note. No
+ RFC text, no product code, no issues/epics/milestones touched. RFC-B's pre-existing dirty
+ `codex-thread-ids.md` was left untouched.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/EXISTING-ISSUE-AMENDMENTS.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/EXISTING-ISSUE-AMENDMENTS.md
new file mode 100644
index 0000000000..89e4c71c91
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/EXISTING-ISSUE-AMENDMENTS.md
@@ -0,0 +1,929 @@
+# Existing-issue amendments — quotable text for owner ratification — DRAFT (no GitHub mutation; owner ratification pending)
+
+Pack T6 deliverable. Every block below is **proposed text the owner can paste verbatim**. Nothing
+here has been posted, edited, closed, labelled, or milestoned on GitHub. Each entry states: target
+issue, amendment type (comment vs body edit), the full text, the rationale, and what it prevents.
+
+**Rules honoured by every amendment.**
+
+- Comments are additive. Where a body edit is proposed it is marked **BODY EDIT** and states which
+ section is replaced, so nothing is silently deleted.
+- No amendment ticks an existing acceptance checkbox. Close-gate reads checkboxes under
+ `## Acceptance` / `## Acceptance criteria` / `gate:`-prefixed boxes
+ (`github-conventions.md` §4.3); a comment cannot and must not discharge one.
+- No amendment adds a closing keyword to an epic (`AGENTS.md`; conventions §4.7).
+- Proposed acceptance boxes are written short and stable so they can be copied verbatim into a
+ ```acceptance-evidence``` mapping.
+- Draft-IDs (`T3-02`, `T4-06`, …) refer to sibling drafts in this seed run. When those are filed the
+ owner substitutes the real `#N`; until then the draft-ID is a placeholder, not a link.
+- Facts marked **re-measured** were executed in this worktree at baseline `fac9e339042c` on
+ 2026-08-08 and supersede corpus figures dated 2026-08-04.
+
+Contents: [#1278](#1278) · [#1276](#1276) · [#1279](#1279) · [#1275](#1275) · [#1245](#1245) ·
+[#1333](#1333) · [#1335](#1335) · [#1210](#1210) · [#1208](#1208) · [#922](#922) · [#301](#301) ·
+[#1126](#1126) · [#1325/#1326/#1329](#1325-1326-1329) · [#979](#979) · [#1090/#1197](#1090-1197) ·
+[board hygiene](#board-hygiene)
+
+---
+
+
+
+## 1. #1278 — Type soundness ratification (0.0.6, `type:umbrella`)
+
+**Amendment type:** comment (additive). No body rewrite — the inventory A/B/C/D structure stays.
+
+**Proposed comment text:**
+
+> **Epic-of-record consolidation + re-measurement (2026-08-08).**
+>
+> This issue is the epic of record for type soundness. #1276 covers the same 2026-08-04 owner
+> directive with a different organisation and is being closed as superseded; its unique content is
+> folded in below so nothing is lost.
+>
+> **Measured numbers folded in from #1276** (as recorded there, 2026-08-04): 56 `as unknown as`
+> occurrences across `packages/` + `plugins/`; 8 `deno-lint-ignore no-explicit-any` suppressions;
+> 7 ratified `quality:scan` allowances (6 in `packages/cli`, 1 in
+> `plugins/workers/streams/producer.ts`); doc line references `web-layer/query-bridge.md:259`
+> (compiler refusal printed at 276-277), `reference/contracts/index.md:32`,
+> `reference/triggers/index.md:310`.
+>
+> **Tranches folded in from #1276**, retained as phases of this epic so deferral stays visible:
+> T1 public surface first (eliminate `any` from exported types); T2 the documented workarounds
+> (#1245 + #1249, docs update lands with the fix); T3 the ratified `quality:scan` allowances
+> ("pending package-boundary unification is a plan, not a resting state"); T4 production
+> `as unknown as`; T5 test-side casts; T6 keep it fixed (extend `quality:scan`).
+> #1276's constraints carry over: framework source runs as WSL Codex slices per `CLAUDE.md`, docs
+> updates follow each fix on the docs lane, and "no suppression-as-fix" — a new
+> `deno-lint-ignore` / `as unknown as` introduced to green a gate is a review-blocking finding.
+> #1276 also names #1255 (`page.layer.delivery` span attribute misreport) as subsumed, alongside
+> #1245 and #1249.
+>
+> **Re-measurement at `fac9e339042c` (2026-08-08).** Four inventory items have moved since the
+> 2026-08-04 measurement and this epic should be re-scoped before it is scheduled:
+>
+> - Inventory A: `web-layer/query-bridge.md` no longer contains `as unknown as`, and
+> `BaseContractProcedure = Readonly<{ ~orpc: any }>` no longer appears in
+> `reference/contracts/index.md` — grep for `as unknown as` and for `~orpc` returns nothing at
+> this baseline. The only remaining docs hit is `reference/triggers/index.md:310`
+> `const observedEvents: any[] = [];` and its executable twin
+> `docs/site/reference/triggers/examples_test.ts:65`.
+> - Inventory B: `packages/fresh/src/application/form/_internal/runtime-types.ts` has **zero**
+> matches for `as unknown as` / `as any` / `quality-allow` today; the "2 casts" line item is
+> stale. The only remaining cast in the whole web layer is
+> `packages/fresh/src/application/builders/define-page/builder/route-support.ts:96`.
+> - Inventory C: the allowance count is **7** under the default `quality:scan` roots but **10**
+> under `--root packages --root plugins`, so three allowances already sit outside the ratified
+> set with nothing reporting the delta.
+> - Inventory D: there are **6** soundness test files, not ~19 —
+> `packages/plugin-{workers,sagas,triggers,auth,ai}-core/tests/contracts/*-contract-soundness_test.ts`
+> and `plugins/workers/services/src/routers/health-soundness_test.ts`. They are already exempt by
+> construction: `.llm/tools/quality/scan-code-quality.ts:87` excludes `_test.ts`.
+>
+> **Correction to a shared assumption.** #1276 T6 states that `quality:scan` "covers
+> `packages/cli/src` + `plugins` only". That is true of the default task
+> (`scan-code-quality.ts:18`), but `quality:scan:repo` already covers all of `packages/` +
+> `plugins/` and runs on push-to-main and a Monday 07:17 UTC cron
+> (`.github/workflows/code-quality.yml:50-59`). Root scope is not the gap. The gaps are: the
+> `explicit-any` rule cannot tell an exported type from a local one
+> (`scan-code-quality.ts:51`); `// quality-allow:` accepts any free-text reason with no issue id
+> (`:136`); `--max-allow` exists (`:173-181`) but is passed by no task and no workflow, so the
+> allowance budget is unbounded; and no Markdown file is ever opened (`:87`).
+>
+> **Inventory C is being filed as its own trackable child** so the guard rail can land and be
+> gated independently of the burn-down. That child carries `Part of #1278`.
+
+**Rationale.** #1278 holds the milestone (0.0.6) and the richer acceptance shape; #1276 holds the
+only measured numbers and the tranche decomposition. Folding rather than choosing keeps both. The
+re-measurement is not optional: three of #1278's own inventory line items no longer describe the
+repository, and scheduling against them would produce slices with nothing to fix.
+
+**What it prevents.** (a) Closing #1276 losing the 56/8/7 counts and the T1–T6 decomposition;
+(b) a remediation slice opening `runtime-types.ts` or `query-bridge.md` and finding the work already
+done — the #1245 failure mode repeating at epic scale; (c) an implementer building the Inventory C
+gate on the false premise that widening roots is the fix.
+
+---
+
+
+
+## 2. #1276 — epic(quality): ratify and eliminate unsound types (Backlog / Triage)
+
+**Amendment type:** comment, then owner closes as **not planned** (duplicate). Per
+`.github/labels.yml` header rules, a not-planned closure **removes** the `status:` label rather than
+setting `status:shipped`.
+
+**Proposed comment text:**
+
+> **Closing as superseded by #1278.**
+>
+> #1278 (`Type soundness ratification`, milestone 0.0.6) and this issue record the same 2026-08-04
+> owner directive over the same evidence set. #1278 is the epic of record because it carries the
+> release milestone and the inventory A/B/C/D acceptance shape.
+>
+> Nothing from this issue is discarded. Its unique content — the measured counts (56
+> `as unknown as`, 8 `deno-lint-ignore no-explicit-any`, 7 ratified `quality:scan` allowances with
+> their rationale strings), the exact doc line references, the T1–T6 tranche decomposition, the
+> "no suppression-as-fix" constraint, the WSL-Codex lane constraint, and the subsumption of #1245 /
+> #1249 / #1255 — has been folded into #1278 verbatim in a comment dated 2026-08-08. The T1–T6
+> tranches survive there as phases so each remains independently deferrable.
+>
+> Superseded by #1278. No work is lost; track type soundness on #1278.
+
+**Rationale.** `github-board-open.md` §4.2 confirms the duplication by reading both bodies. Two
+umbrellas over one directive means two planning surfaces and two chances to schedule the same slice.
+
+**What it prevents.** Two parallel type-soundness programs; a slice filed against #1276's T-numbers
+that duplicates a slice filed against #1278's letter-numbers.
+
+---
+
+
+
+## 3. #1279 — docs: migration chapter (0.0.6, `type:umbrella`)
+
+**Amendment type:** comment (additive), plus a **proposed milestone move** the owner executes
+separately.
+
+**Proposed comment text:**
+
+> **Epic-of-record consolidation (2026-08-08).**
+>
+> This issue is the epic of record for the migration chapter. #1275
+> (`epic(docs): migration chapter — migrate-from guides, capability equivalence matrix, and
+> end-to-end migration recipes`, Backlog / Triage) is the same scope under different wording and is
+> being closed as superseded. Its framing is folded in here so nothing is lost: **migrate-from
+> guides**, a **capability equivalence matrix**, and **end-to-end migration recipes** are the same
+> three deliverables as this issue's per-framework guides / compatibility matrix / e2e recipes.
+> Where the two bodies differ in emphasis, treat #1275's "capability equivalence matrix" as the
+> normative name — it states what the matrix is for.
+>
+> **Proposed milestone change, for owner decision.** Migration documentation is post-remediation
+> marketing surface, not remediation. Recommendation: move this issue out of 0.0.6 to the late
+> train, so 0.0.6 stays the verification + docs-accuracy + soundness cut. This is a scheduling
+> proposal only; no scope changes.
+
+**Rationale.** `github-board-open.md` §4.2 lists #1275/#1279 as the second confirmed duplicate pair.
+The SYNTHESIS milestone-train direction keeps 0.0.6 as the verification/docs/soundness cut; a
+migration chapter cannot be written truthfully until the seams it documents stop moving.
+
+**What it prevents.** A migration guide authored against seams that the typed-seam and generation
+milestones are about to change — the most expensive class of docs rework.
+
+---
+
+
+
+## 4. #1275 — epic(docs): migration chapter (Backlog / Triage)
+
+**Amendment type:** comment, then owner closes as **not planned** (duplicate); remove the `status:`
+label on closure.
+
+**Proposed comment text:**
+
+> **Closing as superseded by #1279.**
+>
+> #1279 (`docs: migration chapter — per-framework guides, compatibility matrix, and e2e migration
+> recipes`) is the same scope and carries a release milestone. This issue's three deliverables —
+> migrate-from guides, capability equivalence matrix, end-to-end migration recipes — have been
+> folded into #1279 in a comment dated 2026-08-08, with "capability equivalence matrix" adopted as
+> the normative name for the matrix.
+>
+> Superseded by #1279. Track the migration chapter there.
+
+**Rationale + prevention.** Same as #1276/#1278: one prose-only umbrella per topic, or the board
+grows two plans for one chapter.
+
+---
+
+
+
+## 5. #1245 — fix(fresh/query): island query types reject the package's own documented patterns
+
+**Amendment type:** **BODY EDIT** — replace the three-boundary scope with the remnant — plus a
+comment recording why. The body edit is required here rather than a comment because the current body
+*describes work that is merged*, and a comment does not stop a reader from implementing it.
+
+**Proposed comment text (post first, then edit the body):**
+
+> **Rescope: ~75% of this issue landed in #1265 (2026-08-04, `77c034c33`, closing #1252).**
+>
+> Re-verified against `main` at `fac9e339042c` on 2026-08-08. Of the three boundaries this issue
+> was filed on, only fragments remain:
+>
+> - Boundary 1 (`initialDataUpdatedAt` absent from `IslandQueryOptions`, TS2353) — addressed by
+> #1265.
+> - Boundary 2 (`createNetScriptQueryClient()` returning a `QueryClient` typed as the narrower
+> `QueryClientPort`, TS2551 + TS2345) — addressed by #1265;
+> `docs/site/web-layer/query-bridge.md` no longer carries the `as unknown as IslandQueryClient`
+> cast.
+> - Boundary 3 (`IslandQueryResult` missing `isRefetching` / `isFetching`, TS2339) — addressed by
+> #1265.
+>
+> Leaving this issue open at its filed scope would send a remediation slice to re-implement merged
+> work. The body is being edited down to the remnant; nothing is being ticked or claimed complete.
+>
+> **Remnant scope (what this issue now owns):**
+>
+> 1. `getIslandQueryClient()`'s `@throws` JSDoc documents a guard the implementation does not have
+> (`packages/fresh/src/application/query/…/query-client.ts:26-27`). This is published through
+> `deno doc`, so the wrong contract ships to consumers.
+> 2. The `clientKey` falsy-input asymmetry between
+> `packages/sdk/src/query/query-factory.ts:174` and `packages/sdk/src/ports/query-factory.ts:98`.
+> 3. Regression tests for both, so the JSDoc and the body cannot diverge again.
+> 4. A consumer migration note: `rickylabs/eis-chat` carries six copied casts written against the
+> pre-#1265 types. The note must say which casts to delete and against which version, so
+> eis-chat-class apps can drop them rather than carry them forward.
+
+**Proposed replacement `## Acceptance` section (BODY EDIT):**
+
+> ## Acceptance
+>
+> - [ ] `getIslandQueryClient()`'s `@throws` matches the implementation, or the guard is added.
+> - [ ] The `clientKey` falsy-input asymmetry between the SDK query factory and its port is resolved.
+> - [ ] Regression tests cover the documented throw contract and the falsy `clientKey` case.
+> - [ ] `deno doc` output for `getIslandQueryClient` states no guard the body lacks.
+> - [ ] A consumer migration note names the casts an eis-chat-class app can now delete and the
+> version they became unnecessary in.
+> - [ ] `docs/site/web-layer/query-bridge.md` is simplified where it documented a boundary that
+> #1265 removed.
+>
+> Related: #1265 (merged 2026-08-04, `77c034c33`), #1252 (closed by it), #1210 (discovery source),
+> #1278.
+
+**Rationale.** `research/repo-audit/web-layer.md` §13 verified the merge by running the checker
+against current source, not by reading the issue. `github-board-open.md` §7 lists #1245 as the owner
+for island query type gaps, so the issue must stay open — but at its true remaining size.
+
+**What it prevents.** The single highest-probability waste in this roadmap: a slice re-implementing
+#1265. It also prevents the opposite error — closing #1245 outright and losing the `@throws` defect
+and the consumer migration note, which nothing else owns.
+
+---
+
+
+
+## 6. #1333 — fix(scaffold/frontend): make the default app an idiomatic eis-chat-grade reference (0.0.5, p0)
+
+**Amendment type:** comment (additive). **Not a body rewrite** — the ten existing acceptance boxes
+stay exactly as written; this comment proposes additional boxes for the owner to append if accepted.
+
+**Proposed comment text:**
+
+> **Acceptance detail expansion (2026-08-08, seed-run proposal — additive).**
+>
+> The ten acceptance boxes here are correct but under-specified in the places where measured agent
+> runs actually drifted. The pre-plan's expansion list, grounded against current source, proposes
+> these as *additional* boxes. Existing boxes are unchanged and none is ticked.
+>
+> - [ ] The default app's primary resource route is implemented contract-first via
+> `withRouteContract`, not a hand-written handler.
+> - [ ] Data reaches the page through the typed SDK on a cache-first path, not a direct service
+> call from the route.
+> - [ ] `withResource` appears in the default app with a shared resource refined per layer.
+> - [ ] Route params and search params are typed (`withPathParams` / `withSearchParams`), not
+> parsed from strings.
+> - [ ] Route-local groups are demonstrated: `(_components)`, `(_islands)`, **`(_shared)`** and
+> **`(_lib)`**.
+> - [ ] The generated app's own quality gate fails on `any` in app code, so a consumer inherits the
+> no-`any` rule rather than reading about it.
+> - [ ] The four seams are visibly distinct in the generated tree — DB model, API contract, route
+> contract, view model — with the narrowing between them shown, not implied.
+> - [ ] Loading, error, empty and success states are executable code in the default route, not
+> prose.
+> - [ ] The default app links to `/design` and `/design/composition` from a place a reader reaches
+> without being told to.
+>
+> **Why these and not others.** Every one of them names a surface a measured Wave-6 run failed to
+> reach: R2 (`workflow-builder-kimi-k3-max`, canary.13) shipped a **676-line `LoomCanvas.tsx`**
+> with product `QueryIsland` / `withForm` / Fresh-UI feature-loop adoption at **0 / 0 / 0**;
+> `definePage` and fresh-ui appear only in scaffold examples, never in Loom's product routes.
+> The repo audit confirms the mechanism: `withResource`, `withForm`, `withRouteContract`,
+> `withSearchParams`/`withPathParams`, `withStreaming` and `definePartial` have **zero** examples
+> in the generated app — which is the surface `agent-conventions.ts` designates as canonical for
+> agents, and `agent-conventions.ts:133` instructs agents to use `withForm` while listing no local
+> reference for it.
+>
+> **Discharged dependency.** This issue's `Related:` line names #1328, which closed 2026-08-07
+> (`status:shipped`, `canary:0.0.5-canary.15`). That dependency no longer blocks.
+>
+> **Boundary.** The mobile-action-loss observation from the Wave-6 review belongs to this issue's
+> acceptance surface unless it reproduces as a `@netscript/fresh-ui` defect on the current canary;
+> it should not be filed separately without that repro.
+
+**Rationale.** The pre-plan's explicit instruction is "expand #1333, do not file new issues" for the
+canonical scaffolded frontend. #1333 is p0 in the active milestone, so an additive comment is the
+only safe amendment shape — a body rewrite of a p0 mid-milestone risks losing agreed text.
+
+**What it prevents.** The T2 pack filing a "canonical vertical slice" issue that duplicates #1333;
+and #1333 shipping against ten boxes that a compliant implementation can satisfy while still
+producing an app with no `withResource`, no typed params, and no `(_shared)`/`(_lib)`.
+
+---
+
+
+
+## 7. #1335 — Epic: Scaffold conformance (Backlog / Triage, `type:umbrella`)
+
+**Amendment type:** comment (additive) + **BODY EDIT** limited to the `## Sub-issues` list. The
+`## Sub-issues` checklist is deliberately *not* close-gated (conventions §4.4), so editing it does
+not touch the merge gate.
+
+**Proposed comment text:**
+
+> **Sub-issue list refresh (2026-08-08).**
+>
+> The only listed sub-issue, **#1328**, closed on 2026-08-07 (`status:shipped`,
+> `canary:0.0.5-canary.15`), so this epic currently reads as 0% complete when its first child has
+> shipped. The `## Sub-issues` list is being refreshed to tick #1328 and to link the children that
+> exist in prose today.
+>
+> **#1333 is this epic's frontend row.** The body names "Frontend scaffold modernization and dynamic
+> app naming" as plain text; that is #1333 (0.0.5, p0). Linking it here prevents a dedup pass from
+> re-filing it. This epic does not own #1333's implementation and must not close it.
+>
+> **Proposed additional children** (seed-run drafts; the owner substitutes filed numbers):
+>
+> - Service-layout child — collapsible `domain` / `application` / `ports` / `adapters` / `routers` /
+> `auth` slice vocabulary, `service add-handler` placement, and a decision table for when a
+> generated service collapses to a single file (seed draft-ID **T3-02**).
+> - Generated-surface conformance rows this epic's inventory must produce a child for, each already
+> evidenced at source: `/design/components` lists 50 of the registry's 66 items with no sync gate;
+> `ui:add page --island` emits a `useSignal(0)` counter and an empty `queryLoaders = {}` instead
+> of the advertised data-screen triad; `resolveProjectRoot` returns the workspace root while the
+> app lives at `apps//`; `appRoutes.crudExample` aliases `serviceExample` so
+> `/examples/crud` is unreachable and a test asserts the alias; the canonical island never passes
+> `initialDataUpdatedAt: props.cachedAt` although the loader computes it; the generated
+> quality-runner's source list is a literal allow-list decoupled from the workspace generator.
+>
+> This is an umbrella. No implementation PR should close it directly.
+
+**Proposed `## Sub-issues` replacement (BODY EDIT):**
+
+> ## Sub-issues
+>
+> - [x] #1328 — generated check misses TSX/plugin runtimes; scaffold-owned quality findings
+> - [ ] #1333 — frontend scaffold modernization and dynamic app naming
+> - [ ] Service-layout child — slice vocabulary + `service add-handler` placement + decision table
+> - [ ] `/design/components` registry sync gate (50 of 66 items listed)
+> - [ ] `ui:add page --island` emits the advertised data-screen triad
+> - [ ] `resolveProjectRoot` writes into the app tree, not the workspace root
+> - [ ] `appRoutes.crudExample` points at `/examples/crud`
+> - [ ] Generated quality-runner source selection derives from the workspace generator
+
+**Rationale.** `github-board-open.md` §4.1 flags #1335's stale checkbox explicitly, and §4.2 warns
+that #1335 names #1333's work as plain text rather than a link — the exact condition that produces a
+duplicate filing.
+
+**What it prevents.** Re-filing #1333 during a dedup pass; an epic that reads as untouched while its
+first child shipped; and the T2/T3 packs filing conformance children with no umbrella linkage.
+
+---
+
+
+
+## 8. #1210 — docs(web-layer): differentiator deep-dives + competitive tutorial benchmark (0.0.6)
+
+**Amendment type:** comment (additive). No body edit — the per-API sub-page structure and the
+competitive benchmark stay as written.
+
+**Proposed comment text:**
+
+> **Cross-capability golden recipes — proposed addition to this issue's scope (2026-08-08).**
+>
+> The per-API deep-dives teach one API at a time. Every measured agent failure so far happened at a
+> *crossing* — the point where two capabilities meet and neither API page owns the seam. Proposal:
+> add a "golden recipes" set to this issue, each recipe end-to-end, type-checked against published
+> entrypoints, and each naming the seams it crosses.
+>
+> - **Contract-first resource screen.** route contract → typed SDK client → cache-first query
+> factory → layered `definePage` → `QueryIsland` hydration → optimistic mutation with rollback.
+> - **DB-model-first product.** generated `@database/zod` model → narrowed/extended versioned API
+> contract → handler → OpenAPI → SDK → page. (Coordinate with #1332, which owns the docs
+> statement of the DB-first predecessor path.)
+> - **Live data screen.** durable stream producer → SSE consumer → island live query, including
+> the event envelope, replay offsets and `traceparent` propagation. (Coordinate with #1329, which
+> owns the envelope definition; this recipe consumes it and must not redefine it.)
+> - **Background work with a receipt.** worker job → saga publish → compensation → one correlated
+> trace, showing that a discarded publish result is a failure and not a success.
+> - **Trigger to service.** webhook or scheduled trigger → worker → service call, including the KV
+> adapter registration and the service-reference injection a generated background runtime needs.
+> - **Protected screen.** auth boundary → protected route → typed principal available in the page
+> loader, with the unauthenticated path shown.
+> - **Second service.** `service add` → generated client/query module → a page that composes two
+> services without hand-written fetch.
+> - **Validated form.** Zod contract → `withForm` → server-side validation → partial navigation
+> → error state, with the native-constraint attributes the browser receives.
+> - **Add a capability the framework does not ship.** third-party plugin → discovery → generated
+> registry → `plugin doctor` → healthy Aspire resource.
+> - **Webhook delivery.** an outbound-delivery recipe over the worker template (retries, backoff,
+> dead-letter, signature) — recorded as a recipe, not a promised primitive.
+>
+> Each recipe should end with the falsifiable check the deep-dives already use: remove the seam and
+> the example must fail, so the recipe proves a mechanism rather than illustrating one.
+>
+> **Provenance of this list.** The pre-plan directs "expand #1210 with cross-capability golden
+> recipes" without enumerating them. The ten above were derived by the 2026-08-08 seed run from the
+> capability crossings that measured Wave-6 runs actually failed at; treat the list as a proposal
+> to be trimmed, not a specification.
+
+**Rationale.** The pre-plan's dedup matrix says "expand instead of new" for #1210. Enumerating the
+recipes inside #1210 is what makes that instruction actionable; a bare "add golden recipes" line
+would be re-interpreted by every implementer.
+
+**What it prevents.** A separate "cookbook" or "recipes" issue duplicating #1210; and deep-dive
+pages that each document their own API correctly while no page documents any crossing.
+
+---
+
+
+
+## 9. #1208 — docs(tutorials): no tutorial demonstrates the page builder (0.0.5, p0, `status:plan`)
+
+**Amendment type:** comment (additive). Records the phase-2 obligation so it cannot evaporate.
+
+**Proposed comment text:**
+
+> **Phase-2 filing obligation — recorded so it cannot be lost (2026-08-08).**
+>
+> This issue's body defines a phase 2 (the full inconsistency-and-underleverage sweep across all
+> tutorials) and says it will be "tracked as a checklist comment on #1208 when phase 1 lands".
+> As of 2026-08-08 **no phase-2 issue exists on the board**, and a checklist comment on a p0 issue
+> that is itself about to close is not a durable owner.
+>
+> Obligation, stated here so the next reader inherits it: **when the phase-1 PR merges, phase 2 is
+> filed as its own issue** in the same milestone family, titled in house shape, carrying
+> `Refs #1208` (not a closing keyword), with the tutorial inventory as its acceptance checklist.
+> If phase 2 is instead decided to be unnecessary, that decision is recorded as a comment here and
+> the row is struck — but it is not left implicit.
+>
+> Note for roadmap authors: because phase 2 has no issue number, it is one of the two highest
+> duplicate-filing risks on this board. Do not file it before phase 1 merges, and do not file it
+> twice after.
+
+**Rationale.** `github-board-open.md` §6.3 and §7 both flag the promised-but-nonexistent phase-2
+issue as a dedup trap. SYNTHESIS §7 ranks it alongside the prose-only umbrellas as a top risk.
+
+**What it prevents.** Both failure modes at once: phase 2 vanishing when #1208 closes, and phase 2
+being pre-emptively filed by a roadmap pass as a new issue that then collides with the real filing.
+
+---
+
+
+
+## 10. #922 — Epic: Frontend contribution layer (0.0.7, `type:umbrella`, 24 open children)
+
+**Amendment type:** comment (additive clarification). **No body rewrite** — the wave narrative and
+the RFC #890 design record stay verbatim.
+
+**Proposed comment text:**
+
+> **Wave-label vs milestone clarification (2026-08-08) — additive, no scope change.**
+>
+> This epic's body sequences its waves as `beta.13` / `beta.15` / `beta.17`. Those milestone titles
+> no longer exist: the `0.0.1-beta.N` line was renamed to `0.0.N` around 2026-08-01, and milestones
+> have since been renamed in place several times. The body text is a historical record and is being
+> left as written; this comment supplies the current mapping so no one reads a dead title as a
+> schedule.
+>
+> Measured milestone placement of this epic's children as of 2026-08-08:
+>
+> | Body wave | Children | Actual milestone |
+> | --- | --- | --- |
+> | Wave 0 proofs (beta.13) | #923–#927 | 0.0.7 |
+> | Wave 1 contracts + spine (beta.13) | #928–#933 | 0.0.7 |
+> | Wave 1b gateway | #934 | 0.0.7 |
+> | Wave 2 DX/lifecycle (beta.13) | #935–#938, #940 | 0.0.7 |
+> | Wave 2 DX/lifecycle | #944 | 0.0.9 |
+> | Wave 3 consumers (beta.15) | #939, #941 | 0.0.7 |
+> | Wave 3 consumers (beta.15) | #942, #943 | 0.0.11 |
+> | Completion (beta.17) | #945, #946 | 0.0.13 |
+>
+> The epic itself is 0.0.7. Note **#944 is a Wave-2 item milestoned later (0.0.9) than Wave-3
+> siblings still in 0.0.7** — that inversion is a real ordering question for whoever schedules this
+> epic, not a labelling artifact.
+>
+> **Re-baseline reminder.** This epic's body says "Refs #427, #432 — both KEEP-and-re-baseline per
+> the RFC's supersession map; no issues closed by this epic's filing." As of 2026-08-08 **the
+> re-baseline has not happened**: #427 and #432 are still open verbatim under `epic:dev-dashboard`
+> (#400), and #400's 29 open children overlap this epic's consumer wave. The re-baseline is
+> outstanding work owned by whoever schedules #922's Wave 3, and it should be done before #400's
+> dashboard panels are planned against the old text.
+>
+> **If milestones shift again**, note the house pattern: milestones are renamed in place
+> highest-to-lowest and the freed title is created afterwards, so children do not move and this
+> table stays valid under the old numbers — only the titles change.
+
+**Rationale.** `github-board-open.md` §6.5 measured the drift; `github-board-history.md` §5 documents
+the rename mechanism that caused it. The re-baseline omission is measured in §4.2.
+
+**What it prevents.** Someone "fixing" #922 by moving children to match dead beta titles; and #400's
+dashboard work being planned against superseded #427/#432 text. Also prevents an epic body rewrite
+that would destroy the RFC #890 review trail.
+
+---
+
+
+
+## 11. #301 — epic: Road to 0.0.1-stable (Backlog / Triage, `type:umbrella`)
+
+**Amendment type:** comment (additive) + optional **BODY EDIT** limited to ticking the five closed
+rows. The child checklist is not close-gated, so ticking it is safe.
+
+**Proposed comment text:**
+
+> **Stale checkbox audit (2026-08-08).**
+>
+> Five children listed here as unchecked are already CLOSED: **#305, #306, #391, #399, #401**.
+> Remaining genuinely open: #302, #303, #307, #309, #313, #327, #400. This epic therefore reads far
+> less complete than it is, which distorts any milestone reforecast that reads it.
+>
+> No checked box on this epic points at a still-open issue, so there are no false-complete rows —
+> the error is one-directional and safe to correct by ticking.
+>
+> Note for planners: three of the remaining rows (#313, #327, #400) are themselves umbrellas with
+> their own child sets, so this epic's true remaining scope is much larger than its row count
+> suggests.
+
+**Rationale.** `github-board-open.md` §4.1 verified each of the five against the closed set.
+
+**What it prevents.** A roadmap reading #301 as ~8% complete and re-planning finished work; and a
+"road to stable" status report built on a checklist nobody has reconciled.
+
+---
+
+
+
+## 12. #1126 — Epic: OpenAPI→MCP service introspection (0.0.5, `type:umbrella`)
+
+**Amendment type:** comment (additive) + optional **BODY EDIT** ticking the nine closed rows.
+
+**Proposed comment text:**
+
+> **Stale checkbox audit (2026-08-08).**
+>
+> Nine children listed here as unchecked are already CLOSED: **#1128, #1129, #1130, #1131, #1132,
+> #1133, #1134, #1135, #1136**. Only **#1137, #1138, #1139, #1140** remain open (all four carry
+> `epic:openapi-mcp`).
+>
+> This epic is materially near completion and currently reads as barely started. Correcting the
+> checklist matters for 0.0.5 scoping specifically: #1126 is one of the 0.0.5 umbrellas, and an
+> uncorrected checklist inflates the apparent remaining 0.0.5 surface.
+>
+> This is an umbrella. No implementation PR should close it directly.
+
+**Rationale + prevention.** Same as #301. Additional stake: #1126 sits in the *active* milestone, so
+its stale checklist directly distorts the "can 0.0.5 close" decision.
+
+---
+
+
+
+## 13. #1325, #1326, #1329 — evidence attachment (no scope change)
+
+**Amendment type:** comment on each. Evidence pointers only — no acceptance box is added, changed or
+ticked, and no scope is touched. These three are already well-specified; the amendment exists so the
+Wave-6 measurements and the 2026-08-08 source audit are discoverable from the issue.
+
+**Proposed comment on #1325** (`fix(triggers): generated background runtime omits the Redis adapter`):
+
+> **Additional evidence (2026-08-08) — no scope change.**
+>
+> Independently reproduced at source: `plugins/triggers/src/adapter/resources/glue/runtime.stub.ts`
+> emits the generated `triggers/runtime.ts` with no `@netscript/kv/redis` import, so the generated
+> runtime carries no adapter for the default Aspire Redis/Garnet cache.
+>
+> Two measured agent runs hit this independently, which is why the generalisation requirement in
+> this issue's acceptance is the real deliverable rather than the one-line import:
+>
+> - **Wave-6 R2** (`workflow-builder-kimi-k3-max/`, Kimi K3 Max, `0.0.5-canary.13`) — crash-loop
+> `KvConnectionError`; the builder added a one-line side-effect import to a scaffold-owned trigger
+> entrypoint, i.e. hand-edited a generated file.
+> - **Wave-6 R3** (`billing-run-grok-4.5-high-canary.16/`, Grok 4.5 high, `0.0.5-canary.16`) —
+> repair commit `8b86649` adds the `@netscript/kv/redis` triggers import; recorded as "hard to
+> diagnose".
+>
+> **Classification conflict worth recording:** R2 classifies this as a framework defect (D-class);
+> R3 classifies the same surface as a docs/MCP discoverability gap with "no framework defect
+> established". Both runs had to add the same glue by hand on two different canaries. The
+> source-level evidence above resolves the conflict in favour of the defect classification.
+
+**Proposed comment on #1326** (`fix(streams): DurableStreamProducer permanently drops writes`):
+
+> **Additional evidence (2026-08-08) — no scope change.**
+>
+> Mechanism re-confirmed at source in
+> `packages/plugin-streams-core/src/application/create-durable-stream.ts`: `#connect` runs once, on
+> failure sets `#connectError` and returns, and `#appendEvent` drops every event while that field is
+> set; no timer, retry policy or state transition ever clears it — so the operator log line
+> promising "until reconnect" names a transition the implementation cannot perform.
+>
+> **Wave-6 R2** recorded the consequence class independently: "durable-stream producer silently
+> drops writes forever after a startup-order race". R3 ran a single clean graph and plausibly
+> avoided the race, so R3's silence is not evidence against this — it is a coverage gap.
+>
+> **Pairing reminder:** this issue's acceptance depends on the standardized stream event envelope
+> defined by **#1329**. The two must be scheduled as a pair; landing reconnect telemetry against an
+> undefined envelope produces spans that #1329 will then redefine.
+
+**Proposed comment on #1329** (`fix(streams): documented SSE consumer shape differs from the wire`):
+
+> **Additional evidence (2026-08-08) — no scope change.**
+>
+> Consumer-side confirmation from measured runs, both of which reverse-engineered the wire because
+> the documented shape does not receive anything:
+>
+> - **Wave-6 R2** (`workflow-builder-kimi-k3-max/`) — `apps/dashboard/islands/LoomCanvas.tsx` uses
+> named `data` events with array payloads, arrived at by runtime inspection.
+> - **Wave-6 R3** (`billing-run-grok-4.5-high-canary.16/`) — used the wrong durable-stream path
+> (`/v1/streams/billing/run-events` → 404) before finding the correct
+> `/v1/stream/netscript/billing/run-events` → 200. R3's own audit classifies this as a docs/MCP
+> discoverability gap; it maps onto this issue's documented-shape defect.
+>
+> Two independent consumers reconstructing the same undocumented envelope is the strongest available
+> argument for this issue's first acceptance box (one exported versioned schema defining every SSE
+> event name and payload) over a documentation-only fix.
+
+**Rationale.** These three are the 0.0.5 streams/triggers core and are already correctly scoped;
+what they lack is the cross-run evidence that makes their *generalisation* requirements defensible
+during implementation review.
+
+**What it prevents.** An implementer satisfying #1325 with a one-line import (R2's own workaround)
+rather than the enumerated invariant; #1326 and #1329 being scheduled apart; and #1329 being closed
+with a docs edit because R3's classification was read without R2's.
+
+---
+
+
+
+## 14. #979 — fix(aspire): plugin API resources still pin host ports 8091–8094 (no milestone)
+
+**Amendment type:** comment (additive evidence). Also appears in the board-hygiene batch below for
+its missing milestone.
+
+**Proposed comment text:**
+
+> **Additional evidence — the stub-port surface is wider than the Aspire entries (2026-08-08).**
+>
+> This issue owns the plugin API resources' pinned host ports. A 2026-08-08 source audit found the
+> same hardcoded ports in three further layers, which matters because dropping `Port` from the
+> scaffolder's plugin entries will not by itself remove the pinning — these paths bypass the
+> allocator entirely:
+>
+> - **Contribution env/health literals** — each contribution allocates through
+> `ctx.port(name, DEFAULT)` and then publishes a literal:
+> `plugins/sagas/src/aspire/sagas-contribution.ts:135` (`SAGAS_API_URL:
+> http://localhost:8092`) and `:146` (health URL, `_ctx` unused);
+> `plugins/triggers/src/aspire/triggers-contribution.ts:139` and `:149` (same pattern).
+> `plugins/workers/src/aspire/workers-contribution.ts:71,81` is the correct counter-example — it
+> uses the allocated port.
+> - **Fixed-port fallbacks in runtime clients** — `SAGAS_API_DEFAULT_PORT = 8092`
+> (`plugins/sagas/src/constants.ts:11`), `plugins/sagas/src/cli/adapters/runtime-api-client.ts:27`
+> (`http://127.0.0.1:8092/api/v1/sagas`),
+> `plugins/workers/src/cli/adapters/runtime-api-client.ts:27` (`…:8091/api/v1/workers`).
+> - **E2E probe contexts** — `plugins/workers/src/e2e/probes/probe-context.ts:5`
+> (`http://localhost:8091`), `plugins/sagas/src/e2e/probes/probe-context.ts:3`
+> (`http://127.0.0.1:8092`).
+>
+> Commit `0b11ca47a` (#1211, randomize default listener ports) is what turned each of these from a
+> working default into latent breakage: once ports are randomized, a literal fallback silently
+> targets a port nothing is listening on.
+>
+> **Why this belongs here and not in a new issue:** this issue already owns the prerequisite chain
+> (resolve endpoints from the Aspire resource service instead of hardcoding, including the
+> `--allow-net` grant handed to the generated project, plus the ~20 `docs/site/**` passages that
+> `curl` those ports). The literals above are the same defect at a different layer and should be
+> enumerated in the same sweep. A separate seed-run draft (**T4-06**) tracks the stub-port
+> hardcodes if the owner prefers them split; if so, it carries `Refs #979` and this comment is its
+> evidence base.
+>
+> **Sibling:** #980 (`netscript service add` pins an Aspire host port) is the same defect on a
+> different command and is likewise unmilestoned.
+
+**Rationale.** `research/repo-audit/runtime-plugins.md` §1.3-1.4 enumerated these at source; the
+`github-board-open.md` §7 dedup checklist names #979 as the owner for Aspire plugin port pinning, so
+this is evidence attachment, not a new filing.
+
+**What it prevents.** A fix that removes `Port` from the scaffolder's entries, passes the E2E suite
+(which probes the literals), and leaves two workspaces still colliding — plus the silent
+`127.0.0.1:8092` fallback that the saga publish-receipt defect rides on.
+
+---
+
+
+
+## 15. #1090 and #1197 — measured adoption evidence (Wave 6)
+
+**Amendment type:** comment on each. Evidence only; neither comment ticks or proposes an acceptance
+box, because #1090's boxes are explicitly observational and #1197's demand a *future* re-measurement.
+
+**Proposed comment on #1197** (`agentic: the agent-init harness had zero adoption on 0.0.4`):
+
+> **Wave-6 measurements — the seventh and eighth data points (2026-08-08).**
+>
+> This issue records six consecutive measured runs with zero docs-MCP calls. Wave 6 adds two more,
+> and the second one breaks the streak in a way that is directly relevant to this issue's
+> acceptance:
+>
+> - **R2** (`workflow-builder-kimi-k3-max/`, Kimi K3 Max, `0.0.5-canary.13`): **live NetScript MCP
+> calls 0** for the whole run. `netscript agent init` was skipped naturally until a 07:53:21Z
+> supervisor correction; the builder's own explanation was that it "misread 'agent tooling' as
+> optional editor garnish instead of the framework discovery path". `plugin doctor` and Aspire
+> OTEL unused. **Even after `agent init` ran, MCP calls stayed 0** — `agent init` could not attach
+> the generated `.mcp.json` to an already-running OpenCode host. Tool mix: 378 tool parts
+> (bash 263, edit 48, write 44, read 14, webfetch 7, todowrite 1, skill 1).
+> - **R3** (`billing-run-grok-4.5-high-canary.16/`, Grok 4.5 high, `0.0.5-canary.16`): **MCP calls
+> non-zero** — export 6, docs search 3, doctor 2, doc 2, operation schema 2, API-service listing
+> 2, plus package/service discovery; Aspire MCP list-apphosts 1, list-resources 1. 514 tool calls
+> total. This followed a **supervisor-enforced preflight**: `agent init`, local docs, reload, MCP
+> attachment and a harmless docs-lookup proof, all in the same session **before implementation**.
+>
+> **What this adds to this issue.** The zero is not purely a discovery-motivation problem. R2 gives
+> a concrete mechanical cause — a running host cannot pick up a newly written `.mcp.json` — which is
+> a fixable defect distinct from "agents do not know to look". And R3 shows the number moves when
+> attachment is enforced at session start, which is evidence for this issue's "routing at the moment
+> of failure" requirement being necessary but not sufficient: attachment has to work first.
+>
+> No acceptance box is proposed here; this issue's acceptance correctly demands a *re-measured*
+> future run, and R2/R3 are prior runs, not that measurement.
+
+**Proposed comment on #1090** (`verify(wave-five): does the shipped agent surface change behaviour?`):
+
+> **Wave-6 observations relevant to this issue's four criteria (2026-08-08) — evidence, not
+> discharge.**
+>
+> None of the four criteria here is satisfied by Wave 6, and this comment does not tick anything.
+> Recording what Wave 6 observed, so the eventual verification run has a baseline:
+>
+> - *Non-zero MCP diagnostic usage:* R2 = 0 across the run; R3 = non-zero (export 6, docs 3,
+> doctor 2) but only after a supervisor-enforced preflight, so it is not an unassisted
+> observation.
+> - *An agent building a data screen runs `ui:add` or records why not:* R2 did neither —
+> product `QueryIsland` / `withForm` / Fresh-UI feature-loop adoption **0 / 0 / 0**, and a
+> **676-line `LoomCanvas.tsx`** shipped instead. `definePage` and fresh-ui appear only in the
+> scaffold examples, never in the product routes.
+> - *The #1071 falsifiable check (blind, six agents per arm, varying only the app-scoped conventions
+> file):* not run in Wave 6. Wave 6 was a natural experiment with a deliberate
+> no-contamination rule — suggestions such as naming the plugins or banning hand-rolled SSE were
+> explicitly **rejected as builder prompt contamination**. That discipline is worth preserving in
+> the eventual arm design, but it means Wave 6 cannot substitute for the controlled check.
+> - *An agent asked to build a service-backed UI reaches a Web Layer page before writing a route:*
+> not observed in either run.
+>
+> **Thesis reinforcement.** Wave 6 restates this issue's own lesson with a second mechanism: R2
+> adopted the framework and immediately surfaced five-plus D-class seams, so non-adoption is not
+> inevitable — the shipped surface has to be *reachable at the moment of need*, and R3 shows the
+> number moves when it is enforced.
+>
+> Reminder for planners: #1090, #1102, #1197 and #1201 are one measurement chain. Planning them
+> separately builds the extraction harness three times.
+
+**Rationale.** SYNTHESIS §1 and §6 treat the measurement chain as owned and untouchable; the value
+this run can add is evidence, not scope. R2/R3 are the first measurements taken *after* the runs
+#1197 describes.
+
+**What it prevents.** A remediation slice claiming #1197's acceptance on the strength of R3's
+non-zero MCP count — which was supervisor-enforced, not spontaneous; and the T7 pack re-deriving the
+Wave-6 numbers instead of citing them.
+
+---
+
+
+
+## 16. Board-hygiene batch
+
+One batch, executed by the owner in a single pass. Every item is label/milestone metadata only — no
+issue body changes, no closures, no scope decisions. All facts from `github-board-open.md` §5.1,
+§4.1 and `github-conventions.md` §2.2, measured 2026-08-08.
+
+### 16.1 Issues missing required metadata
+
+The minimum contract is: ≥1 `type:`, ≥1 `area:`, exactly one `status:`, a `priority:`, and a
+milestone (`github-conventions.md` §2.3).
+
+| Issue | Missing | Proposed action |
+| --- | --- | --- |
+| **#175** | **all labels** (zero labels), no `priority:`, no `status:` | Triage: add `type:`, `area:`, `priority:`, `status:triage`. It sits in milestone `0.0.2`, which is stable-released — so also decide: still wanted, or close as not planned. |
+| **#950** | no `priority:`, no `status:` | Add `priority:` + `status:triage`. It is an `epic:ai-stack` member in milestone `0.0.8`. |
+| **#1000** | no `priority:`, no milestone | Add `priority:` + a milestone. Also carries the legacy `documentation` label (see 16.3). |
+| **#979** | no milestone | Assign a milestone. It has a real dependency chain (E2E port probes + ~20 docs passages) so it should not sit unmilestoned; see §14 above. |
+| **#980** | no milestone | Assign the same milestone as #979 — same defect, different command. |
+
+**Proposed comment for #175** (it is the only zero-label issue and needs a human decision):
+
+> **Board hygiene (2026-08-08).** This issue carries **no labels at all** — the only such issue on
+> the open board — and no `priority:`. It is assigned to milestone `0.0.2`, which was released as
+> `v0.0.2` on 2026-08-01 and still holds 5 open issues (#175, #767, #768, #863, #864). Requesting
+> triage: apply the minimum taxonomy (`type:` + `area:` + `priority:` + `status:triage`), and either
+> re-milestone it to a live cut or close it as not planned. No scope judgement is being made here.
+
+### 16.2 `.github/labels.yml` parity
+
+The file is materially out of date in both directions. Per its own header rule — *"Add new labels
+here first, then create them; do NOT delete existing labels (that strips them off live issues) —
+deprecate in this file and propose removal to the maintainer"* — the fix is **declare and create,
+never delete**.
+
+**(a) Declared in `labels.yml` but NOT live (2) — create them:**
+
+- `status:close-gate-override` (`b60205`, "Audited exception to the closing-keyword acceptance
+ gate")
+- `docs-eval:skip`
+
+**Consequence if not fixed:** the audited close-gate escape hatch documented in
+`netscript-pr/SKILL.md` **cannot be applied today** — the label does not exist, so an audited
+exception has no way to be recorded. This is the highest-value item in the whole hygiene batch
+because it silently removes a documented process option.
+
+**(b) Live but NOT declared in `labels.yml` (33, excluding the machine-generated `canary:*`) — add
+them to the file so the declared taxonomy matches reality:**
+
+- `area:` — `agentic`, `ai`, `contracts`, `db`, `packages`, `queue`, `release`, `runtime-config`,
+ `sagas`, `services`, `streams`, `triggers`, `workers` (several in active use: `area:agentic` on
+ #1330/#1331/#1343, `area:contracts` on #1332/#1263, `area:release`).
+- `epic:` — `deploy-plugin`, `desktop-frontend`, `enterprise-auth`, `road-to-stable`,
+ `unified-runtime`.
+- `status:` — `blocked` (in live use on #1320 and #1280), `in-progress`, `in-review`, `review`.
+- `type:` — `feature`, `release`.
+- gates/flags — `gate:ci`, `e2e-cli-gate`, `priority:high`, `codex`, `dx`, `prime-time`, `sagas`,
+ `service`, `question`, `invalid`.
+
+**(c) Duplicate pairs — deprecate in the file with a note, do not delete.** Record the preferred
+member so new issues stop splitting: prefer `type:feat` over `type:feature`; `priority:p1` over
+`priority:high`; `area:database` over `area:db`; `area:plugins` over the per-plugin
+`area:sagas`/`area:streams`/`area:triggers`/`area:workers` (the per-plugin ones are what #1325,
+#1326 and #1329 actually carry, so if the split is intentional, say so in the file rather than
+leaving it ambiguous).
+
+**(d) The `status:` single-label hazard.** `labels.yml` says exactly one `status:` per open issue,
+but four undeclared `status:` values are live (`blocked`, `in-progress`, `in-review`, `review`),
+two of which duplicate declared columns. Either declare `status:blocked` (it is genuinely in use and
+has no declared equivalent) and deprecate the other three, or map them onto declared columns.
+Whichever, record it in the file.
+
+**(e) `wave:*` labels.** `wave:v1`, `wave:v1-min` and `wave:defer` are declared and live but appear
+on none of the recently filed issues. Mark them deprecated in `labels.yml` unless the plan revives
+the band, so new filings stop being asked to consider them.
+
+### 16.3 Legacy non-namespaced labels still in use
+
+- `rfc` on **#234, #313, #510, #820** — all open, all `Backlog / Triage`. Decision needed: keep
+ `rfc` as the flag it is declared to be, or introduce a namespaced equivalent. Do not strip it from
+ live issues without a replacement.
+- `documentation` on **#1000** only — the last user of the GitHub-standard label where
+ `type:docs` + `area:docs` is the house taxonomy. Proposed: add `type:docs` + `area:docs` to #1000
+ and leave `documentation` in place (deletion strips it from the issue).
+
+### 16.4 Orphaned `epic:` labels (label group with no umbrella issue)
+
+- `epic:desktop-frontend` — sole member #859.
+- `epic:docs-cut` — sole member #695.
+- `epic:telemetry-revamp` — sole member #248 (which is also `epic:ai-stack`).
+
+Proposed: for each, either file/point to an umbrella, fold the member into an existing epic, or
+deprecate the label in `labels.yml`. A single-member epic label is indistinguishable from a typo at
+read time.
+
+### 16.5 Cross-epic double membership
+
+- **#451, #453, #454, #455** carry both `epic:deployment` and `epic:unified-runtime`. This is not
+ itself an error, but it means **#823**'s entire open membership sits inside **#327**'s unchecked
+ child list — three umbrellas (#327, #823, #830) over one child set.
+- **#830** is an umbrella *and* a member of `epic:deployment` (i.e. a child of #327).
+- **#248** sits in both `epic:ai-stack` and `epic:telemetry-revamp`.
+
+Proposed: record the intended containment in each umbrella body (a single line naming the parent) so
+the double membership reads as deliberate. Do not remove labels — the label group is the machine
+truth for membership and stripping it loses the relationship.
+
+**Proposed comment for #823** (the clearest case):
+
+> **Board hygiene (2026-08-08).** This epic's entire open membership (#451, #453, #454, #455) also
+> carries `epic:deployment` and appears in #327's unchecked child list; #830 is a third umbrella
+> over the same deployment surface. No labels are being changed. Requesting one line in each of
+> #327 / #823 / #830 stating the intended containment, so a planner reading any one of them knows
+> whether the four shared children are owned here, there, or jointly. Planning these three
+> independently will produce duplicate slices over one child set.
+
+**Rationale for the whole batch.** `github-board-history.md` §6.7: label hygiene in this repo is
+real and enforced — every closed 0.0.5 row carries exactly one `status:`, a `canary:` label and its
+milestone, and the close-gate verifies GraphQL `closingIssuesReferences` against body keywords.
+Drafts produced by this run will be rejected by those same gates if the taxonomy they are filed
+against is itself inconsistent.
+
+**What it prevents.** (a) An audited close-gate exception being impossible to record because
+`status:close-gate-override` does not exist; (b) new issues splitting across `type:feat`/
+`type:feature` and `area:db`/`area:database`, which makes every label-based query wrong;
+(c) #979/#980/#1000 remaining invisible to every milestone view; (d) three deployment umbrellas
+each planning the same four children.
+
+---
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Baseline
+`fac9e339042c` (== `origin/main`). Sources: `SYNTHESIS.md` §3 (adjudications 2, 3, 4),
+`research/github-board-open.md` §4–§7, `research/github-board-history.md` §5–§6,
+`research/github-conventions.md` §2–§4, `research/preplan-package.md`,
+`research/repo-audit/{web-layer,scaffold-doctrine,runtime-plugins}.md`, `research/wave-6-runs.md`.
+Every re-measured figure was executed in this worktree on 2026-08-08; the working tree was left
+clean. **No GitHub state was read-modified or written by this run.**
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/FILING-LOG.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/FILING-LOG.md
new file mode 100644
index 0000000000..e4c574fadf
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/FILING-LOG.md
@@ -0,0 +1,85 @@
+# Stage-H filing log
+
+Filed on 2026-08-08 after explicit owner ratification. GitHub is now authoritative; the issue
+drafts in this planning PR are provenance and implementation context only.
+
+## Milestone train
+
+Existing milestone objects were renamed from highest to lowest so their issue membership stayed
+attached while two releases were inserted:
+
+| Milestone object | Previous title | Live title |
+| --- | --- | --- |
+| #21 | 0.0.13 | 0.0.15 |
+| #20 | 0.0.12 | 0.0.14 |
+| #19 | 0.0.11 | 0.0.13 |
+| #18 | 0.0.10 | 0.0.12 |
+| #17 | 0.0.9 | 0.0.11 |
+| #16 | 0.0.8 | 0.0.10 |
+| #24 | 0.0.7 | 0.0.9 |
+| #25 | 0.0.6 | 0.0.8 |
+| #26 | — | 0.0.6 (new) |
+| #27 | — | 0.0.7 (new) |
+
+The complete former-0.0.6 membership was first restored to new milestone #26. The planned
+exceptions were then reconciled: #1279 → 0.0.15; #979 and #980 → 0.0.8; #1000, #175, #767,
+#768, #863, and #864 → Backlog / Triage. No existing issue was closed.
+
+## Draft-ID to live-issue map
+
+| Draft | Live issue | Milestone |
+| --- | --- | --- |
+| T1-01 | #1348 | 0.0.6 |
+| T1-02 | #1349 | 0.0.6 |
+| T1-03 | #1350 | 0.0.6 |
+| T1-04 | #1351 | 0.0.6 |
+| T1-05 | #1352 | 0.0.6 |
+| T1-06 | #1353 | 0.0.6 |
+| T2-01 | #1354 | 0.0.7 |
+| T2-02 | #1355 | 0.0.7 |
+| T2-03 | #1356 | 0.0.7 |
+| T2-04 | #1357 | 0.0.7 |
+| T2-05 | #1358 | 0.0.7 |
+| T2-06 | #1359 | 0.0.7 |
+| T2-07 | #1360 | 0.0.7 |
+| T3-01 | #1361 | 0.0.6 |
+| T3-02 | #1362 | 0.0.6 |
+| T3-03 | #1363 | 0.0.6 |
+| T3-04 | #1364 | 0.0.6 |
+| T4-01 | #1365 | 0.0.7 |
+| T4-02 | #1366 | 0.0.7 |
+| T4-03 | #1367 | 0.0.7 |
+| T4-04 | #1368 | 0.0.7 |
+| T4-05 | #1369 | 0.0.7 |
+| T4-06 | #1370 | 0.0.8 |
+| T4-07 | #1371 | 0.0.8 |
+| T4-08 | #1372 | 0.0.8 |
+| T5-01 | #1373 | 0.0.8 |
+| T5-02 | #1374 | 0.0.8 |
+| T5-03 | #1375 | 0.0.8 |
+| T5-04 | #1376 | 0.0.8 |
+| T5-05 | #1377 | 0.0.8 |
+| T6-01 | #1378 | 0.0.8 |
+| T6-02 | #1379 | 0.0.8 |
+| T6-03 | #1380 | 0.0.8 |
+| T7-01 | #1381 | 0.0.8 |
+| TA-01 | #1382 | 0.0.8 |
+| TA-02 | #1383 | 0.0.8 |
+| TA-03a | #1384 | 0.0.8 |
+| TA-03b | #1385 | 0.0.8 |
+| TA-03c | #1386 | 0.0.8 |
+| TA-04 | #1387 | 0.0.8 |
+| TA-05 | #1388 | 0.0.8 |
+
+## Reconciliation receipts
+
+- Created 41 issues (#1348–#1388): 10 in 0.0.6, 12 in 0.0.7, and 19 in 0.0.8.
+- Replaced internal Draft-ID dependency references in every live issue with live issue numbers.
+- Verified every new issue is open, has exactly one milestone, `status:triage`, and at least one
+ `type:`, `area:`, and `priority:` label.
+- Posted the ratified additive amendment blocks to #1278, #1276, #1279, #1275, #1245, #1333,
+ #1335, #1210, #1208, #922, #301, #1126, #1325, #1326, #1329, #979, #1197, #1090, #175,
+ and #823. Marker: ``.
+- Preserved open PR #1215 and every other former-0.0.6 item during the milestone insertion.
+- No issue was closed and no implementation work was started by this filing pass.
+
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/FILING-MANIFEST.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/FILING-MANIFEST.md
new file mode 100644
index 0000000000..763af2f810
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/FILING-MANIFEST.md
@@ -0,0 +1,72 @@
+# Stage-H filing manifest
+
+Owner ratification: 2026-08-08, in-turn instruction to open all planned issues and execute the
+milestone train. PLAN-EVAL and IMPL-EVAL remain explicitly owner-waived for this planning-only run.
+GitHub is authoritative after this manifest executes.
+
+## Preconditions captured immediately before filing
+
+- Source PR: #1347 at `1d6c6a70fd6250fee07f8942a2f4873bf68c2b0f`.
+- Open issues audited: 259 (pull requests excluded).
+- Open milestones audited: 13.
+- Proposed issue drafts: 41; exact-title collisions with the live open board: 0.
+- Every requested label exists live.
+- Existing `0.0.6` milestone number: 25; open-issue snapshot count: 23.
+
+## Milestone rename operations — execute in this order
+
+Title-only collision-free slide, highest to lowest:
+
+1. milestone 21: `0.0.13` → `0.0.15`
+2. milestone 20: `0.0.12` → `0.0.14`
+3. milestone 19: `0.0.11` → `0.0.13`
+4. milestone 18: `0.0.10` → `0.0.12`
+5. milestone 17: `0.0.9` → `0.0.11`
+6. milestone 16: `0.0.8` → `0.0.10`
+7. milestone 24: `0.0.7` → `0.0.9`
+8. milestone 25: `0.0.6` → `0.0.8`
+
+Then create:
+
+- `0.0.6` — Verification, docs truth & RFC ratification.
+- `0.0.7` — Typed seams + generation.
+
+Milestone 25 becomes the new `0.0.8` — Runtime truth + service slice.
+
+## Existing 0.0.6 preservation snapshot
+
+Move all 23 issues from renamed milestone 25 back to the newly created `0.0.6` before filing new
+issues:
+
+`#1085`, `#1093`, `#1112`, `#1139`, `#1140`, `#1163`, `#1175`, `#1201`, `#1210`, `#1243`,
+`#1215` (open PR), `#1246`, `#1260`, `#1262`, `#1263`, `#1278`, `#1279`, `#1280`, `#1293`,
+`#1296`, `#1306`, `#1320`, `#1343`. The execution script must re-read milestone 25 and move its
+entire membership, not trust this prose list. After preservation, move `#1279` from the new `0.0.6`
+to `0.0.15` per the plan.
+
+## Other existing-issue moves
+
+- `#979`, `#980` → `0.0.8`.
+- `#1000` → `Backlog / Triage`.
+- `#175`, `#767`, `#768`, `#863`, `#864` → `Backlog / Triage` from shipped `0.0.2`.
+- No existing issue is closed during filing.
+
+## New issue source
+
+File every Markdown draft under `milestones/*/*.md`, using the directory milestone as the locked
+milestone decision. Strip the draft marker and metadata preamble from the live issue body. Apply
+the declared labels. Count contract:
+
+- `0.0.6`: 10 new issues.
+- `0.0.7`: 12 new issues.
+- `0.0.8`: 19 new issues.
+- Total: 41.
+
+After all issues exist, replace every internal Draft-ID dependency reference with its live issue
+number and record the complete mapping in `FILING-LOG.md`.
+
+## Reconciliation
+
+Apply the additive existing-issue amendment blocks from `EXISTING-ISSUE-AMENDMENTS.md` as comments
+where the target remains open. Do not close duplicate umbrellas during this filing; preserve the
+seed-run rule that supersession closes only through a later owner action or resolving PR.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/IMPLEMENTATION-HANDOFF.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/IMPLEMENTATION-HANDOFF.md
new file mode 100644
index 0000000000..f5c98d9b92
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/IMPLEMENTATION-HANDOFF.md
@@ -0,0 +1,98 @@
+# Implementation handoff — DRAFT (no GitHub mutation; owner ratification pending)
+
+What becomes executable, in what order, on which lanes, once the owner ratifies the fork sweep
+(`MASTER-PLAN.md` §7) and a later authorized run files the board from the manifest
+(`ISSUE-DEDUP-AND-SUPERSESSION.md`). Implementation lanes launch from **GitHub + these design
+packs**, never from this run's chat history. Routing is data: select every lane from
+`.llm/harness/workflow/lane-policy.md` at dispatch time; identities below are the *expected*
+canonical routes as of 2026-08-08.
+
+## 0. Pre-implementation gate (owner + one filing run)
+
+1. Owner ratifies forks F1–F12 (or amends; every default is reversible).
+2. A dedicated filing session executes, in order: label parity (`.github/labels.yml` — the file
+ is missing 33+ live labels and live is missing `status:close-gate-override`/`docs-eval:skip`),
+ milestone renames highest→lowest + create 0.0.7/0.0.8, amendments from
+ `EXISTING-ISSUE-AMENDMENTS.md`, then issue filing per milestone directory with `FILING-LOG.md`
+ mapping draft-ID → live number. Re-verify each touched issue's live state immediately before
+ mutation (GitHub wins over this plan on conflict).
+3. After filing, GitHub is the single source of truth; these run docs get authority banners.
+
+## 1. First executable groups (PR-sized, cluster rules per `agent-milestone-orchestrator`)
+
+**Group A — 0.0.5 close-out (already implementation-ready today, no ratification needed).**
+The undispatched W2–W5 remainder is fully specified on live issues: the streams pair
+(#1326+#1329, one supervisor — they share the envelope), #1333 (p0 scaffold frontend), #1208
+phase 1. Lane: `complex_implementation` (Codex Sol · high) for #1326/#1329/#1333;
+`documentation_authoring` for #1208. Ready because: complete contracts + acceptance on the live
+issues; the corpus adds only evidence pointers (amendments).
+
+**Group B — 0.0.6 wave 1 (ready at ratification; docs + gates, cheap lanes).**
+- B1: T5-01 dialect fix (**after F7 is decided**) + T5-02 compile-the-docs gate — one docs-lane
+ PR pair; T5-02's checker is repo tooling (`chore_code` lane).
+- B2: T5-03 + T5-04 MCP wiring — small scoped slices, `light_implementation`.
+- B3: T6-01 + T6-02 quality/CI gates — `normal_implementation`; T6-02's lock decision first.
+- B4: RFC-A + RFC-B tracking issues filed; RFC review is owner + `deep_analysis` (Fable) session
+ over the `rfcs/` drafts; ratification recorded on the tracking issues.
+- Gate set: scoped wrappers + `doc:lint` + the new gates' own RED-first fixtures. Not e2e-cli.
+
+**Group C — 0.0.7 wave 1 (entry: RFC-A accepted).**
+- C1: T1-04 transport consolidation (prereq of T1-02) — `normal_implementation`.
+- C2: T1-02 seam re-exposure + T1-03 typed errors — `complex_implementation`, jsr-audit gate
+ (public surface changes), review per effort-paired ladder.
+- C3: T2-03 root-targeting fix (hard prereq of T2-01/02/04) — `normal_implementation`.
+- Then C4: T2-02 generator → C5: T2-01 slice generator + T2-04 triad → C6: T1-05 auth dogfood →
+ C7: T1-06 trace contribution. Byte-identity and no-`any` consumer gates are the slice gates.
+
+**Group D — 0.0.8 wave 1 (entry: 0.0.7 generators in a canary).**
+- D1: T4-01 saga receipts (p0, **after F10**) + T4-08 E2E truth gates (its detector) — one
+ supervisor, `complex_implementation`.
+- D2: TA-02 → TA-01 → TA-03a/b/c auth cluster — TA-02 first (the seam), `complex_implementation`;
+ security-review skill on every TA PR.
+- D3: T3-02 service layout → T3-03 command kit (after RFC-B) — `complex_implementation`.
+- D4: T4-02/T4-03/T4-04 runtime truth set — parallelizable across supervisors.
+- Exit: T7-01 Wave-7 smoke (arms per `WAVE7-AND-AGENT-ADOPTION.md`, **after F11**).
+
+## 2. Harness profile per group
+
+Every brief starts with `use harness` and carries a `## SKILL` chapter. Group A/C/D framework
+slices: archetype per touched package (`netscript-doctrine`; SDK work = Archetype 2/4, CLI =
+Archetype 6, plugins = Archetype 5) + `SCOPE-service` or `SCOPE-frontend` overlay as fits; gates
+from `gates/archetype-gate-matrix.md` **plus** `quality:scan` + `arch:check` (mandatory for
+`packages/**`/`plugins/**` — the #745 lesson), jsr-audit for public-surface waves. Docs slices:
+`SCOPE-docs` + the doc-audit pipeline (`docs_audit` Sol pass → `docs_polish` Fable pass).
+PLAN-EVAL: conditional per current policy — required for RFC-A/RFC-B implementation waves and the
+milestone-rename filing run; N/A for single-issue mechanical slices. IMPL-EVAL: mandatory
+(this run's waiver does **not** extend to implementation runs).
+
+## 3. Agent-brief skeleton (per PR cluster)
+
+```text
+use harness
+## SKILL
+netscript-harness, netscript-doctrine, netscript-pr, , rtk
+## Contract
+
+## Gates (deliverables, not suggestions)
+
+## Boundaries
+
+## Trail
+branch /; draft PR on first commit; per-slice PR comments; closing keyword only when
+every acceptance box is truthfully tickable (close-gate #387).
+```
+
+## 4. Why these groups are implementation-ready
+
+Every draft carries current-source evidence (file:line at `fac9e339042c`), an executable
+acceptance set with negative tests, explicit boundaries against the 259-issue live board, and
+dependency edges that the group ordering above respects. The three verify-first rows (T4-07,
+theme-island CORS, saga OOM) and the G16 service-name question are staged as verification tasks,
+not implementation, so no group blocks on an unproven claim. The measurement chain
+(#1102/#1201/#1197/#1090) is consumed, not duplicated, by T7-01.
+
+## 5. Standing constraints for every implementing agent
+
+No `deno.lock` deletion or cache nukes; rtk-prefixed reads; scoped wrappers for verdicts; e2e
+(`deno task e2e:cli`) only at merge-readiness; canary discipline per `netscript-release`;
+`agentic:leak-check` on any failed runtime session; drift → run `drift.md`, never silent.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/ISSUE-DEDUP-AND-SUPERSESSION.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/ISSUE-DEDUP-AND-SUPERSESSION.md
new file mode 100644
index 0000000000..a4c73ca627
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/ISSUE-DEDUP-AND-SUPERSESSION.md
@@ -0,0 +1,170 @@
+# Issue dedup & supersession — FILED
+
+> The owner-ratified filing completed on 2026-08-08. GitHub is authoritative; see
+> `FILING-LOG.md` for Draft-ID mappings and live reconciliation receipts.
+
+Disposition of every remediation-relevant existing issue and every proposed new issue.
+Vocabulary: **KEEP** (owner unchanged) · **EXPAND** (additive amendment, text in
+`EXISTING-ISSUE-AMENDMENTS.md`) · **SPLIT** (focused child added, parent kept) · **SUPERSEDE**
+(folds into the named record; close only via owner-ratified supersession comment or downstream PR
+keyword — never by this run) · **MOVE** (milestone change; ledger in `MILESTONE-TRAIN.md` §4) ·
+**NEW** (draft in a milestone directory). Ground truth: `research/github-board-open.md`
+(2026-08-08, 259 open issues). Issues not listed here are governed milestone-level by the rename
+train (`MILESTONE-TRAIN.md`) and are untouched individually.
+
+## 1. Existing issues — dispositions
+
+### Scaffold / generation cluster
+
+| Issue | Disposition | Rationale / dependencies |
+| --- | --- | --- |
+| #1333 (p0, 0.0.5) scaffold frontend + app naming | **KEEP + EXPAND** | Remains the single frontend-modernization owner; acceptance detail added per pre-plan §1 (contract-first route, cache-first SDK, `withResource`, typed params, route-local groups, no-`any` consumer gate, four-seam distinction, states, `/design`). Its #1328 "Related" row is discharged (closed). T2 generators are the tooling it composes with — deliberately separate issues so #1333 stays landable in 0.0.5. |
+| #1335 (umbrella, Backlog) scaffold conformance | **KEEP + EXPAND + SPLIT** | Stays the conformance umbrella; sub-issue list refreshed (#1328 closed); gains child T3-02 (service layout) and links to T2 generator drafts as conformance consumers. Never closed by a PR. |
+| #1325 (0.0.5) triggers Redis glue | **KEEP + EXPAND** | Owner unchanged; audit evidence pointer attached (`runtime.stub.ts` static repro). Its generalization requirement ("a saga fix cannot ship while the trigger sibling is broken") is reinforced by T4-08's child-probing E2E. |
+| #1327 (0.0.5) `db migrate` false success | **KEEP** | Complete contract already on the issue. |
+| #1332 (0.0.5) DB-schema-first docs | **KEEP** | Complete; T5-01 must not overlap (Boundaries row in draft). |
+| #1343 (0.0.6) installed-consumer canary proof | **KEEP** | The proper home for plugin-doctor-layout reproduction (pre-plan item G routes through it). |
+| #979 / #980 (unmilestoned) port pins | **KEEP + MOVE → 0.0.8 + EXPAND** | Prerequisites (endpoint-resolving E2E, docs port passages) are delivered by T4-08/T4-06; evidence amendment attaches the stub-port findings. |
+
+### Streams / durable runtime
+
+| Issue | Disposition | Rationale |
+| --- | --- | --- |
+| #1326 (p0, 0.0.5) producer reconnect | **KEEP + EXPAND** | Owner unchanged (0.0.5 close-out); amendment attaches audit citations (`create-durable-stream.ts` line-level) and R2/R3 reproductions. Receipt-typing for `upsert`/`delete` (currently `void`) is *included* in its acceptance re-read — flagged in the amendment, not a new issue. |
+| #1329 (p0, 0.0.5) SSE envelope | **KEEP + EXPAND** | Owner unchanged; #1326 planned as its pair (already cross-declared). |
+| T4-03 storage semantics | **NEW** | Explicitly the uncovered remainder both issues' scopes exclude (persistence mode/`STREAMS_DATA_DIR`/restart proof) — pre-plan item F confirmed uncovered by the board sweep. |
+
+### Docs / MCP / measurement chain
+
+| Issue | Disposition | Rationale |
+| --- | --- | --- |
+| #1208 (p0, 0.0.5) tutorials ph.1 | **KEEP + EXPAND** | Amendment records the phase-2 filing obligation (promised as a checklist comment, not yet an issue — top dedup trap). |
+| #1210 (0.0.6) per-API deep dives | **KEEP + EXPAND** | Gains the cross-capability golden-recipe list (pre-plan §3). |
+| #1260 / #1201 / #1102 (0.0.5–0.0.6) MCP corpus/retrieval | **KEEP** | Chain untouched; T5-03/T5-04 are plumbing-only with Boundaries rows. |
+| #1197 / #1090 measurement pair | **KEEP + EXPAND** | Wave-6 measured evidence attached; Wave-7 smoke *consumes* them (see `WAVE7-AND-AGENT-ADOPTION.md` §5). |
+| #1275 (Backlog) migration chapter | **SUPERSEDE → #1279** | Duplicate umbrella pair; #1279 is the record. |
+| #1279 (0.0.6) migration chapter | **KEEP + MOVE → 0.0.15** | Post-remediation adoption surface. |
+| #1277 (Backlog) docs-site polish | **KEEP** | Not remediation-critical; untouched. |
+
+### Type soundness
+
+| Issue | Disposition | Rationale |
+| --- | --- | --- |
+| #1278 (0.0.6, umbrella) | **KEEP as epic-of-record + EXPAND** | Gains #1276's measured numbers (56 casts / 8 ignores / 7 allowances) + T1–T6 tranche structure; T6-01 lands inventory-C as its trackable child. |
+| #1276 (Backlog, umbrella) | **SUPERSEDE → #1278** | Same 2026-08-04 directive, same evidence; two prose-only umbrellas is the board's clearest duplicate. |
+| #1245 (Backlog) island query types | **KEEP + rescope (EXPAND)** | ~75% landed by merged #1265; remnant = `@throws` mismatch + regression tests + consumer migration note. Re-implementing is the risk. |
+| #1249 (Backlog) `controlProps`/Zod 4 | **KEEP** | Both defects execution-confirmed current; contract complete. |
+
+### Plugin architecture / auth
+
+| Issue | Disposition | Rationale |
+| --- | --- | --- |
+| #922 + #923–#946 (0.0.7→**0.0.9** by rename) | **KEEP + EXPAND** | Train moves wholesale by rename; internal wave order intact; amendment maps stale beta.13/15/17 body labels to the renamed cuts and re-raises the #427/#432 re-baseline. #928's contract freeze reviews against ratified RFC-A. |
+| #1093 (0.0.6) discovery hardcoding | **KEEP** | Already the right shape; RFC-A cites it as an alignment constraint. |
+| #871 + #872–#887 (incl. #884/#885) | **KEEP** | Enterprise scope untouched at 0.0.14 (renamed from 0.0.12). TA drafts carry explicit Boundaries: defects/defaults only, no org contracts, no vendor adapters. |
+| #934 gateway / #942 auth frontend | **KEEP** | TA-02/TA-04 align to them via Boundaries; no overlap. |
+| #1243 (auth CLI port default) | **KEEP** | Named owner for the hardcoded 4437; TA drafts cite, don't absorb. |
+
+### Aspire / infra / release
+
+| Issue | Disposition | Rationale |
+| --- | --- | --- |
+| #1280 (0.0.6, blocked) backing health | **KEEP (blocked)** | Do not re-litigate; T4-02 is the *plugin child* liveness contract the issue explicitly does not cover. |
+| #1320 (0.0.6, blocked) single Zod | **KEEP (blocked)** | Upstream-constrained. |
+| #1004 / #1126 / #1163 / #1166 / #1169 (0.0.5 release machinery) | **KEEP** | 0.0.5 close-out scope; #1126's stale checkboxes get a hygiene amendment (9 children already closed). |
+| #301 (Backlog, road-to-stable umbrella) | **KEEP + EXPAND** | Hygiene amendment: 5 unchecked children already closed. |
+| #863 / #864 / #175 / #767 / #768 (0.0.2 stragglers) | **MOVE → owner retriage** | Explicit retriage (default Backlog); #175 additionally needs labels (zero today). Never silently closed. |
+
+### Epic-overlap normalization (amendments only — no membership changes)
+
+#823 ⊂ #327 (children #451/#453–#455): #327 becomes umbrella-of-record, #823 narrows to the
+Nitro-output RFC. #892 ↔ #327/#830: mutual boundary notes. #400 ↔ #922: schedule the #427/#432
+re-baseline at 0.0.9 entry. Orphaned single-member epic labels (`epic:desktop-frontend`,
+`epic:docs-cut`, `epic:telemetry-revamp`) recorded for the labels.yml parity amendment.
+
+## 2. Proposed new issues (41 drafts + 2 RFC documents)
+
+Full text: `milestones//-*.md` and `rfcs/`. Deps reference draft-IDs and live
+issue numbers. Every draft carries `## Boundaries` naming adjacent owners.
+
+### 0.0.6 — Verification, docs truth & RFC ratification (13 drafts)
+
+| Draft | Title (short) | Prio | Depends on |
+| --- | --- | --- | --- |
+| T1-01 | rfc: SdkClientContribution tracking issue (RFC-A doc in `rfcs/`) | p1 | — |
+| T3-01 | rfc: production command composition kit (RFC-B doc in `rfcs/`) | p1 | — |
+| T5-01 | docs/sdk: one canonical client dialect (3 module names, 2 query APIs) | p0 | — (coord #1333/#1335) |
+| T5-02 | test(docs): compile-the-docs gate replaces needle checker | p1 | T5-01 |
+| T5-03 | fix(agent): emit `--docs-root` in `.mcp.json`; corpus visible | p1 | — |
+| T5-04 | fix(mcp): `execute_command` version pin / local-host spawn | p1 | — |
+| T5-05 | docs(reference): plugin-core pages, publish-gate path, README dialect | p2 | T5-01, T5-02 |
+| T6-01 | chore(quality): `quality:scan` export-blind `any`, allowance ids, docs snippets | p1 | — (Part of #1278) |
+| T6-02 | chore(ci): fresh-ui joins root check/lint; lock self-mutation | p1 | — (blocks T6-01 lock decision) |
+| T6-03 | docs(doctrine): verdict refresh + `arch:check:repo` two mechanical fixes | p2 | — |
+
+### 0.0.7 — Typed seams + generation (12 drafts)
+
+| Draft | Title (short) | Prio | Depends on |
+| --- | --- | --- | --- |
+| T1-02 | feat(sdk): re-expose oRPC link seams (headers/interceptors/plugins/fetch/link) | p1 | T1-01, T1-04 |
+| T1-03 | fix(sdk): `safe()` drops `TError`; docs example doesn't compile | p1 | T1-01 (file as Part of #1278 if its prose already names it — see ledger) |
+| T1-04 | refactor(sdk): transport policy behind one owned function (pre-oRPC-v2) | p1 | T1-01 |
+| T1-05 | feat(sdk/auth): auth contribution dogfood (`authClient`) | p1 | T1-01, T1-02, T1-04 |
+| T1-06 | feat(sdk): trace-context as second, non-auth contribution | p1 | T1-01, T1-02, T1-05 |
+| T2-01 | feat(cli): resource route-slice generator | p1 | T2-03 (hard), T2-02; RFC-A for contribution clause |
+| T2-02 | feat(cli): contract-derived client/query/invalidation generator | p1 | T2-03; RFC-A clause |
+| T2-03 | fix(cli): `resolveProjectRoot` app-root targeting + E2E/docs twins | p1 | — |
+| T2-04 | fix(cli): `ui:add page` emits the advertised data-screen triad | p1 | T2-03 |
+| T2-05 | chore(design): `/design` registry sync gate (50 vs 66) | p1 | — |
+| T2-06 | fix(fresh): `crudExample` route alias defect (test asserts the bug) | p2 | — |
+| T2-07 | fix(scaffold): wire `cachedAt`→`initialDataUpdatedAt`; consumer migration note | p2 | — (cites merged #1265) |
+
+### 0.0.8 — Runtime truth + service slice (19 drafts)
+
+| Draft | Title (short) | Prio | Depends on |
+| --- | --- | --- | --- |
+| T3-02 | fix(scaffold/service): service internal layering child (Part of #1335) | p1 | — |
+| T3-03 | feat(service): command kit implementation | p1 | T3-01, T3-02 |
+| T3-04 | docs: outbound webhook delivery recipe + template | p2 | T3-03 soft |
+| T4-01 | fix(sagas): non-ignorable publish receipts; kill 8092 fallback | **p0** | — (seq. T4-06, T4-08) |
+| T4-02 | fix(plugins): child liveness contract (workers/triggers bare; sagas partial) | p1 | T4-06, T4-08 |
+| T4-03 | fix(streams): durable-storage semantics (`STREAMS_DATA_DIR`) | p1 | — (coord #1326/#1329) |
+| T4-04 | fix(sagas): compensation telemetry call-sites + E2E span asserts | p1 | — |
+| T4-05 | fix(plugins): WORKER_CONCURRENCY mismatch; always-throwing root exports | p2 | — |
+| T4-06 | fix(plugins): pre-randomization port hardcodes in stubs + consumer stub | p2 | #979 prereq |
+| T4-07 | verify(aspire): ServiceReferences injection claim (counter-evidence found) | p1 | — verify-first |
+| T4-08 | test(e2e): child + streams probes; compensating/COMPENSATED status truth | p1 | T4-02, T4-06 |
+| TA-01 | fix(scaffold): `/api` protected by default | **p0** | TA-02, T1-05 |
+| TA-02 | fix(plugin): `createPluginService` auth seam (incl. remote authenticator port) | **p0** | — |
+| TA-03a | fix(auth): signout revokes arbitrary session ids | **p0** | TA-02 |
+| TA-03b | fix(auth): signin/callback discard `Set-Cookie` | **p0** | — |
+| TA-03c | fix(service): default CORS `origin:'*'` vs credentialed calls | p1 | — |
+| TA-04 | feat(service): typed principal + `$meta` policy metadata | p1 | — (consumed by T1-05/#934; prereq for #884) |
+| TA-05 | test(e2e): authenticated + rejection auth gates | p1 | TA-01/02/03a |
+| T7-01 | verify(0.0.8): Wave-7 measured adoption smoke (exit gate) | p1 | T2-01/02, T4-01, TA-01, #1197, #1090 |
+
+## 3. Stage-E ledger — cross-pack items the packs flagged for supervisor decision
+
+1. **Corpus corrections that must reach the master plan:** T4-07's wave-6 claim has worktree
+ counter-evidence (drafted verify-first, never implementation); sagas runner *does* get a
+ generated health check (workers/triggers don't) — T4-02 written against the corrected surface;
+ auth arch-debt anchors exist in `.llm/harness/debt/arch-debt.md` (audit G13 first half false —
+ no issue); `#1278` Inventory A is ~80% discharged and Inventory D counts 6 (not ~19) exempt
+ soundness tests; `quality:scan:repo` already covers all packages (the gap is export-blind
+ `any` + free-text allowances, not scope); `arch:check:repo` red = 52 A14 false positives + 1
+ no-`--root` config bug — two cheap fixes, not package debt.
+2. **Ownerless items needing a home (owner decision):** server-side plugin seam
+ (`PluginContractRouter = object`, Hono-vs-oRPC middleware, G5/G8/G9) — recommend a 0.0.7
+ companion draft or an explicit deferral; saga compensation *semantics* (no prior-step rollback,
+ unpersisted compensation state, silent missing-handler on primary path) — candidate T4-09;
+ dead `VALIDATE_TRACES_SCRIPT` + lost `validateOtlpExporterEndpoint()` (GAP-6) — p2 tooling row;
+ harness read-first paths that don't exist (D12) — cheap fix, suggest fold into T6-03 at filing;
+ MCP generation-surface tools (`list_generators`…) — defer to #1126/#1201 chain decision.
+3. **Cross-pack dependency:** `ServiceQueryUtils` context/`TError` narrowing (S10) lives in T2's
+ query work but bites T1-05/T1-06 at the TanStack layer — sequencing note for the handoff.
+4. **Filing-time checks:** T1-03 files as `Part of #1278` if its live prose already names
+ `safe`/`isDefinedError`; #451 gets an unblocked-by-T1-02 amendment; #1263 cross-references
+ T1-03 (server-side twin); oRPC 1.14.15 bump folded in T1-04 acceptance (split if owner
+ prefers a deps issue); TA-03a's "CLI sends a credential" box may move to #1243 as amendment.
+5. **Verify-first rows carried (no drafts):** theme-island CORS, saga OOM, plugin-doctor layout
+ (via #1343), `AUTH_API_SERVICE_NAME` `'auth-api'` vs `'auth'` runtime resolution (G16).
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/MASTER-PLAN.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/MASTER-PLAN.md
new file mode 100644
index 0000000000..024cd11a57
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/MASTER-PLAN.md
@@ -0,0 +1,151 @@
+# NetScript long-range remediation — MASTER PLAN — FILED
+
+> Owner-ratified filing completed 2026-08-08. GitHub is authoritative; see `FILING-LOG.md` for
+> live issue numbers and milestone receipts. This document remains the planning rationale.
+
+Seed run `plan-fable5-remediation-roadmap--seed` · PR #1347 · baseline `origin/main` @
+`fac9e339042c` (re-verified unchanged at plan lock, 2026-08-08). Supervisor: Claude Fable 5 ·
+high. PLAN-EVAL and IMPL-EVAL owner-waived (drift D-2); the owner personally reviews this plan.
+
+This is the integrating document. Detail lives in: `SYNTHESIS.md` (evidence synthesis),
+`ISSUE-DEDUP-AND-SUPERSESSION.md` (per-issue dispositions + 41 new drafts + Stage-E ledger),
+`MILESTONE-TRAIN.md` (train + moves + entry/exit), `WAVE7-AND-AGENT-ADOPTION.md`,
+`EXISTING-ISSUE-AMENDMENTS.md`, `milestones/*/` (complete issue drafts), `rfcs/` (RFC-A, RFC-B),
+`IMPLEMENTATION-HANDOFF.md`, and the cited corpus under `research/`.
+
+## 1. The product bar
+
+A credible production meta-framework in 2026 must clear (evidence:
+`research/external/meta-frameworks.md`): an end-to-end typed data story at the cache-coherence
+frontier (SvelteKit remote functions, TanStack Start serialization checks — the two designs our
+oRPC seam is measured against); scaffold/CLI generation that emits the idiomatic app, not a
+counter stub; a first-party auth story that composes with the typed client; durable background
+work with causal proof; observability that follows one request across every boundary; and an
+agent-native surface (docs corpus + MCP + generated conventions) that measurably changes agent
+behavior.
+
+**NetScript's genuine differentiation, ranked** (same corpus): (1) first-party saga/compensation
+orchestration — unowned by every JS meta-framework surveyed; (2) Aspire as a non-proprietary
+local orchestration graph ("Encore's dev experience without Encore's cloud"); (3) plugin-seam
+uniformity as the carrier for agent-teaching plugins; (4) the portable oRPC/OpenAPI contract as
+the reason the agent story works; (5) Deno single-toolchain. The remediation program exists to
+make the table-stakes true so the differentiation is believable.
+
+## 2. Principles (owner-ratified inputs, now evidence-hardened)
+
+1. **Generation over prose.** Six waves prove instruction does not transfer; the only untested
+ lever that worked was making the right thing the emitted thing (`SYNTHESIS.md` §1).
+2. **Types over convention.** Arbitrary `any`/casts in route code are unacceptable; the no-`any`
+ gate extends to *consumer* output, not just framework exports.
+3. **Composable seams over escape hatches.** Auth gets no bespoke hook; it dogfoods the generic
+ `SdkClientContribution` chain (RFC-A), proven general by a second non-auth contribution.
+4. **Runtime truth over green wrappers.** Receipts are non-ignorable, children report liveness,
+ durable claims survive restart, and every causal claim has a trace that fails if the seam is
+ removed. (The fourth leg this run adds to the pre-plan's three.)
+5. **Current GitHub wins over carried-in reports** — enforced throughout; six corpus corrections
+ from source re-verification are recorded in `ISSUE-DEDUP-AND-SUPERSESSION.md` §3.1.
+
+## 3. Current state in one paragraph
+
+The board holds 259 open issues across 13 open milestones; 0.0.5 is mid-canary (canary.16 green)
+with its continuation plan ~40% delivered and four p0s undispatched. The web layer's builder
+surface is complete and nearly cast-free, but the scaffold demonstrates none of it; the CLI has
+no generator for the canonical slice and its one page verb emits a counter into the wrong tree;
+the SDK client is sealed (auth cannot compose; oRPC's machinery is hidden, not missing); docs
+teach three names for the client module and two query dialects; runtime plugins can report green
+while children are dead, receipts are droppable, and "durable" streams are in-memory; the docs/
+MCP discovery chain is structurally unwired (`--docs-root` never emitted). Full evidence:
+`SYNTHESIS.md` §1–2, `research/repo-audit/*`.
+
+## 4. Target architecture (the five contracts the program lands)
+
+1. **The typed extension chain (RFC-A).** `SdkClientContribution`: one versioned, typed chain
+ extending client construction, request context, credentials/headers, transport middleware,
+ procedure policy metadata (oRPC `$meta`), error types, query factories and invalidation.
+ Mostly *unhides* oRPC 1.14.6 machinery. Host-app usable without plugins; compile/config-time
+ failure on absence/version-mismatch/conflict; auth first consumer, trace-context second.
+2. **The canonical vertical slice, generated.** DB-derived schema (where present) → API contract
+ → typed route contract + params/search → generated client/query/invalidation module →
+ `definePage` composition root → `withResource`/layers → forms/partials/streams → route-local
+ `(_components)/(_islands)/(_shared)/(_lib)` → Fresh-UI states → tests that reject `any`/raw
+ fetch/manual parsing. Emitted by `ui:add` slice mode + the client generator; #1333 makes the
+ default app the exemplar.
+3. **The flexible service slice + command model (RFC-B).** Collapsible
+ `domain/application/ports/adapters/routers/auth` vocabulary; transactional commands with
+ expected-version, idempotent receipts, audit+outbox in one commit; `service add-handler`
+ places into the slice; telemetry carries a command vocabulary.
+4. **Runtime truth.** Non-ignorable publish receipts; endpoint discovery that errors instead of
+ guessing; child-process liveness in the health surface; explicit stream persistence modes;
+ compensation visible in traces; E2E gates that probe children and assert spans.
+5. **The agent-native surface.** One docs dialect, compiled docs snippets, MCP corpus wired by
+ default, generation verbs discoverable, measured adoption (Wave-7) as the program's exit
+ criterion.
+
+## 5. Dependency DAG (program level)
+
+```text
+0.0.5 close-out (existing scope only)
+ └─> 0.0.6 RFC-A ratify ──────────────┬─> 0.0.7 T1 seam impl ─┬─> 0.0.7 T1-05/06 dogfoods
+ RFC-B ratify ──────────────┼───────────────────────┼─> 0.0.8 T3-03 command kit
+ T5 docs dialect + gates ───┤ 0.0.7 T2 generators ─┴─> 0.0.8 T7-01 Wave-7 smoke
+ T6 quality/hygiene gates ──┘ │ ▲
+ (T5-01 dialect choice feeds T2-02 naming) │
+ 0.0.8 T4 runtime truth + TA auth defects + T3-02 service slice ────────┘
+ (T4-06/T4-08 are #979's prerequisites; TA-02 precedes TA-01; T4-01 sequenced with T4-08)
+0.0.9 (renamed) #922 frontend-contrib — #928 contracts reviewed against ratified RFC-A
+```
+
+Intra-milestone edges are on every draft header and in `ISSUE-DEDUP-AND-SUPERSESSION.md` §2.
+The only cross-pack sequencing hazard is recorded in §3.3 there (`ServiceQueryUtils` narrowing
+lives in T2 but bites T1-05/06).
+
+## 6. Sequencing and release mechanics
+
+Per `MILESTONE-TRAIN.md`: two inserted milestones via the house rename pattern (verified twice in
+board history); every existing issue retained; five explicit per-issue moves; canary-first
+publishing per `netscript-release` throughout; Wave-7 verdict gates the train past remediation.
+No `wave:*` labels (dead system); no semver jump — a "0.1.0" claim is exactly the Wave-7 GO.
+
+## 7. Owner-fork sweep (numbered; none silently taken)
+
+| # | Fork | Default proposal (reversible) |
+| --- | --- | --- |
+| F1 | Insert two milestones (rename shift of 0.0.7→0.0.13 up two) vs pack remediation into 0.0.6/0.0.7 | **Insert** — keeps #922's nine-p0 path unmixed |
+| F2 | #922 before vs after remediation cuts | **After** (new 0.0.9); #928 contract freeze reviews against ratified RFC-A |
+| F3 | Fold #1276→#1278 and #1275→#1279 (close the Backlog duplicates) | **Fold** — amendment text ready; closes happen only on owner action |
+| F4 | RFC mechanism: issue-hosted (#1123 precedent) vs first-ever `rfcs/NNNN` file | **Issue-hosted**, divergence from `rfcs/README.md` recorded (zero file RFCs exist on main) |
+| F5 | 0.0.2 stragglers (#175/#767/#768/#863/#864) destination | **Backlog + labels fixed** — explicit retriage, no closes |
+| F6 | #1279 (migration chapter) leaves 0.0.6 | **→ 0.0.15** — post-remediation adoption surface |
+| F7 | Canonical client dialect: module name + ONE query API (`createQueryFactories`+KV vs `createServiceQueryUtils`) | T5-01 proposes `lib/.ts` + query-factories path; **owner ratifies the dialect** before docs rewrite |
+| F8 | Server-side plugin seam (`PluginContractRouter = object`, Hono-vs-oRPC middleware) | **Defer** to RFC-A unresolved-questions; revisit at 0.0.7 planning (currently ownerless) |
+| F9 | Saga compensation *semantics* (no prior-step rollback, unpersisted state) — file T4-09 now vs after T4-01 evidence | **After T4-01** lands its verification evidence |
+| F10 | T4-01 receipt mechanism: compiler-forced discrimination vs throw-on-rejection + usage gate | Draft presents both; **owner picks at ratification** |
+| F11 | Wave-7 scale: 3 arms × 2 runs × frontier models (cost) | **Approve as designed**; trimming arm B (init-gate lever) is the acceptable cut |
+| F12 | oRPC 1.14.15 bump: folded into T1-04 vs standalone deps issue | **Folded** (patch-level, identical export surface) |
+
+## 8. Risk register
+
+| Risk | Mitigation |
+| --- | --- |
+| Duplicate filing against 9 prose-only umbrellas + promised-but-unfiled #1208-ph.2 | Every draft has `## Boundaries`; dedup table §1/§2; filing happens once from a manifest |
+| Re-implementing landed work (#1245→#1265, #1328, #1184) | Dispositions cite merge SHAs; drafts cite current source, not wave-era observations |
+| Milestone-rename blast radius (~150 issues' display) | House pattern (title-only rename, zero per-issue mutation) + move ledger |
+| RFC-A over-design | Constraint written into the RFC: unhide oRPC, don't parallel it; two-consumer proof required |
+| 0.0.5 scope creep | Train rule: no new scope enters 0.0.5; remediation starts at 0.0.6 |
+| Corpus staleness at filing time | Stage-H filing (later, owner-ratified) re-verifies issue states before mutation; GitHub wins |
+| Program stalls mid-train | Each cut has entry/exit criteria; Wave-7 verdict is a go/no-go, not a vibe |
+
+## 9. Explicit exclusions (this plan deliberately does not touch)
+
+Enterprise-auth vendor scope (#871 children, incl. #884/#885 — TA drafts are defects/defaults
+only); deployment/process-manager/desktop epics (#327/#510/#830/#892/#823 — normalization
+amendments only); Dev Dashboard (#400, paused); AI stack (#238) beyond the trace-context
+contribution; docs-site visual polish (#1277); migration-chapter authoring (#1279, moved late);
+blocked upstream items (#1280, #1320); `wave:*` label revival; any semver-minor jump; and all
+GitHub board mutation, which remains owner-gated Stage H.
+
+## 10. What ratification unlocks (pointer)
+
+`IMPLEMENTATION-HANDOFF.md` names the first executable PR groups, their harness profiles, gates,
+and agent briefs. Nothing in it launches until the owner ratifies the forks above and the filing
+manifest is executed by a later, separately-authorized run.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/MILESTONE-TRAIN.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/MILESTONE-TRAIN.md
new file mode 100644
index 0000000000..6530ccf11c
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/MILESTONE-TRAIN.md
@@ -0,0 +1,107 @@
+# Milestone train — FILED
+
+> Executed after owner ratification on 2026-08-08. GitHub is authoritative; see `FILING-LOG.md`
+> for the final milestone-object mapping and reconciliation receipts.
+
+Proposed release train for the long-range remediation program. Grounded in the live board
+snapshot (`research/github-board-open.md`, 2026-08-08: 259 open issues, 13 open milestones) and
+the house milestone-shift pattern verified twice in `research/github-board-history.md` §10
+(rename-in-place highest→lowest preserving number/state/description, then create the freed title;
+per-issue written move reasons; pre/post tables). **Every existing issue is retained; whole
+milestones move by rename, so zero per-issue mutation is needed for the shifted content.**
+
+## 1. Train at a glance
+
+| Cut | Name / theme | Content | Provenance |
+| --- | --- | --- | --- |
+| **0.0.5** (current, mid-canary) | Close-out as already scoped | The 21 open issues incl. the undispatched W2–W5 remainder (#1326+#1329 pair, #1333, #1208 ph.1, #1338…). **No new scope enters.** | existing |
+| **0.0.6** | **Verification, docs truth & RFC ratification** | Existing 22 (#1343, #1210, #1260, #1201, #1278 amended, #1093, #1280/#1320 blocked…) + new: RFC-A + RFC-B tracking issues (T1-01, T3-01), canonical-dialect docs fixes (T5-01…T5-05), soundness/hygiene gates (T6-01…T6-03). Move out: #1279 → 0.0.15 (see §4). | existing + drafts |
+| **0.0.7 (new)** | **Typed seams + generation** | T1-02…T1-06 (oRPC re-exposure, typed errors, transport consolidation, auth + second contribution dogfoods), T2-01…T2-07 (route-slice generator, client/query generator, `resolveProjectRoot`, `ui:add` triad, `/design` sync, defect fixes). | new drafts |
+| **0.0.8 (new)** | **Runtime truth + service slice** | T4-01…T4-08 (saga receipts p0, child liveness, stream durability, saga spans, env/port fixes, E2E child gates), TA-01…TA-05 (auth defects + generated-path defaults), T3-02…T3-04 (service layout, command kit, webhook recipe), Wave-7 measured smoke as the exit gate. #979/#980 join here (their E2E/docs prerequisites are T4-06/T4-08). | new drafts + 2 unmilestoned |
+| **0.0.9** (was 0.0.7) | Frontend contribution layer | #922 + #923–#941 intact (9 p0 critical path unchanged internally). | rename |
+| **0.0.10** (was 0.0.8) | as currently scoped (48) | unchanged membership | rename |
+| **0.0.11** (was 0.0.9) | as currently scoped (15, incl. #944) | unchanged | rename |
+| **0.0.12** (was 0.0.10) | as currently scoped (2) | unchanged | rename |
+| **0.0.13** (was 0.0.11) | as currently scoped (10, incl. #942/#943) | unchanged | rename |
+| **0.0.14** (was 0.0.12) | enterprise-auth contracts band (#884/#885 et al., 11) | unchanged | rename |
+| **0.0.15** (was 0.0.13) | horizon band (44: process-manager/deploy-plugin tail) + #1279 | rename + 1 move |
+
+## 2. Why insert exactly two milestones
+
+- **Dependency shape.** RFC-A/RFC-B ratify in 0.0.6 (docs/verification cut — cheap, already
+ half-full of verification work). Their implementations are the only content of new-0.0.7/0.0.8,
+ so the remediation program never mixes with #922's nine-p0 critical path — which was the
+ pre-plan's explicit constraint ("future plugin/auth milestones shift only as part of a coherent
+ dependency graph; never silently absorbed", `research/preplan-package.md` §Milestone).
+- **Coherence for #922.** #928 (contracts/v1) freezes plugin contribution contracts. RFC-A
+ defines the *SDK/client* contribution axis. Landing RFC-A first (0.0.6 ratify → 0.0.7
+ implement) lets #928's envelope be reviewed against it instead of forcing a v2 envelope later.
+- **Wave-7 placement.** The measured unfamiliar-agent smoke is the *exit gate of 0.0.8*: the
+ program's thesis (generation changes agent behavior) is falsifiable exactly once the
+ generators + runtime truth exist. Advancing to 0.0.9 (#922) without that proof repeats the
+ capability-present-not-activated failure the corpus measured six times.
+
+## 3. Rename execution order (for the filing stage, owner-ratified only)
+
+Per the house pattern (highest→lowest so titles never collide; title is the only field changed):
+
+```text
+0.0.13 → 0.0.15 0.0.12 → 0.0.14 0.0.11 → 0.0.13 0.0.10 → 0.0.12
+0.0.9 → 0.0.11 0.0.8 → 0.0.10 0.0.7 → 0.0.9
+then create: 0.0.7 "Typed seams + generation", 0.0.8 "Runtime truth + service slice"
+```
+
+Milestone descriptions get an authority banner + one-line theme; no due dates (house norm: none
+exist today). **This run executes none of this.**
+
+## 4. Per-issue moves (the complete list — everything else moves by rename or stays)
+
+| Issue | From → To | Reason |
+| --- | --- | --- |
+| #1279 (migration chapter, umbrella) | 0.0.6 → 0.0.15 | Marketing/adoption surface, not remediation; #1275 duplicate folds into it first (amendments). |
+| #979 (plugin API port pins) | none → 0.0.8 | Its two prerequisites (endpoint-resolving E2E gates, docs port passages) are T4-08/T4-06 work in 0.0.8. |
+| #980 (`service add` port pin) | none → 0.0.8 | Sibling of #979, same prerequisite chain. |
+| #1000 (docs) | none → Backlog / Triage | Untriaged; hygiene amendment adds labels. |
+| #175, #767, #768, #863, #864 | 0.0.2 → owner retriage (default Backlog) | 0.0.2 shipped long ago; #175 has zero labels. Explicit retriage, never a silent close. |
+
+New drafts are filed directly into their §1 milestones at ratification time.
+
+## 5. Entry/exit criteria per remediation cut
+
+- **0.0.6 entry:** 0.0.5 stable cut green (canary-pair doctrine, `netscript-release`). **Exit:**
+ RFC-A + RFC-B ratified (tracking issues closed as accepted); docs speak ONE client dialect and
+ the compile-the-docs gate is live; #1343 installed-consumer proof green; #1278 guard rail
+ fail-closed.
+- **0.0.7 entry:** RFC-A accepted. **Exit:** a scaffolded app + one added service reach a typed,
+ cache-first, auth-composable page **entirely through generated modules** (no hand-written
+ client/query wiring), with the no-`any` consumer gate green; second-run generator byte-identity
+ proven.
+- **0.0.8 entry:** 0.0.7 generators shipped in a canary. **Exit:** saga publish/compensation
+ causally provable from persisted state + one correlated trace; child-liveness states visible;
+ durable-stream restart proof; **Wave-7 measured smoke shows the generated path adopted or
+ explicitly rejected by an unfamiliar agent** (see `WAVE7-AND-AGENT-ADOPTION.md`).
+- **0.0.9+ entry:** Wave-7 verdict recorded. #922 proceeds on its own already-planned wave
+ structure.
+
+## 6. Epic-overlap normalization (amendments, not moves)
+
+Recorded here because the train depends on epic scopes staying disjoint; full text in
+`EXISTING-ISSUE-AMENDMENTS.md`:
+
+- #823's entire open membership (#451/#453–#455) sits inside #327's child list → amendment
+ declares #327 the umbrella-of-record for those four, #823 narrowed to the Nitro-output RFC.
+- #400 ↔ #922: #427/#432 "KEEP-and-re-baseline" from the #890 RFC supersession map has never
+ been executed → amendment schedules the re-baseline at 0.0.9 entry.
+- #892 vs #327/#830: no cross-reference in either body → amendment adds mutual boundary notes.
+- #922 body wave labels (beta.13/15/17) vs actual milestones → additive clarification comment
+ mapping waves to the renamed train (0.0.9/0.0.13/0.0.15).
+- Duplicate umbrellas: #1276 → folds into #1278 (0.0.6, epic-of-record); #1275 → folds into
+ #1279 (moves to 0.0.15).
+
+## 7. What this train deliberately does not do
+
+No issue is closed by this plan (folds happen via owner-ratified supersession comments; closes
+only via downstream PR keywords). No epic absorbs another epic's children. 0.0.5 membership is
+untouched. The `wave:*` label system stays dead (conventions corpus: zero recent usage) — the
+train uses milestones only. Semver stays 0.0.x; no minor-version jump is proposed until the
+0.0.8 exit proof exists (a credible "0.1.0" claim is exactly the Wave-7 verdict).
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/QWEN-FINAL-ADVERSARIAL-EVAL.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/QWEN-FINAL-ADVERSARIAL-EVAL.md
new file mode 100644
index 0000000000..43548b101e
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/QWEN-FINAL-ADVERSARIAL-EVAL.md
@@ -0,0 +1,226 @@
+# QWEN FINAL ADVERSARIAL ACCEPTANCE EVALUATION — RFC-A (#1390) × RFC-B (#1389)
+
+## 0. Identity and transport
+
+| Field | Value |
+| --- | --- |
+| Evaluator | Qwen 3.8 Max (`openrouter/qwen/qwen3.8-max`), fresh session, maximum reasoning effort |
+| Role | Final independent adversarial acceptance reviewer (root-orchestrator-owned pass; separate from generator Codex and cycle-1/2 evaluator Claude Fable 5) |
+| Date | 2026-08-08 |
+| Transport | OpenCode CLI on Linux; direct `git`/`gh`/registry/web access from the evaluation environment |
+| Mutation boundary honored | This session wrote **only** this report. No RFC text, fixture, harness artifact, product code, branch, PR, label, comment, issue, milestone, or merge state was touched. No subagents or rival evaluators were launched. |
+
+## 1. Exact evaluated SHAs
+
+| Object | SHA | Verified |
+| --- | --- | --- |
+| RFC-A accepted content (`rfcs/0000-sdk-client-contributions.md`) | **`78a7cecd1d5eaafa7a65bc25a21af497567128dc`** | `git show` in `/home/codex/repos/ns-rfc-sdk-client`; RFC text byte-identical at branch HEAD (diff content-commit→HEAD touches only `.llm/runs/` artifacts) |
+| RFC-A branch HEAD (cycle-2 verdict artifact) | `14b5c858cbead4aabe06e991528d8e9eaaaca7dc` | matches live `gh pr view 1390 .headRefOid` |
+| RFC-A fixture | `packages/sdk/tests/type-fixtures/sdk-client-contributions-rfc_type.ts` @ `14b5c858c` | read in full |
+| RFC-B accepted content (`rfcs/0000-command-composition-kit.md`) | **`c98c08adabbd992a557ff7c596deae68b9c9cd62`** | `git show` in `/home/codex/repos/ns-rfc-command-kit`; RFC text byte-identical at branch HEAD |
+| RFC-B branch HEAD (cycle-2 verdict artifact) | `57b51128fbc28187b55e1e377cd3e1777332dfa1` | matches live `gh pr view 1389 .headRefOid` |
+| Cross-RFC brief | `CROSS-RFC-REVIEW.md` @ `cb0ca256e` in `/home/codex/repos/netscript-fable5-remediation-plan` | read in full |
+| Product baseline | `origin/main` @ `fac9e339042c5394bf882311657d8981d353a1c3` | merge-base of both RFC branches; both worktrees clean |
+
+## 2. Evidence read and executed
+
+**Read (complete):** both RFCs at accepted SHAs (1,611 + 1,966 lines); both `plan-eval.md` histories (cycle 1 findings F-A1–F-A10 / F-B1–F-B7 + cycle-2 resolution tables); RFC-A committed type fixture (503 lines); both `final-handoff.md`; RFC-A `drift.md` (via cycle-2 artifact set); both cycle-2 worklog gate tables; `CROSS-RFC-REVIEW.md`; seed-run `MASTER-PLAN.md`, `ISSUE-DEDUP-AND-SUPERSESSION.md`, `FILING-LOG.md`; `rfcs/README.md` (RFC process); `0000-template.md` presence; netscript-pr skill label/lifecycle sections; `.github/labels.yml` status taxonomy; doctrine `02-public-surface.md` sanctioned oRPC-types exception.
+
+**Executed (independent, not taken from prior evaluators):**
+
+- `deno check --unstable-kv packages/sdk/tests/type-fixtures/sdk-client-contributions-rfc_type.ts` → **exit 0** (third independent execution of this gate).
+- Live registry queries (npm): `@orpc/client` / `@orpc/server` dist-tags `latest=1.15.0`, `beta=2.0.0-beta.26`; publish timeline (v1.15.0 at 2026-08-08T13:52Z, **after** beta.26 at 11:21Z; seven v1 patches since 2026-07-25); `@orpc/opentelemetry` dist-tags `latest=1.14.11`, `beta=2.0.0-beta.26` (v1 line confirmed).
+- Primary upstream fetch: official v1→v2 migration guide (`v2.orpc.dev/docs/migrations/from-v1`) — verified wire incompatibility, middleware-dedup removal, `.$meta`→`defineMeta`, GET rejection by default (`allowMethods` = POST/PUT/PATCH/DELETE), `Sec-Fetch-Mode` CSRF plugin, `status`→`errorStatusMap` split, `safe()` result-shape change, `isDefinedError`→`isInferableError`, serializer-instance change, `@orpc/otel`→`@orpc/opentelemetry`, `RequestHeadersPlugin`→`RequestHeadersHandlerPlugin`, Zod-v4-only `@orpc/zod`, TanStack package consolidation.
+- Live GitHub: PR #1390/#1389 state/labels/head/mergeable/body/comments; GraphQL review threads (**0 total / 0 unanswered on both**); check-run state at both HEADs (all lanes `skipped` — path-filtered docs-only); full issue sweep of #1348–#1388 (titles/states/milestones/labels), plus #451, #1093, #1278, #1293, #1347, #1320 context; `rfc`-labelled issue list; PR #822 file list (no `rfcs/` files → no numbering race).
+- Worktree source verification at baseline (every load-bearing RFC claim spot-checked): closed 9-field `CreateServiceClientOptions` with reserved no-op `port`/`timeout`; context-less `ServiceClientMethod`; `inferRPCMethodFromContractRouter` at `http-client-link.ts:17/:82` (GET live today); GET-only dedupe filter; `retry: 0` default; trace injection at `:90-91`; `baseContract: ReturnType` erasure at `contract-primitives.ts:81`; six-code `commonErrorMap`; current erased `BaseContractErrors = MergedErrorMap, ErrorMap>`; `'~orpc'` accessor in `ContractProcedureLike`; `withTransaction()` root-client assertion (`database/mod.ts:128`); 5-value `IsolationLevel` incl. MSSQL `Snapshot`; `WorkerIdempotencyPort` "exactly-once-effective" comment (`:30`); MySQL adapter `SNAPSHOT` (`:480`); queue adapter runtime `ensureSchema`/`CREATE TABLE IF NOT EXISTS` (`:296-348`); telemetry `netscript.correlation.id` (`messaging.ts:15`) and `netscript.idempotency.key` (`saga.ts:12`); Desktop `createORPCClient` + MessagePort `RPCLink` (`desktop/application/desktop-rpc-client.ts:1-32`); `packages/service/deno.json` has **no** `@netscript/database` dependency (new edge genuinely new); `plugin-contributions.ts` closed `'auth-backend'` doctor literal, no `sdkClients` group; `create-service-query-utils.ts:63` cast fast path; oRPC procedure context today is `context: {}` (`service/src/primitives/handlers.ts:126/:156`) with `principal` only in the Hono middleware bag; Prisma 7.8.0 in lock; zod 3.25.76 + 4.4.3 coexisting in lock; `deno.lock` oRPC family pinned 1.14.6; `rfcs/` on main contains only template+README (zero numbered RFCs).
+
+## 3. Severity-ranked findings table
+
+Prior evaluators' artifacts were treated as evidence only; every finding below was re-derived against current source/live state.
+
+| # | Sev | RFC | Finding | Evidence / disposition |
+| --- | --- | --- | --- | --- |
+| Q1 | **Low (advisory, board)** | B | Guide-level envelope derivation uses `context.principal.*`, `context.correlationId`, `context.traceHeaders`, `context.signal` inside the oRPC handler, but today's oRPC procedure context is `context: {}` (`handlers.ts:126/:156`); `principal` exists only as an untyped Hono bag entry — exactly the defect #1387 (TA-04) is filed to fix. RFC-B's issue decomposition never cross-references #1387/#1383. | Not a design blocker: the kit contract is actor-source-agnostic (caller supplies `CommandEnvelope.actor`; system actors explicit; absence never anonymous). **Repair:** amendment on #1363 (and backlink on #1387) naming the typed-principal shape as the consumer dependency for the authenticated envelope path, so Stage-8 generated handler examples compile against a real surface. |
+| Q2 | **Low (advisory, board)** | A | #1350's live body is exactly the `safe()`/error-map repair (verified) and does **not** own `NetScriptProcedureMeta` initialization. RFC-A Stage 1b metadata therefore has no owner until the Stage-0 decision (widen #1350 vs dependent child) is recorded. | Correctly staged by the RFC (Stage 0; FCP Q6), but it is a **hard downstream edge**: #1352 auth dogfood cannot ship without the metadata vocabulary. Must be recorded at/before numbering, not left to FCP drift. |
+| Q3 | **Info** | A | v2 gate list does not explicitly name the `@orpc/zod` **Zod-v4-only** requirement (verified in migration guide) while the repo lock carries both zod 3.25.76 and 4.4.3 and #1320 (single Zod) is blocked upstream. | Fold into the v2 epic scope row (schema/OpenAPI parity). RFC-A's gate list is a stated minimum, so this is an addition, not a defect. |
+| Q4 | **Info** | A | The `74 non-test / 91 total` oRPC file count is the worklog's recorded scan; my independent quick greps with different exclusion rules produced 39–56. Direction (broad cross-package footprint) confirmed; exact count is scan-definition-dependent. | No action; the number is motivation, not a gate. |
+| Q5 | **Info** | A+B | `CommandTelemetryStart.idempotency` is `'claimed' \| 'not_requested'` while result/attribute vocabulary adds `'replayed'/'missing'/'mismatch'/'busy'`. Coherent because the span starts before the claim resolves (algorithm step 3 precedes step 5) and final attributes come from `finish(result)` — but the asymmetry deserves one doc line at implementation. | Implementer note, not an RFC defect. |
+| Q6 | **Info (process)** | A+B | All CI check runs at both HEADs are `skipped` (path-filtered docs-only + `ci:skip-*` labels, ruled valid by cycle 2 — diff is RFC + run artifacts + one compile-only fixture, no package/lock/generator change). The **only** compile evidence is the recorded fixture gate; re-executed here (exit 0). | Acceptable at RFC bar; root orchestrator should know no CI lane compiled these branches. |
+| Q7 | **Info (process)** | A+B | Frontmatter `target-milestone` convention differs (A: 0.0.7 = implementation; B: 0.0.6 = ratification, footnoted). Both are factually consistent with the live board (verified: #1349–#1353 in 0.0.7; #1361/#1348 in 0.0.6; #1362–#1364/#1363 in 0.0.8; #1350 in 0.0.7). | Harmonize convention at numbering. |
+| Q8 | **Info (process)** | A+B | MASTER-PLAN fork F4 recorded "issue-hosted" as the default, but execution followed the canonical file-based process of `rfcs/README.md` (file + companion tracking issue). These are the first file RFCs (`rfcs/` on main has none); PR #822 adds no `rfcs/` file, so no numbering race. | Retire the F4 divergence note at numbering; maintainer chooses whether RFC-A or RFC-B receives 0001. |
+| Q9 | **Info (hygiene)** | A | PR #1390 body DoD checkbox "Formal cycle-2 verdict is recorded" remains unchecked although the APPROVED verdict comment (19:01:57Z) and artifact exist; body phase line still says `status:plan-eval` while the live label is `status:augment-review`. | Cosmetic; fix at FCP prep. |
+
+**No critical or major findings.** All 17 cycle-1 findings (F-A1–F-A10, F-B1–F-B7) were mechanically re-checked against the accepted content and are **confirmed resolved** — see §5.
+
+## 4. Individual verdicts
+
+### 4.1 RFC-A — Typed SDK client contributions (PR #1390): **PASS_ACCEPT**
+
+The RFC is decision-complete at the RFC bar:
+
+- **Seam law is closed.** Descriptor protocol (`family/major` closed at 1), id grammar/limits, context declaration ↔ TypeScript required/optional bijection, `headerKeys` as reserved-output subset, mandatory `responseCache` trichotomy, sequential prepare with deterministic first failure, duplicate rejection with named conflict markers, and order-independence (valid contributions commute) are all normative with runtime re-validation for JS/widened/plugin boundaries.
+- **Compatibility is proven, not asserted.** Every widened public generic carries an explicit default (normative table); the committed in-tree fixture compiles real `ContractLike`/`defineServices`/`ServiceQueryUtils`/key/desktop surfaces, pins the exact 3-tuple default server key, the 5-tuple partitioned key, direct-only omission, required-context call/query arguments, duplicate-context diagnostic, and the 16/17 budget — and I re-executed it to exit 0.
+- **The hard lifecycle cases are settled.** Prepare-once-per-epoch with byte-equivalent replay across unary retries; iterator-phase reconnect = new epoch with exactly one re-preparation (credential rotation fixture mandated); abort semantics; dedupe header-safety; desktop MessagePort rejection (type + runtime + generator); reserved framework keys excluded from the contribution projection; private ports located, unexported, and absence-gated via `deno doc` + packed-consumer negatives.
+- **Security model is explicit.** Redaction list, partition non-secret law, cleartext/loopback bearer rules, redirect/cookie/CORS limitations, input-as-borrowed-data duty, and the metadata-guides-but-does-not-enforce boundary.
+- **Upstream boundary is honest.** Zero-oRPC gate scoped to new RFC-A + generated declarations under a non-growing #1350/#1278 allowlist (doctrine's sanctioned oRPC-types exception verified); stable-v1 first adapter; v2 fenced into its own RFC/spike with a complete gate matrix.
+
+Residual decisions are the eight FCP questions, all policy-safe under my own adjudication (§7), with Q6 (metadata ownership) carrying the §3-Q2 recording obligation.
+
+### 4.2 RFC-B — Production command composition kit (PR #1389): **PASS_ACCEPT**
+
+The RFC is decision-complete at the RFC bar:
+
+- **The atomicity claim is exactly one store commit**, enforced by construction: `sideRecordAtomicity: 'same_commit'` is a construction invariant, all four side-record delegates derive from the transaction callback's `TTx`, `callbackAttempts: 'one'` forbids adapter replay, and the refusal boundary rejects every cross-store/network-in-transaction shape rather than weakening the promise.
+- **The previously-silent boundary decisions are now normative.** Per-provider receipt-claim algorithms (PG `ON CONFLICT DO NOTHING RETURNING` + transaction-local `lock_timeout` save/restore, `55P03`→busy, `40001` retryable; MySQL savepoint recovering **only** 1062 with `INSERT IGNORE` explicitly forbidden, session `innodb_lock_wait_timeout` restore-or-discard; MSSQL `UPDLOCK/HOLDLOCK` on the named generated unique index with `SET LOCK_TIMEOUT` restore; busy callback-terminal; every timeout path rolls back) — mechanically sound against provider semantics and poison-free by construction.
+- **Package ownership is acyclic and declared.** Relay runtime (decoded delivery/sinks/supervisor) in `@netscript/service/commands/relay`; raw rows/lease/token/release in `@netscript/database/commands`; the only new edge is `service → database` (verified absent today in `packages/service/deno.json`); database imports no service type.
+- **Reuse discipline is recorded.** Queue non-wrapping decision with real reasons (delete-on-ack/DLQ semantics, runtime DDL verified present, `TTx` join impossibility); pattern/test reuse only, with a named reconciliation prerequisite before any code sharing.
+- **Identity law is complete.** JCS/RFC-8785 request hash over the exact versioned semantic request; key hash separate; scope/fingerprint determinism with frozen-value conformance; execute-as-new for changed scope/renamed command stated honestly as the consequence plus migration obligation; definition-version change under a stable key = mismatch.
+- **Capability honesty is enforced.** `selectableIsolationLevels` vs `defaultIsolation` split; MySQL blocked on `SNAPSHOT` removal + allow-listing (defect verified live at `adapter.ts:480`, distinct from #1293); SQLite default-only shape deferred to FCP Q2 with no support claim; Deno KV and multi-store refused.
+- **Conformance is the strongest artifact in either RFC**: 20-item positive/negative matrix, 10 named fault seams, real-provider requirement (no type-only fakes), and the root-client negative control.
+
+Residual decisions are the four FCP questions, all policy-safe with recorded recommendations; plus the §3-Q1 advisory cross-reference.
+
+### 4.3 Cross-RFC verdict: **COMPOSE CLEANLY — PASS**
+
+- **No circular dependency.** Shared prerequisite #1350 (0.0.7) is one-way; RFC-A stages 1a/1b and RFC-B stage 0 both consume it. RFC-B's new `service → database` edge is acyclic. Neither RFC imports the other's surface.
+- **No conflicting error ownership.** RFC-A's `SdkClientContributionError` is local, pre-dispatch, never a contract error, never `.errors(...)`-mergeable; RFC-B's `commandErrorMap` is route-opt-in contract vocabulary. Both require #1350's literal-preserving `ContractBuilder` spelling; whichever lands first establishes it and the other reuses (obligation explicit in both texts; current erased spelling verified live, so #1350 is genuinely prerequisite).
+- **No conflicting context/telemetry/idempotency ownership.** RFC-A reserves trace headers to the client transport (final `traceparent` must describe the SDK client span — verified injection point at `http-client-link.ts:90`); RFC-B persists validated W3C context in durable rows and emits `command.*` vocabulary from `@netscript/telemetry/attributes` with stricter redaction than today's messaging/saga attributes (asymmetry acknowledged, cleanup owned as FCP Q3). Disjoint layers. RFC-A's permitted `idempotency-key` **header** and RFC-B's envelope `idempotencyKey` **input field** are different layers; the future HTTP recipe (#1364) must name the authoritative one for services accepting both — watch item, not a conflict.
+- **No duplicated primitives.** No existing JCS/canonicalization utility exists in `packages/` (verified), so RFC-B's codec is new, not reinvented; RFC-A's descriptor/reference types have no prior seam; plugin discovery routes through #1093 rather than a second switch system.
+- **One cosmetic inconsistency** (frontmatter milestone convention, §3-Q7) and **one shared spelling obligation** — both already named in the cross-RFC brief.
+
+## 5. Cycle-2 resolution audit (all 17 findings)
+
+Independent re-check against the accepted content SHAs; "anchor" = location in the accepted RFC text.
+
+| Finding | Cycle-2 claim | Adversarial re-verification | Result |
+| --- | --- | --- | --- |
+| F-A1 scoped zero-oRPC gate | resolved | Anchor present (scoped to new declarations + generated clients; non-growing allowlist; doctrine sanction verified); gate now passable in principle | **Confirmed** |
+| F-A2 server key algebra | resolved | Default exact 3-tuple preserved (fixture asserts it against live `createActionQueryKey`); 5-tuple partitioned shape; six-surface disposition table; cast fast-path restricted; upstream fixture dispositioned (verified cast at `create-service-query-utils.ts:63`) | **Confirmed** |
+| F-A3 compatibility defaults | resolved | Normative defaults table covers every widened public generic; fixture proves default assignability of `ServiceClient`/`ServiceQueryUtils` | **Confirmed** |
+| F-A4 stream reconnect credential | resolved | Epoch model normative; reconnect = fresh single preparation; mandatory A→B rotation fixture with preparation-count 2 and per-epoch byte equality; aborted stream starts no epoch | **Confirmed** |
+| F-A5 desktop bypass | resolved | Out of scope with rejection: excess-property + runtime `SDK_CONTRIBUTION_TRANSPORT_UNSUPPORTED`; targets browser/server only; generator failure; docs obligation; fixture `@ts-expect-error` against real `CreateDesktopServiceClientOptions` | **Confirmed** |
+| F-A6 private ports | resolved | Location `src/internal/client-contributions/` (named files, no barrel, absent from exports); doc-graph + packed-consumer negative gates incl. exact specifier rejections | **Confirmed** |
+| F-A7 context projection | resolved | Contributors see declared-context projection + `signal` only; seven framework keys reserved and rejected; forced-retry fixture drives `context.retry: 1` (verified `retry: 0` default live); private prepared-call channel specified incl. context-replacement preservation | **Confirmed** |
+| F-A8 v2 corrections | resolved | GET direction corrected (GET live today at `http-client-link.ts:82`; v2 rejects GET by default — verified in migration guide); `inferRPCMethodFromContractRouter` replacement + dedupe no-op gates present; OTel rename assigned to #1351 (verified `@orpc/opentelemetry@1.14.11` on v1 line and repo using `@orpc/otel`); lock-only family pinning stated | **Confirmed** |
+| F-A9 in-tree fixture | resolved | Fixture committed, models real surfaces; **re-executed: exit 0** | **Confirmed** |
+| F-A10 adopted facts | resolved | Per-attempt header resolution + header-safe dedupe cited as fact; raw-input sensitivity sentence present; Stage 1a/1b split explicit; v1-maintained evidence present (re-verified live today) | **Confirmed** |
+| F-B1 claim algorithms | resolved | Normative per-provider table with lock-timeout mechanics, busy-terminal semantics, rollback-everywhere; poison-free by construction; conformance item 10 adds provider-specific negative controls; primary citations appended | **Confirmed (exceeds bar)** |
+| F-B2 relay ownership | resolved | Ownership rows complete; decoded-vs-raw split; `service → database` declared as a **new** edge (verified absent); database imports no service type | **Confirmed** |
+| F-B3 queue reuse | resolved | Decision recorded with real reasons; runtime DDL verified live; reconciliation child proposed for owner filing; rejected-alternative entry present | **Confirmed** |
+| F-B4 scope instability | resolved | Execute-as-new law where identity is defined; determinism obligation on `scope()` mirroring `fingerprint()`; negative conformance items 5–6 (changed scope; renamed command; changed version = mismatch) | **Confirmed** |
+| F-B5 MySQL SNAPSHOT | resolved | Named in capability matrix + Stage-6 exit condition; defect verified live at `adapter.ts:480`; #1293 kept adjacent and distinct | **Confirmed** |
+| F-B6 transaction client type | resolved | Generator-owned `CommandTransactionClient = Omit` with engine-module re-export; Prisma 7.8 probe consistent with lock (7.8.0); `withTransaction` root-client repair retained (assertion verified live) | **Confirmed** |
+| F-B7 batch (a–h) | resolved | All eight verified: A4 archetypes; literal-preserving spelling pinned to #1350 (current erased spelling verified live); `claimed` vocabulary unified; ratification-milestone footnote; `WorkerIdempotencyPort` wording fix obligated (comment verified live at `:30`); SQLite selectable/default split in FCP Q2; `db command-store add` sub-noun with `db init` distinction + negative CLI test; telemetry asymmetry acknowledged with cleanup question | **Confirmed** |
+
+**Conclusion:** cycle 2 genuinely resolved every finding, including the negative gates (desktop rejection, packed-consumer negatives, forbidden telemetry fields, root-client negative control, provider negative controls) and public/package ownership (zero-oRPC scoping; service/database relay split; no new package). The PASS verdicts are substantiated.
+
+## 6. oRPC v2 migrate-first decision (RFC-A question 4)
+
+### 6.1 Current upstream state (verified live today, 2026-08-08)
+
+- `@orpc/client` / `@orpc/server` dist-tags: **`latest` = 1.15.0 (stable), `beta` = 2.0.0-beta.26**. v1.15.0 published 2026-08-08T13:52Z — *after* beta.26 (11:21Z). Seven v1 patch releases since 2026-07-25. **Stable v1 is actively maintained; v2 is pre-release and churning (beta.21→beta.26 in 13 days).**
+- Official migration guide confirms: RPC wire protocol changed (**v1 client cannot talk to v2 server** — coordinated deploy required); automatic middleware deduplication removed (double-execution hazard); `.$meta` replaced by `defineMeta` plugins; GET rejected by default (`allowMethods` defaults POST/PUT/PATCH/DELETE; `SimpleCsrfProtectionHandlerPlugin` checks `Sec-Fetch-Mode`); `status` removed from error definitions → handler `errorStatusMap`; `safe()` result reshaped (typed third element + `isSuccess` fourth); `isDefinedError` → `isInferableError`; serializer becomes an instance with string-keyed handlers; `RPCLink` url split into `origin`+`url`; interceptor renames; `@orpc/otel` → `@orpc/opentelemetry`; `RequestHeadersPlugin` → `RequestHeadersHandlerPlugin` (incoming server handler plugin); `@orpc/zod` requires **Zod v4**; TanStack consolidated into one package with `scoped` defaults.
+
+### 6.2 Decision matrix
+
+| Option | Technical merit | Risk | Verdict |
+| --- | --- | --- | --- |
+| **A. Migrate to v2 first, then build the seam** | v2 brings typed-error/status redesign, meta plugins, CSRF plugin, serializer instance — none of which provides outbound header ownership, duplicate/conflict law, cache-partition declaration, redaction, or plugin discovery. `RequestHeadersHandlerPlugin` is incoming-server-only and absent for direct calls. v2 TanStack still excludes client context from keys, so RFC-A's partition/direct-only law survives unchanged. | Beta dependency for a framework publish surface; wire incompatibility forces coordinated client/server rollout across a generated-app ecosystem before the seam even exists; 74-file blast radius; middleware double-execution audit; GET/dedupe law rewrite; Zod-v4 entanglement while #1320 is blocked. | **REJECT** |
+| **B. RFC-A on stable v1 now; v1.15.0 family move as a separate lock-only decision; v2 as its own RFC/spike later** | Seam is upstream-major-neutral by construction (three NetScript ports; zero upstream identities in new declarations); stable v1 adapter proven against locked 1.14.6 lifecycle facts; v1.15.0 available today with a normative lock-only whole-family gate; v2 adapter later must pass the same conformance suite. | Minimal: v1 line maintenance (verified active); v2 work deferred, not avoided. | **RECOMMEND** |
+| **C. Run the v2 spike in parallel now** | Read-only spike could de-risk the future RFC. | Splits attention during 0.0.7/0.0.8 delivery; beta target moves under the spike; no implementation value until RFC-A ports land (ports are the migration boundary). | **Only after RFC-A acceptance, as a filed tracking issue, unscheduled** |
+
+### 6.3 Real v2 benefits vs. features that do not solve the seam
+
+- **Real (transport/server-owned, belong to the future v2 RFC):** wire/protocol modernization; `errorStatusMap` + typed-error redesign (interacts with #1350's `safe()` shape — note v2 changes `safe()` arity again); middleware model; GET/CSRF policy plugins; serializer instance (Fresh/Desktop parity work); unified websocket; hibernation; consolidated TanStack package.
+- **Do NOT solve the outbound typed contribution seam:** request-header handler plugin (incoming only; no ownership/conflict/async-resolution/redaction/cache-partition semantics); metadata plugins (server procedure metadata, not client preparation); retry/dedupe plugin renames (transport policy the SDK already owns); TanStack client-context exclusion (preserves RFC-A's law rather than replacing it).
+
+### 6.4 Recommended sequencing and prerequisites
+
+1. Accept RFC-A; implement stages 0–5 on stable v1.
+2. #1351 decides the **lock-only exact-family move to v1.15.0** (timely: it is today's `latest`), with `deno ci --frozen` + no-mixed-family evidence; no manifest pin churn; no v2.
+3. After RFC-A acceptance, **file one new v2 migration RFC tracking issue** (`rfc` label, ratification-adjacent milestone, implementation unscheduled). Prerequisites: RFC-A stages 2–3 landed (the three ports are the migration boundary); owner beta-risk decision (wait for `latest` dist-tag unless explicitly accepted); #1351 complete. Gate list: RFC-A's v2 gate matrix **plus** (from this pass) the Zod-v4 requirement and the v2 `safe()`/`isInferableError` shape change vs the landed #1350 spelling. The v2 adapter must pass RFC-A's entire contribution conformance suite before its migration RFC may enter FCP (already normative).
+
+## 7. FCP safety and owner decisions required before numbering/merge
+
+Adjudicated independently — all remaining FCP questions are policy-safe (both outcomes typed/implemented in the RFC text; no rework risk):
+
+**RFC-A (8 questions):** Q1 budget ≥16 (fixture-proven); Q2 env-reading factory (scoped either way); Q3 #451 scheduling (independent); Q4 naming refinements (semantics fixed); Q5 wrapper-vs-memo (both fixture-constrained); Q6 metadata ownership (vocabulary normative either way — but see recording obligation below); Q7 incoming companion default (direct-call absence mandated either way); Q8 v1.15.0 before/after seam (both supported by the lock-only gate). Q9–Q11 belong to the separate v2 RFC.
+
+**RFC-B (4 questions):** Q1 idempotency default (recommendation: required, explicit opt-out only); Q2 SQLite default-only shape (recommendation: accept shape, release later, no support claim); Q3 correlation telemetry (recommendation: durable-row/log-only default + separate vocabulary cleanup); Q4 retention defaults (recommendation: explicit deployment values until operational evidence).
+
+**Decisions that MUST be recorded by the owner before/at numbering:**
+
+1. **RFC-A Stage 1b metadata ownership** — widen #1350 or file a dependent child (hard prerequisite for #1352; verified #1350 does not own it today).
+2. **RFC-A v1.15.0 sequencing** — before or after the minimal seam (either valid; decide to unblock #1351).
+3. **RFC-B Q1 idempotency default** (shapes generated scaffolds/telemetry defaults).
+4. **RFC-B Q2 SQLite disposition** (decides whether the optional child files at all).
+5. **Cross-RFC frontmatter `target-milestone` convention** — pick one at numbering.
+6. **RFC numbering order** — 0001/0002 assignment between RFC-A and RFC-B (maintainer choice; no race with #822).
+
+## 8. Board consequence audit (audit only — nothing created)
+
+### 8.1 Amendments to existing issues (owner-executed, text sources identified)
+
+| Issue | Amendment | Source |
+| --- | --- | --- |
+| **#1349** (0.0.7) | Add RFC-A Stage-2 scope: descriptor/composer, defaulted context-generic client/query surfaces, private `src/internal/client-contributions/` ports + `deno doc`/packed absence gates, prepared-header channel statement, server/TanStack key algebra surfaces, reconnect preparation law, desktop rejection, cache handling. | RFC-A stage table + CROSS-RFC §2 |
+| **#1350** (0.0.7) | Stage-0 reconciliation: record the literal-preserving four-generic `ContractBuilder` spelling shared by both RFCs; record the Stage-1b metadata decision (widen vs child). Verified current body is `safe()`-only. | RFC-A stages 0/1a/1b; RFC-B stage 0 |
+| **#1351** (0.0.7) | Add: `@orpc/opentelemetry` rename decision (v1-line 1.14.11 available; repo currently on `@orpc/otel`); lock-only exact-family pin policy for the v1.15.0 move; GET-dedupe no-op trap as an acceptance row; explicit "no v2 migration in this issue". | F-A8b/c/d; RFC-A transport section |
+| **#1352 / #1353** (0.0.7) | #1352: auth-core bearer factory, access-metadata behavior, redaction, partition/direct-only, manifest reference, scaffold choice (blocked-by metadata decision). #1353: re-scoped to final-trace-ownership proof (transport retains sole final injection; contributor trace-header ownership rejected). | RFC-A stages 4–5 |
+| **#1362** (0.0.8) | Command-handler generation depends on the #1362 layered service shape; generators refuse missing layering. | RFC-B CLI section |
+| **#1363** (0.0.8) | Inherit RFC-B's 10-child table (labels/milestones as listed in the RFC); relay-ownership + claim-algorithm references; `CommandTransactionClient` generator deliverable; **new from this pass:** name #1387's typed-principal shape as the consumer dependency for authenticated envelope derivation (§3-Q1). | RFC-B decomposition + this eval |
+| **#1364** (0.0.8) | Consume the stable outbox ID in the webhook recipe; name which idempotency carrier is authoritative (header vs envelope) for services accepting both. | RFC-B sink rules; CROSS-RFC §1 |
+| **#1387** (0.0.8) | Backlink: command-kit envelope derivation consumes the typed-principal/context surface this issue creates. | This eval (§3-Q1) |
+| **#1293** | None — stays adjacent; the MySQL `SNAPSHOT`/allow-list fix is the distinct Stage-6 child. | F-B5 |
+
+### 8.2 Genuinely new issues required (file by owner after acceptance)
+
+| New issue | Type | Milestone/labels | Dedup check |
+| --- | --- | --- | --- |
+| **oRPC v2 migration RFC tracking issue** (the only new epic-scale object) | `rfc` tracking issue for a future RFC/spike | Ratification-adjacent (`0.0.6`-era or Backlog until scheduled); `rfc`, `type:docs`/`type:feat` at RFC filing, `priority:p2` until owner beta decision | **Not a duplicate**: full #1348–#1388 title sweep verified; #1351 is stable-v1 transport consolidation and must say "no v2 migration" |
+| RFC-A Stage 6 locale non-auth proof child | feat child of the #1348 family | 0.0.7; `type:feat`, `area:sdk`, `priority:p1` | Distinct from #1352 (auth) and #1353 (trace proof) |
+| RFC-A Stage 1b metadata child — **only if** Stage 0 chooses child-over-widen | feat/fix child | 0.0.7 | Would not duplicate #1350 once the decision is recorded |
+| Queue runtime-DDL reconciliation child (RFC-B prerequisite for future code sharing) | fix | 0.0.8; `type:fix`, `area:database`, `priority:p2` | No live issue covers queue `ensureSchema` externalization (verified) |
+| SQLite command-store child — **only if** FCP Q2 accepts | feat | FCP-decided | Distinct from #1293/#1363 children |
+
+### 8.3 Dependencies/milestones verification (live board matches RFC sequencing)
+
+#1348/#1361 in 0.0.6 (ratification); #1349–#1353 in 0.0.7 (RFC-A implementation); #1350 in 0.0.7 (shared prerequisite, consumed by RFC-B stage 0); #1362–#1364 + #1363 umbrella in 0.0.8 (RFC-B implementation); #451 Backlog; #1093/#1278/#1293 in 0.0.6. All 41 filed issues open, correctly milestoned, `status:triage`. No duplicate of either RFC exists among #1348–#1388 or other live issues (also checked #572/#822 draft PRs — neither overlaps; #822 files no `rfcs/` content).
+
+## 9. Process readiness audit
+
+| Check | State | Verdict |
+| --- | --- | --- |
+| Draft state | Both PRs draft, MERGEABLE, base `main` | Correct — RFCs stay draft until acceptance |
+| Labels | Both: `rfc`, `type:docs` (+ `type:test` on #1390 for the compile-only fixture — honest), correct `area:`/`priority:p1`, `ci:skip-e2e`/`ci:skip-scaffold` (valid: no runtime/generator/export/lock change in either diff), exactly one `status:` (`augment-review` — correct phase for this advisory pass) | Compliant |
+| Check selection | All CI lanes skipped at both HEADs (path-filtered docs-only); fixture compile gate recorded and re-executed here (exit 0) | Valid, with §3-Q6 caveat |
+| Reviewer separation | Generator (Codex) ≠ PLAN-EVAL cycles 1–2 (Claude Fable 5, owner-designated, same session both cycles, separate from generator) ≠ this adversarial pass (Qwen 3.8 Max, fresh, no subagents) | Separation intact |
+| RFC numbering | `rfcs/README.md`: maintainer assigns next free integer at acceptance; both files correctly `0000`; first-ever file RFCs; no race (#822 adds no file) | Ready for maintainer |
+| FCP rule | Maintainer announces ~7-day FCP with disposition; **not yet announced on either PR** | **Mandatory wait** |
+| Unresolved threads | GraphQL: 0 review threads on both PRs; review-thread gate PASS recorded | Clean |
+| Closing keywords | PR bodies reference #1348/#1361 **without** closing keywords (correct: tracking issues stay open for implementation); no epic-closing keywords anywhere | Correct |
+| PR body hygiene | #1390 DoD checkbox + phase line stale vs live verdict/label (§3-Q9) | Cosmetic fix at FCP prep |
+
+**Merge readiness:** technically both PRs could merge green today, but **must not**: `rfcs/README.md` requires the formal FCP interval and maintainer acceptance before numbering/renaming/merge, and both PR bodies themselves instruct "keep draft; do not number or merge until maintainer acceptance and remaining owner-directed review passes complete". This evaluation's PASS_ACCEPT is the final technical gate; the FCP interval is a **process** wait, not a technical deficiency.
+
+## 10. Root-orchestrator action list
+
+1. **Record this verdict** on both PRs (structured comment citing this artifact); move `status:augment-review` → the FCP-entry state per lifecycle; fix #1390's stale DoD checkbox/phase line.
+2. **Owner records the six decisions** of §7 (metadata ownership, v1.15.0 sequencing, RFC-B Q1–Q4, milestone-frontmatter convention, numbering order).
+3. **Announce ~7-day FCP** on #1390 and #1389 with disposition **accept**, attaching the FCP question sets and recommendations.
+4. **At FCP close:** mark PRs ready-for-review, assign RFC numbers, rename to `rfcs/NNNN-*.md`, fill frontmatter (harmonized milestone convention), `status:ready-merge`, merge. Tracking issues #1348/#1361 stay open, milestoned, carrying the accepted RFC link.
+5. **Post board amendments** of §8.1 (marker-comment convention per Stage-H practice).
+6. **File new issues** of §8.2 in order: v2 migration RFC tracking issue (after RFC-A merge), locale child, metadata child (if chosen), queue-reconciliation child (owner decision), SQLite child (if FCP accepts).
+7. **Dispatch implementation only after** amendments land: RFC-A stages 0–5 against #1349–#1353 (0.0.7) with #1350 first; RFC-B stages 0–9 against #1363 children (0.0.8).
+8. **Hold the line:** no v2 beta in any implementation PR; #1351 owns the only oRPC movement (lock-only v1.15.0 decision); the v2 RFC must clear RFC-A's conformance suite on its adapter before its own FCP.
+
+---
+
+## Verdict summary
+
+- **RFC-A (#1390 @ `78a7cecd1`): PASS_ACCEPT** — technically ready for FCP/acceptance; mandatory process wait (FCP interval) applies; owner decisions §7 items 1–2 must be recorded at numbering.
+- **RFC-B (#1389 @ `c98c08ada`): PASS_ACCEPT** — technically ready for FCP/acceptance; mandatory process wait (FCP interval) applies; owner decisions §7 items 3–4 must be recorded.
+- **Cross-RFC: PASS** — composes cleanly; no circular dependencies, no conflicting error/context/telemetry/idempotency ownership, no duplicated primitives; one shared #1350 spelling obligation and one watch-item intersection, both already owned.
+- **oRPC v2 migrate-first: REJECTED** — implement RFC-A on stable v1 now; separate lock-only v1.15.0 decision in #1351; one new v2 migration RFC tracking issue filed after RFC-A acceptance, implementation unscheduled, gated by RFC-A's conformance suite.
+- **Cycle-2 findings:** all 17 (F-A1–F-A10, F-B1–F-B7) independently confirmed resolved.
+- **New findings:** none above Low; two Low board advisories (#1363↔#1387 principal dependency; #1350 metadata ownership recording) and seven Info items, all with exact repairs in §3/§8.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/SYNTHESIS.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/SYNTHESIS.md
new file mode 100644
index 0000000000..43e07d4213
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/SYNTHESIS.md
@@ -0,0 +1,177 @@
+# Stage-C Synthesis — Fable 5 long-range remediation roadmap
+
+Supervisor synthesis of the full Stage-B corpus (19 artifacts: `research/preplan-package.md`,
+4 wave files, 3 board files, 8 repo-audit files, 3 external files). Every claim below traces to a
+corpus file; corpus files carry the primary citations. Written by the Tier-A supervisor
+(Fable 5 · high) after reviewing all corpus artifacts. **Draft — no board mutation.**
+
+## 1. Answer to the core question
+
+**What prevents NetScript from being a credible, differentiated production-grade meta-framework
+is not missing capability — it is that the product surface (generated app + docs + typed seams +
+runtime proof) contradicts the capability.** The corpus proves this four ways:
+
+1. **The APIs exist and reach product quality when used.** eis-chat reaches the bar using only
+ `packages/fresh` APIs on main — with zero `any` — by inventing six app-space conventions the
+ scaffold does not generate (`research/external/eis-chat.md` §1–3). The framework's own example
+ route uses `defineRouteContract`/`withResource`/forms (`research/repo-audit/mcp-cli.md` #9).
+2. **The generated path contradicts the idiomatic path.** `ui:add page --island` — the verb the
+ generated agent conventions tell agents to run — emits a `useSignal(0)` counter and an empty
+ `queryLoaders = {}`, and writes to the wrong tree (`resolveProjectRoot` returns the workspace
+ root while the app lives at `apps//`) (`repo-audit/mcp-cli.md` #7, #11). The default
+ scaffold demonstrates none of the page-builder surface (`repo-audit/web-layer.md` §canonical).
+3. **The typed seam is sealed exactly where products must extend it.** `CreateServiceClientOptions`
+ is a closed 9-field record; no headers/interceptors/plugins/fetch/link/context seam;
+ `createHttpClientLink` unexported; so the shipped auth plugin cannot ride the typed client and
+ the scaffold leaves `/api` public — a framework test codifies it (`repo-audit/services-sdk.md`
+ S-findings; `repo-audit/auth.md`). The oRPC pin is **not** the blocker: 1.14.6 vs 1.14.15
+ public exports are identical; every gap is NetScript's own wrapper erasure
+ (`research/external/orpc.md`).
+4. **Runtime claims outrun runtime truth.** The shipped sample job discards the saga publish
+ receipt (mirrored verbatim in docs); `STREAMS_DATA_DIR` is referenced by nothing so "durable"
+ streams are always in-memory; background children have zero health checks; 5 of 6 saga span
+ factories have zero callers so compensation is invisible in traces
+ (`repo-audit/runtime-plugins.md` #4, #7, #8; `repo-audit/observability-aspire.md` GAP-1).
+
+Six waves of measured agent runs corroborate the mechanism: capability present is not capability
+activated (`#1090` thesis); docs/MCP discovery is structurally weak (0 MCP calls across six
+consecutive measured runs, `github-board-open.md` §6.3/#1197); and the two GO-grade Wave-6 runs
+still hand-rolled everything the missing generators should have emitted
+(`research/wave-6-runs.md`, `research/preplan-package.md`).
+
+**Direction (confirmed from the pre-plan, now evidence-hardened): generation over prose, types
+over convention-only guidance, composable seams over one-off escape hatches — plus a fourth leg
+the pre-plan under-weighted: runtime truth over green wrappers** (health/receipts/traces that fail
+when the seam is removed).
+
+## 2. Gap taxonomy (charter classes → evidence)
+
+| Class | Signature findings (corpus refs) |
+| --- | --- |
+| Docs/discovery failure | 4 P0 docs breaks on one seam — three names for one client module, two query dialects both taught as canonical, `--with-client` cited once site-wide; `docs:accuracy` is a needle-checker; MCP default corpus is 1–2 documents and `agent init` emits `.mcp.json` without `--docs-root` (`repo-audit/docs-quickstart.md`, `repo-audit/mcp-cli.md` #2–3) |
+| Scaffold/generation failure | counter-stub `ui:add page`; mis-rooted `resolveProjectRoot`; no second-service client/query generator; hardcoded `dashboard`/`users` names; `/design` gallery 50-item snapshot vs 66-item live registry; quality-runner `SOURCE_ROOTS` decoupled from workspace members (`repo-audit/mcp-cli.md`, `repo-audit/scaffold-doctrine.md` D1–D4) |
+| API/type-system seam | closed SDK client record; `PluginContributions` has no client/SDK group and a closed `doctorChecks` literal; `safe` drops `TError` (`isDefinedError` → `never`, executed-check-proven); `ServiceRouter = Record`; `$meta()` used zero times (`repo-audit/services-sdk.md`, `research/external/orpc.md` G1–G11) |
+| Runtime correctness | discarded saga publish receipts + `127.0.0.1:8092` fallback; producer drops writes forever (#1326, open); always-in-memory streams; `WORKER_CONCURRENCY` vs `WORKERS_CONCURRENCY`; hardcoded pre-randomization ports in sagas/triggers/streams stubs (`repo-audit/runtime-plugins.md`) |
+| Plugin-composition failure | first-party plugin services structurally unguardable (`createPluginService` has no auth option); discovery hardcodes official factory callees (#1093); Aspire `composeAppHost` seam is dead code (`repo-audit/auth.md`, `repo-audit/runtime-plugins.md` #5) |
+| Harness/evaluation failure | E2E probes API health only, never background children or streams — why #1325 shipped green; no E2E asserts any `saga.*` span; `docs:accuracy`/`check-exports-drift` cover fractions (`repo-audit/runtime-plugins.md` #7, `repo-audit/observability-aspire.md` gap 3) |
+| Product expectation outside framework scope | tenancy in a demo (#884/#885 own the framework leg); webhook dispatcher (no promised primitive — decide recipe vs template); mobile-action loss (→ #1333 acceptance) (`research/preplan-package.md` §verify-first) |
+
+## 3. Adjudications of supervisor-delegated decisions
+
+1. **Trigger Redis glue / `ServiceReferences` injection (Wave-6 R2 "D-class" vs R3 "docs").**
+ Adjudicated **framework generation defects**. The triggers glue omission is proven at source
+ (`runtime.stub.ts` emits no `@netscript/kv/redis` import — `repo-audit/runtime-plugins.md` #2)
+ and owned by **#1325**. `ServiceReferences`-parsed-but-never-injected has **no board owner**
+ found by the board sweep → new issue draft, flagged verify-on-current-canary first.
+2. **#1276 vs #1278 (type-soundness duplicates).** Keep **#1278** (milestoned 0.0.6, inventory
+ A–D + guard-rail shape) as the epic-of-record; propose folding #1276's unique measured numbers
+ (56 `as unknown as`, 8 lint-ignores, 7 `quality:scan` allowances, tranche T1–T6 structure)
+ into #1278 via amendment, then owner closes #1276 as superseded. Rationale: milestone
+ assignment + richer acceptance shape live on #1278; #1276's tranches survive as #1278 phases.
+3. **#1275 vs #1279 (migration-chapter duplicates).** Keep **#1279**; fold/close #1275. Propose
+ moving #1279 out of 0.0.6 (migration docs are post-remediation marketing surface, not
+ remediation) to the late train.
+4. **#1245.** RESCOPE, do not re-implement: ~75% landed via merged #1265. Remnant =
+ `getIslandQueryClient()` `@throws`-vs-body mismatch, regression tests, and a consumer
+ migration note so eis-chat-class apps delete their six copied casts
+ (`repo-audit/web-layer.md`; `external/eis-chat.md` #7).
+5. **Webhook dispatch.** Not a framework defect. Disposition: first-party **recipe + worker
+ template** (docs + generation), not a new plugin; p2 draft under the service/command pillar.
+6. **Theme-island CORS, saga OOM, plugin-doctor layout.** Remain verify-first rows (repro on the
+ current canary before filing); plugin-doctor routes through #1343's installed-consumer smoke.
+7. **Divergent agent claims.** Where two corpus agents disagree, the domain agent with source
+ citations wins (e.g. MCP tool count = 21 per `tool-types.ts:4-26`, over the external agent's
+ abstention; wave-3 effort = Sol high per 3-of-4 sources).
+
+## 4. Deep-dive topics (Stage-D design packs)
+
+- **T1 — Typed extension architecture (RFC-A + oRPC re-exposure).** `SdkClientContribution`
+ contract; reopen the erased oRPC seams (headers/interceptors/plugins/link/context/typed
+ errors/`$meta` policy metadata); auth as first dogfood consumer + one non-auth contribution;
+ transport-policy consolidation ahead of oRPC v2. Depends on nothing; unblocks T2/T5/auth.
+- **T2 — Canonical vertical slice + generation.** Resource/route-slice generator; contract-derived
+ client/query/invalidation generator; fix `resolveProjectRoot`; `ui:add page` triad made real;
+ #1333 acceptance expansion; route-local groups; `/design` registry sync gate; dynamic app
+ naming. Consumes T1's client contract.
+- **T3 — Service architecture + production command slice.** #1335 service-layout child
+ (collapsible `domain/application/ports/adapters`); command-composition kit RFC
+ (transaction/idempotency/audit/outbox/optimistic-concurrency); `service add-handler` placement;
+ webhook-delivery recipe/template.
+- **T4 — Runtime truth.** Saga publish receipts + endpoint discovery (new p0); plugin child
+ liveness/health contract (distinct from blocked #1280); stream durability semantics
+ (`STREAMS_DATA_DIR`); saga compensation telemetry call-sites; env-var name mismatches;
+ hardcoded-port stubs (feeds #979); E2E gates that probe children and assert `saga.*` spans.
+- **T5 — Docs & agent discovery.** Tier-1 docs rewrite to ONE dialect of the canonical seam;
+ compile-the-docs gate replacing the needle checker; MCP corpus wiring (`--docs-root` in emitted
+ `.mcp.json`, #1260/#1201/#1102 chain intact); README/Quickstart truth; package reference pages.
+- **T6 — Type soundness + board hygiene.** #1278-of-record amendment + fold plan; #1245 rescope;
+ #1249; remaining-cast burn-down; `quality:scan` extension to exported types + docs snippets;
+ board hygiene amendment pack (stale epic checkboxes #301/#1126/#1335, labels.yml parity,
+ 0.0.2 stragglers, unmilestoned #979/#980/#1000).
+- **T7 — Wave-7 harness + measured adoption.** Thin: capability-map rows proved/simulated/absent/
+ rejected; causal-trace rules; generated-slice-command usage or recorded rejection; measurement
+ chain #1102/#1201 (build) → #1197 (re-measure) → #1090 (observe) untouched as owners.
+- **T8 — Milestone train + program sequencing.** Rename-shift insertion per house pattern;
+ epic-overlap normalization (#823⊂#327, #400↔#922 re-baseline, #892 vs #327/#830); every issue
+ retained and moved with written reasons.
+
+## 5. Milestone-train direction (to lock at Stage E)
+
+Constraints from the corpus: 0.0.5 is mid-canary with 21 open issues incl. 4 p0 + #1338
+(`status:impl`); 0.0.7 is entirely #922's critical path (9 p0s); 0.0.8/0.0.13 are the two big
+forward buckets; the house shift pattern is rename-in-place highest→lowest then create the freed
+title; canary-first release doctrine per `netscript-release`.
+
+Working proposal (Stage E finalizes exact renames + per-issue moves):
+
+1. **0.0.5 closes as re-scoped** — the undispatched W2–W5 remainder (streams pair #1326+#1329,
+ #1333, #1208 phase 1, plus its existing tail) finishes the milestone; no new scope enters.
+2. **0.0.6 stays the verification + docs/MCP + soundness cut** (#1343, #1210, #1260, #1201,
+ #1278 amended, #1093), gaining only the docs P0-dialect fixes and the two RFC ratifications
+ (RFC-A SDK composition; RFC command-kit) as *RFC tracking issues* — implementation stays out.
+3. **Insert two remediation milestones** after 0.0.6 by the rename-shift pattern: new **0.0.7 =
+ "Typed seams + generation"** (RFC-A implementation, client/query generator, route-slice
+ generator, `resolveProjectRoot`, #1333 completion follow-ups) and new **0.0.8 = "Runtime truth
+ + service slice"** (saga receipts, child liveness, stream durability, service layout + command
+ kit). Current 0.0.7–0.0.13 shift to 0.0.9–0.0.15 wholesale, preserving every issue and
+ internal ordering (#922's train moves intact; its stale beta.13/15/17 body labels get an
+ amendment, not a rewrite).
+4. **Wave-7 smoke** gates the exit of new-0.0.7/0.0.8 (a measured unfamiliar-agent run must show
+ the generated path changes behavior before the train advances past remediation).
+
+Open fork for the owner (carried to plan lock): whether #922 Wave-0 proofs (#923–#927) should
+precede RFC-A implementation (contract-coherence argument: both define plugin contribution axes)
+or run after new-0.0.8. Default proposal: RFC-A ratifies in 0.0.6 with #928's envelope reviewed
+against it; #922 implementation stays at its shifted position (new 0.0.9).
+
+## 6. New-issue surface (drafting input for Stage D)
+
+Every candidate below was checked against the dedup checklist (`github-board-open.md` §7) and has
+**no existing owner**; drafts must carry `## Boundaries` naming the adjacent owners. Grouped by
+pack: T1: RFC-A tracking issue; oRPC seam re-exposure; typed-error repair (`safe`/`isDefinedError`,
+`baseContract` widening); transport-policy consolidation (pre-oRPC-v2). T2: route-slice generator;
+client/query/invalidation generator; `resolveProjectRoot` fix; `ui:add page` triad; `/design`
+registry sync; `crudExample` alias bug; canonical island `initialDataUpdatedAt` wiring. T3:
+service-layout child; command-kit RFC; webhook recipe. T4: saga receipt p0; plugin child liveness;
+stream durability; saga-span call-sites; env-name mismatches; stub port hardcodes;
+`ServiceReferences` injection (verify-first); E2E child/span gates. T5: Tier-1 docs rewrite;
+compile-the-docs gate; MCP `--docs-root` wiring; `execute_command` version pin fix; package
+reference pages. T6: `quality:scan` extension; fresh-ui inclusion in root check/lint; doctrine
+verdict refresh (D6); `arch:check:repo` accepted-red closure plan (D8); board-hygiene amendments.
+T7: Wave-7 rubric/harness issue(s). Auth (T1-adjacent, checked against #871's 16 children which
+own *enterprise* scope, not these defects): plugin-service auth seam; signout-without-session
+defect; oRPC Set-Cookie discard; scaffold `/api` protection default; principal typing.
+
+## 7. Sharpest risks
+
+1. **Duplicate filing** — 9 prose-only umbrellas + the #1208-phase-2 promised-but-nonexistent
+ issue are the top traps; every draft carries `## Boundaries` + dedup row.
+2. **Re-implementing landed work** — #1245 (75% landed), #1328/#1184 closed; drafts must cite
+ current source, not wave-era observations.
+3. **Board-shift blast radius** — renaming 7 milestones touches ~150 issues' display; mitigated
+ by the house rename pattern (no per-issue mutation) + a written move ledger.
+4. **RFC-A over-design** — the seam must stay host-app-usable without plugins; oRPC already
+ provides the machinery; the RFC should mostly *unhide* it (`external/orpc.md` key negative
+ result).
+5. **0.0.5 scope creep** — remediation lands after 0.0.5 closes; only already-scoped W2–W5 work
+ finishes there.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/WAVE7-AND-AGENT-ADOPTION.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/WAVE7-AND-AGENT-ADOPTION.md
new file mode 100644
index 0000000000..464c65acd1
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/WAVE7-AND-AGENT-ADOPTION.md
@@ -0,0 +1,80 @@
+# Wave 7 & agent adoption — DRAFT (no GitHub mutation; owner ratification pending)
+
+Proposed Wave-7 harness changes and the measured smoke matrix. Deliberately **thin**: Wave 7
+proves docs/MCP/generation adoption; it does not script product choices. Sources:
+`research/wave-5-6-plans.md` (brief lineage, rubric, measurement method),
+`research/wave-6-runs.md` (R1–R3 + recurrence table), `research/preplan-package.md` (§Wave-7
+rules, owner constraints), `research/github-board-open.md` §6.3 (the measurement chain).
+
+## 1. What Wave 7 exists to falsify
+
+**Hypothesis under test:** after the 0.0.7/0.0.8 remediation cuts, the generated path (resource
+slice, client/query module, protected-by-default auth, durable-runtime truth) changes unfamiliar-
+agent behavior — adoption becomes the path of least resistance instead of a compliance ask.
+
+Wave 6's evidence base: capability present ≠ activated (six consecutive measured runs with zero
+MCP/doctor/otel usage; `#1197`); R3's only-GO run is confounded (supervisor-enforced init gate,
+different model, different canary — `research/wave-6-runs.md` #7). Wave 7 must remove those
+confounds, not re-run Wave 6 harder.
+
+## 2. What carries over unchanged (proven Wave-5/6 machinery)
+
+- **Never name a capability** in the brief; pressure-not-feature forcing functions
+ (`projects/README.md:25-28` rule; the `withForm`-derived-unprompted validation).
+- Project catalog with version gate (Quickstart canary pin — the Wave-6 pilot's silent stable
+ install is the cautionary case), distinctive-skin bar, "one entity is not a product".
+- Taxonomised executed-command census (SETUP/LOOP/DIAG/REPO; parse tool calls, not transcript
+ greps; exclude generated code; track the nested init repo).
+- Capability-map rows end **proved / simulated / absent / rejected**, one evidence pointer each;
+ rejected-substitutes column (the Kimi runbook's non-de-scopeable matrix).
+- Causal claims need a trace/state transition that fails if the named seam is removed;
+ config presence, registry presence, green wrapper exit, and screenshots are not proof.
+- Runtime truth wins over article/README; mechanical article fidelity check; supervisor
+ contamination rules (seven classes) + intervention ledger; leak-check/teardown hygiene.
+
+## 3. What changes in Wave 7 (the additions, each tied to a remediation deliverable)
+
+1. **Generated-slice usage is a first-class measured row.** The agent must use
+ `ui:add page` (slice mode), the client/query generator, and `service add-handler` — or record
+ a rejection with a reason. Both outcomes are valid data; silence is a harness failure.
+ (Measures T2; the #1090 box "runs `ui:add` or records why not" becomes executable.)
+2. **Type-escape review blockers become mechanical gates.** Arbitrary `any`, `as unknown as`,
+ raw route/search parsing, raw `fetch` on a supported seam → the consumer-side no-`any` gate
+ (T2/T6 deliverables) runs against the agent's product, not just framework exports.
+3. **Runtime-truth gates replace wrapper-exit gates.** Saga publish receipt handling, child
+ liveness states, durable-stream restart proof (T4) are probed by the evaluator from persisted
+ state + one correlated trace — the R2/R3 "registration and enqueue acceptance explicitly
+ insufficient" rule, now backed by framework surfaces that make it checkable.
+4. **MCP-on-by-default measurement.** The scaffold's `.mcp.json` carries the docs corpus (T5-03);
+ Wave 7 measures MCP call counts with the mirror absent (#1201's "workspace with no docs/
+ directory" condition). Six-zeros is the baseline to beat; the #1102/#1201 capability issues —
+ not Wave 7 — own retrieval quality.
+5. **Every configured gate enumerated and run separately**; wrapper/last-stage exit is
+ insufficient (Wave-6 rule, kept because #1328's 154-finding surface shipped green under it).
+6. **Confound control.** Same brief, same canary, same budget across arms; the R3 enforced-init
+ gate becomes a **measured arm variable** (enforced vs not), because it is the strongest
+ untested causal lever in the corpus (`research/wave-5-6-plans.md` #17).
+
+## 4. Measured smoke matrix (the minimum publishable design)
+
+| Arm | Canary | Init gate | Slice commands | What it isolates |
+| --- | --- | --- | --- | --- |
+| A | post-0.0.8 | not enforced | available | pure product effect of generation |
+| B | post-0.0.8 | enforced (R3 protocol) | available | init-gate lever on top of generation |
+| C (control) | pre-remediation 0.0.6 | not enforced | absent | the baseline delta |
+
+Two runs per arm minimum, different frontier models, blind gap-audit scoring (Wave-6 rubric).
+Success criterion for the program (0.0.8 exit): Arm A shows non-zero generated-slice adoption
+AND zero unexplained type-escapes; Arm A vs C shows a categorical difference on the capability
+map's frontend-composition and runtime-truth rows. Anything less feeds confirmed residual gaps
+back as issues — not as harness thickening.
+
+## 5. Board footprint (kept minimal)
+
+Wave 7 adds **one** issue to the train: `verify(0.0.8): Wave-7 measured adoption smoke — arms
+A/B/C, matrix + verdict` (0.0.8 exit gate; draft lives in the 0.0.8 milestone directory). The
+existing measurement chain stays untouched as owners: **#1102 + #1201 build capability, #1197
+demands re-measurement, #1090 holds the observational boxes** — the Wave-7 issue *consumes*
+them; planning it separately from that chain would duplicate the measurement harness
+(`research/github-board-open.md` §6.3 chain finding). Harness texts themselves live in the
+owner's Drive wave folders + `.llm/harness/`, not on the board.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T1-01-rfc-a-tracking-issue.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T1-01-rfc-a-tracking-issue.md
new file mode 100644
index 0000000000..d7391bc4f8
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T1-01-rfc-a-tracking-issue.md
@@ -0,0 +1,135 @@
+# rfc: SdkClientContribution — one typed chain for client construction, credentials, transport, policy metadata, and query invalidation — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T1-01 · **Proposed milestone:** `0.0.6` · **Labels:** `rfc` `type:feat` `area:sdk`
+`area:plugins` `priority:p1` `status:triage` · **Depends on:** none
+
+> Filing note (not part of the issue body): file via the `rfc_proposal.yml` form, which auto-applies
+> `rfc` + `status:triage` and prefixes the title with `rfc: `. The RFC text is
+> `rfcs/RFC-A-sdk-client-composition.md` in this run; per `research/github-conventions.md` §5.4 the
+> live house pattern is an issue-hosted RFC (#1123), not a merged `rfcs/NNNN-*.md` file — zero
+> numbered RFC files have ever merged. Paste the RFC's numbered sections into the issue body, or
+> open the RFC PR **and** this tracking issue if the owner wants the documented file process
+> exercised for the first time. Record which was chosen.
+
+## Summary
+
+`@netscript/sdk` owns a transport nobody can extend. `CreateServiceClientOptions` is a closed
+nine-field record with no `headers`, `fetch`, `interceptors`, `plugins`, `link`, or context type
+parameter, and `ServiceClientContext` is a closed interface rather than a type parameter — so the
+shipped auth plugin cannot ride the typed client, no plugin can contribute to client construction,
+and procedure policy metadata does not exist. Every mechanism needed is already present in the
+pinned oRPC 1.14.6, so this is an un-hiding problem, not a missing-primitive problem. This RFC
+ratifies one versioned `SdkClientContribution` contract that carries client construction, request
+context, headers/credentials, transport middleware, `$meta` policy metadata, response/error types,
+and query defaults/invalidation through a single typed chain, before #928 defines a second
+contribution dialect and before #934 invents its own policy shape.
+
+## Evidence
+
+- RFC text: `rfcs/RFC-A-sdk-client-composition.md` (this run).
+- Corpus: `research/repo-audit/services-sdk.md` §2, §3.1–§3.6, gap register S4/S5/S11/S12/S13;
+ `research/repo-audit/auth.md` §0, §2, §3.1, gaps G1/G2/G9/G10;
+ `research/external/orpc.md` §1, §4 (G1–G4, G6, G7, G11), §5, §6.
+- Source at baseline `fac9e339042c` (re-verified for this draft):
+ - `packages/sdk/src/ports/service-client.ts:203-222` — the closed nine-field options record;
+ `port` and `timeout` documented "Reserved …".
+ - `packages/sdk/src/ports/service-client.ts:129-155` — `ServiceClientContext` is a concrete
+ interface, not a type parameter.
+ - `packages/sdk/src/client/service-client.ts:41-49` — `port`/`timeout` are never destructured.
+ - `packages/sdk/src/client/http-client-link.ts:82-126` — `method`, `headers`, and `plugins` are
+ literals inside the link.
+ - `packages/sdk/src/client/mod.ts:15-36` — `createHttpClientLink` is not exported;
+ `packages/sdk/src/ports/mod.ts:7` advertises "the transport seam" while
+ `packages/sdk/src/ports/client-link-factory.ts:18-25` is unexported.
+ - `packages/service/src/primitives/handlers.ts:41-58` vs
+ `packages/service/src/builder/service-rpc.ts:57` — `RPCHandlerConfig.plugins` exists and is never
+ populated by the builder path.
+ - `packages/plugin/src/config/domain/plugin-contributions.ts:12-39` — twelve contribution groups,
+ none client-side; `:16` is a closed literal union naming a first-party plugin.
+ - `grep -rnE '\$meta<|\.meta\(' packages plugins` → no oRPC metadata usage.
+- Board: #928 (contribution contracts v1, `0.0.7`), #934 (deny-by-default gateway, `0.0.7`), #1093
+ (discovery hardcodes official factories, `0.0.6`), #884 (org-aware policy contracts, `0.0.12`),
+ #451 (in-process link mode, `Backlog / Triage`).
+
+## Current surface
+
+`createServiceClient(options)` → `createORPCClient(createHttpClientLink(...))`
+(`packages/sdk/src/client/service-client.ts:41-66`). The link is constructed once with literal
+`headers`, literal `plugins`, hard-coded `fetch`, and `method:
+inferRPCMethodFromContractRouter(contract)`. The only per-call seam that works is the retry/dedupe
+knob set typed into `ServiceClientContext` (`http-client-link.ts:27`, regression-tested at
+`packages/sdk/tests/integration/service-client-runtime_test.ts:113,153`). `defineServices()` forwards
+the same nine fields and inherits the gap wholesale
+(`packages/sdk/src/presets/define-services.ts:106-116`). There is no plugin-facing client axis
+anywhere.
+
+## Target contract
+
+A ratified RFC-A that fixes, at minimum:
+
+- a versioned `SdkClientContribution` envelope (literal `contractVersion`, namespaced `name`,
+ `environment`, `requires`, typed `context` marker, `headerKeys` + `headers`, the four oRPC
+ interceptor/plugin arrays, `fetch`, `errors`, `query`);
+- a composition algebra where the client's per-call context is the intersection of the chain's
+ declared contexts and `with` is optional — a host app with no plugins compiles unchanged;
+- a failure taxonomy in which absence, version mismatch, and conflict fail at compile time or
+ construction time, never as a missing header or a silently dropped plugin;
+- `NetScriptProcedureMeta.policy` as the single policy-metadata shape, threaded through
+ `baseContract` via `oc.$meta<…>()`;
+- server/client environment boundaries that are enforced, not documented;
+- the rule that contributions never observe the HTTP method, so oRPC v2's POST-only default lands in
+ one owned function.
+
+Ratification means: the owner accepts or amends the shape, the tracking issue receives its
+milestone, and T1-02…T1-06 may proceed. This issue closes only when all implementation children are
+merged.
+
+## Acceptance
+
+- [ ] RFC-A text is published in the house shape and linked from this issue.
+- [ ] The envelope's field list, version rule, and composition algebra are ratified or amended in
+ writing by the owner.
+- [ ] The failure taxonomy (absence / version mismatch / conflict) is ratified with the detection
+ point named for each row.
+- [ ] `NetScriptProcedureMeta.policy` is ratified as the single policy shape, and #934 is notified on
+ its issue before it reaches implementation.
+- [ ] The envelope is reviewed against #928's contribution contract and the divergences are recorded
+ on both issues.
+- [ ] Q1 (cookie topology) and Q2 (`PluginContributions` group shape) are answered on this issue
+ before T1-05 opens.
+- [ ] Every implementation child (T1-02…T1-06) references this issue with `Part of #`.
+- [ ] This issue is not closed by any implementation PR's closing keyword.
+
+## Boundaries
+
+- Do **not** duplicate **#928** (`[frontend-contrib S6] plugin-frontend-core contracts/v1`) — it owns
+ the frontend contribution contract; this RFC only asks that the two envelopes share a version
+ field, a conflict key, and a failure taxonomy.
+- Do **not** duplicate **#934** (generated deny-by-default procedure gateway) — it owns the gateway;
+ this RFC owns only the metadata type it will read.
+- Do **not** duplicate **#1093** (plugin discovery hardcodes official factory functions) — it owns
+ discovery; this RFC owns the contribution shape.
+- Do **not** duplicate **#884** (organization-aware identity and authorization policy contracts) —
+ tenancy typing on `Principal`/`AuthSession` stays there; this RFC only keeps the client context
+ extensible enough that a tenant field can arrive later.
+- Do **not** duplicate **#885** (auth conformance/mocking/scaffold test kit) or **#872**
+ (auth capability discovery).
+- Do **not** duplicate **#451** (in-process link-mode adapter) — T1-02's public link seam unblocks
+ it; it stays its own issue.
+- Do **not** duplicate **#1278** (type soundness ratification) — the `safe`/`isDefinedError` repair
+ is filed as T1-03 against a specific executed check, not as a soundness sweep.
+- Not in scope: implementation. This is a tracking issue; no code lands under it directly.
+
+## Docs/consumer proof
+
+Ratification is proved by documents, not by code: the RFC text, the owner's written disposition, the
+cross-links recorded on #928 and #934, and answers to Q1/Q2 on this issue. Adoption proof belongs to
+the children — T1-05 and T1-06 must each show a working consumer, and T1-03 must show
+`docs/site/services-sdk/sdk.md:199` compiling.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from the Stage-B
+corpus (`research/repo-audit/services-sdk.md`, `research/repo-audit/auth.md`,
+`research/external/orpc.md`) and Stage-C synthesis §4 (pack T1) and §5. All source claims re-verified
+against worktree baseline `fac9e339042c`. No GitHub mutation was performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T3-01-rfc-b-tracking-issue.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T3-01-rfc-b-tracking-issue.md
new file mode 100644
index 0000000000..58e9bab5ca
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T3-01-rfc-b-tracking-issue.md
@@ -0,0 +1,98 @@
+# RFC: production command composition kit — one transactional boundary for business state, audit, outbox and idempotency — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T3-01 · **Proposed milestone:** 0.0.6 · **Labels:** `rfc` `type:docs` `area:service`
+`area:database` `priority:p1` `status:triage` · **Depends on:** none (ratification only; T3-03 is the
+implementation and depends on this)
+
+## Summary
+
+NetScript ships every part of a production command and no composition of them: `withTransaction` is
+exported and has zero callers, idempotency exists only for worker deliveries, and "outbox" and
+"audit" do not exist anywhere in the codebase. Consumers therefore hand-roll the boundary that
+decides whether a retried request charges twice or a committed state change is never announced — the
+Wave-6 billing run, the only GO-grade run of three, wrote its entire command layer in app space. This
+issue tracks ratification of **RFC-B**, which proposes a DB-adapter-neutral command composition kit
+and, equally important, states per adapter where that boundary cannot exist. Ratification only —
+implementation is a separate issue in a later milestone.
+
+## Evidence
+
+- RFC draft: `.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/rfcs/RFC-B-command-composition-kit.md`
+ (seed run; not yet a repo file).
+- `packages/database/mod.ts:128` — `withTransaction(client, fn, options)` is exported and documented
+ (`docs/site/reference/database/index.md:52`); `grep -rn "withTransaction"` across `packages/`,
+ `plugins/`, `docs/` finds no caller. `$transaction` appears in no scaffold template and in no
+ `docs/site/services-sdk` page.
+- `packages/cli/src/kernel/assets/service/routers/v1.ts.template:25-53` — the generated write path is
+ a bare Prisma delegate call with no transaction, no version condition, and no receipt.
+- `packages/plugin-workers-core/src/ports/worker-idempotency-port.ts` — `claim`/`markApplied`/`release`
+ with `caller`/`message-id`/`payload-hash` resolution exists, KV-backed, scoped to worker deliveries
+ only.
+- `grep -rln "outbox"` over `packages/`, `plugins/`, `docs/` → no match. No audit-record primitive.
+- `packages/telemetry/src/attributes/helpers.ts` has job/saga/execution attribute helpers and **no**
+ command vocabulary; `packages/telemetry/src/attributes/spans.ts:45-46` has `rpc.client`/`rpc.server`
+ only.
+- Corpus: `research/preplan-package.md` item **H**; `research/wave-6-runs.md` §R3 (billing product owns
+ its command layer; two `FAIL_FIX` rounds before state advanced; empty `trigger_events` /
+ `job_execution_history` relational projections while authority lived in KV) and §R2 (app-owned
+ retry/compensation executor, `plugin-sagas` never adopted).
+
+## Current surface
+
+`@netscript/service` gives a builder, per-request context (`ctx.db`, `ctx.principal`,
+`ctx.traceHeaders` — `packages/service/src/builder/service-builder-impl.ts:259-282`) and oRPC
+handlers. `@netscript/database` gives an unused transaction helper and an isolation vocabulary
+(`packages/database/ports/database-client.ts:59-77`). `@netscript/plugin-sagas-core` gives
+`sagaCompensate`/`send`/`spawn` (`src/public/mod.ts:43`). Nothing connects them: there is no unit of
+work at the handler seam, no expected-version condition, no idempotency receipt, no same-commit side
+records, and no command span. `commonErrorMap`
+(`packages/contracts/src/application/contract-primitives.ts:21-52`) has no conflict code, so an
+optimistic-concurrency failure can only surface as a 500.
+
+## Target contract
+
+RFC-B is ratified (accepted, rejected, or accepted-with-amendments) with, at minimum, a recorded
+decision on: the kit's public home and shape; the unit-of-work port and its published
+`UnitOfWorkCapabilities`; the idempotency key + request-hash + replay-receipt semantics; the
+same-commit rule for audit and outbox and the relay that delivers them; the command telemetry
+vocabulary; the injected-failure conformance suite; the per-adapter capability table including the
+explicit statement that **no portable atomic boundary exists across two stores**; and the refusal
+boundary where a command becomes a saga. The three forks in RFC-B §8 (receipt-storage owner, package
+home, `expectedVersion` transport) are answered rather than deferred.
+
+## Acceptance
+
+- [ ] RFC-B body is on this issue in the house numbered-section shape.
+- [ ] The per-adapter capability table names, per store, whether same-commit side records are possible.
+- [ ] The RFC states the cross-store negative result explicitly and claims no exactly-once delivery.
+- [ ] The saga refusal boundary is defined and cross-references `sagaCompensate`.
+- [ ] The three §8 forks each carry a recorded owner decision.
+- [ ] The RFC names no billing-domain symbol; a reviewer confirms the surface is domain-free.
+- [ ] The RFC declares its dependency on the typed-error repair and what lands without it.
+- [ ] An implementation issue exists and carries `Part of #` before this issue closes.
+- [ ] Ratification adds no code; `packages/` and `plugins/` are unchanged by the ratifying PR.
+
+## Boundaries
+
+Do not duplicate: **#1123** (OpenAPI→MCP RFC — agent legibility of an existing API surface, not how a
+command commits); **#922**/**#928** (frontend contribution contracts); **#884**/**#885** (org-aware
+identity/authorization contracts and the auth conformance kit — the kit consumes `Principal`, it does
+not define authorization); **#742** (saga definition versioning); **#1278** (type-soundness
+ratification — this RFC must not restate the cast inventory); **#1332** (DB-schema-first docs);
+**#1263** (generated by-id handler returns 500 instead of a defined 404 — a contract-error defect in
+the existing CRUD template, fixed independently of this seam). This issue does **not** implement
+anything: no `packages/` or `plugins/` change lands under it.
+
+## Docs/consumer proof
+
+Ratification is proven by the decision record, not by adoption. Adoption proof belongs to the
+implementation issue: a generated project whose non-CRUD command uses the kit, a services-sdk how-to
+that replaces the hand-rolled pattern, and a docs example that type-checks against the published
+export map.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Pack T3 of the Fable-5
+remediation plan (`SYNTHESIS.md` §4); source item **H** in `research/preplan-package.md`. Checked
+against the dedup checklist in `research/github-board-open.md` §7 — no existing owner for
+transactional/idempotent command composition. Draft only; no board mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-01-canonical-client-dialect.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-01-canonical-client-dialect.md
new file mode 100644
index 0000000000..48b96852a8
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-01-canonical-client-dialect.md
@@ -0,0 +1,149 @@
+# fix(docs/sdk): the golden path names three different client modules and two incompatible query APIs, and the file the quickstart points at is a CSS entry — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T5-01 · **Proposed milestone:** 0.0.6 · **Labels:** `type:fix` `area:docs` `area:cli`
+`area:sdk` `priority:p0` `status:triage` · **Depends on:** none (coordinate with #1333, #1335)
+
+## Summary
+
+The documented path from a contract to a rendered page names three different modules for the one
+data-layer file — `apps/dashboard/client.ts`, `lib/api-clients.ts`, `lib/example-service.ts` — and
+only the third is ever written by a generator. The first is the scaffold's CSS hot-reload entry, so
+the quickstart's closing instruction sends a first-time reader to a stylesheet. On top of that the
+SDK ships two query surfaces with incompatible call signatures (`createQueryFactories` positional,
+`createServiceQueryUtils` object-wrapped) and the docs teach both as "the" canonical module without
+ever naming the fork. The one flag that bridges service → UI, `netscript service add --with-client`,
+appears exactly once site-wide, and when it does run it writes a file whose exported symbols are all
+named `exampleService*` regardless of the service. This is the seam the framework's entire pitch
+depends on, and it is unfollowable today.
+
+## Evidence
+
+Corpus: `research/repo-audit/docs-quickstart.md` §2.1–§2.4, §3, §4 Tier-1 table;
+`research/repo-audit/mcp-cli.md` §4.1; `SYNTHESIS.md` §2 (docs/discovery row), §4 T5.
+
+Verified in the worktree at `origin/main` `fac9e339042c`:
+
+1. `packages/cli/src/kernel/assets/app/client.ts.template` is three lines, all CSS imports
+ (`import './assets/styles.css'; import './assets/design.css';`). It contains no client.
+2. `docs/site/quickstart.vto:65` documents `client.ts # [owned] contract-derived client instance`
+ and `:252` tells the reader to "connect its query loader to the contract-derived client in
+ `apps/dashboard/client.ts`". `lib/` appears nowhere in the quickstart tree (`:57-87`).
+3. The real module is `apps//lib/example-service.ts`, written from
+ `packages/cli/src/kernel/assets/app/lib/example-service.ts.template` by
+ `packages/cli/src/kernel/application/scaffold/writers/write-example-service-app-files.ts:66`.
+ The template binds `createServiceClient` + `createQueryFactories` + `bridgeInvalidation`.
+4. `lib/api-clients.ts` is written by no generator and is cited across 10 published pages:
+ `docs/site/index.vto`, `services-sdk/sdk.md`, `web-layer/query.md`, `web-layer/examples.md`,
+ `web-layer/interactive.md`, `web-layer/form.md`, `tutorials/storefront/06-storefront-ui.md`,
+ `tutorials/live-dashboard/01-scaffold.md`, `.../03-sdk-cache-first-query.md`,
+ `.../04-definePage-QueryIsland.md`.
+5. Two query APIs: `createQueryFactories` at `packages/sdk/src/query/query-factory.ts:192`
+ (positional `queryOptions(input, options?)`, plus the server KV tier) vs
+ `createServiceQueryUtils` at `packages/sdk/src/query-client/create-service-query-utils.ts:55`
+ (a remap of oRPC `createTanstackQueryUtils`, `queryOptions({ input })`, no KV tier). Dialect A is
+ taught at `docs/site/services-sdk/sdk.md:92-115`; dialect B at `docs/site/web-layer/query.md:139-150`
+ — both call their module "the spine" / "one module per app".
+6. `--with-client` exists at
+ `packages/cli/src/public/features/services/add/add-service-command.ts:37` and scaffolds
+ `apps//lib/.ts` via
+ `packages/cli/src/kernel/adapters/service/client-scaffolder.ts:9-21,33-56`. It appears in the docs
+ exactly once, at `docs/site/reference/cli/commands.md:150`.
+7. **New defect found while verifying:** `client-scaffolder.ts:47` renders
+ `TEMPLATE_KEYS.appLibExampleService` for *any* service name, so
+ `netscript service add orders --with-client` writes `apps//lib/orders.ts` exporting
+ `exampleServiceName`, `exampleServiceClient`, `exampleServiceQueries`,
+ `exampleServiceListInvalidation` (template lines 8, 16, 22, 11). The generated symbol names do not
+ mention the service and two services collide on meaning, not on file path.
+8. Fabricated aliases in the same samples: `@contracts` (`web-layer/query.md:143`,
+ `services-sdk/sdk.md:100`, `tutorials/live-dashboard/02-contract-to-service.md:112`,
+ `.../03-sdk-cache-first-query.md:55`) and `@/lib/api-clients.ts` (`services-sdk/sdk.md:189,194,199`).
+ The scaffold generates only `'@app/' -> './'` and `'@/contracts'`
+ (`packages/cli/src/kernel/adapters/templates/app/generate-app-deno-json.ts:62-63,125-130`).
+
+## Current surface
+
+- One generated data-layer module per scaffolded project: `apps//lib/example-service.ts`, using
+ dialect A (`createQueryFactories`), plus `bridgeInvalidation` with a hand-written
+ `(routerName, action)` string pair.
+- One re-runnable path to a second module: `service add --with-client`, which reuses the same
+ template verbatim and therefore emits `example*` symbol names.
+- Docs teach three module names and two dialects, with two aliases that do not resolve.
+- `packages/sdk/README.md:32` — the JSR landing page (`jsr-package-settings.json:6`
+ `readmeSource: "readme"`) — front-loads `createServiceQueryUtils`, i.e. dialect B.
+
+## Target contract
+
+One name, one dialect, ratified in this issue and enforced by T5-02's gate:
+
+1. **Module name — `apps//lib/.ts`.** Rationale: it is what the CLI already writes
+ (`client-scaffolder.ts:19`, `write-example-service-app-files.ts:66`); it is per-service, so a
+ second service is a new file rather than a merge-edit into an aggregate; and
+ `apps//client.ts` is unavailable — it is the Fresh CSS entry. `lib/api-clients.ts` is
+ retired from the docs (a single aggregate module cannot be generated per-service without
+ rewriting an existing file) and its 10 pages are rewritten to the per-service form.
+2. **Query dialect — `createQueryFactories` (dialect A) is canonical.** Rationale: it is what the
+ scaffold emits, it is the only surface with the server KV tier (`getCachedEntry`, `prefetch`,
+ `invalidate`, `key`) that the cache-first loader story on `web-layer/query-bridge.md` depends on,
+ and `defineServices()` already returns it. `createServiceQueryUtils` remains public and is
+ documented in exactly one place, explicitly labelled as the thin oRPC/TanStack remap with no KV
+ tier and a different call shape (`queryOptions({ input })`), with a one-line
+ "do not mix" warning. No page outside that one may present it as the app's data-layer spine.
+3. **Template naming.** `example-service.ts.template` becomes service-name-derived so
+ `service add orders --with-client` emits `ordersClient` / `ordersQueries` /
+ `ordersListInvalidation`, and the scaffolded default keeps working because its service name is
+ substituted the same way.
+4. **Discoverability.** `--with-client` is documented on the golden path: `quickstart.vto`,
+ `cli-reference.md`, `services-sdk/how-to/add-a-service.md`, and `how-to/index.md`.
+5. **Aliases.** Every sample uses `@app/lib/.ts` and `@/contracts` — the two
+ aliases the scaffold actually generates.
+
+## Acceptance
+
+- [ ] Docs name exactly one data-layer module path, `apps//lib/.ts`.
+- [ ] `lib/api-clients.ts` appears in zero published pages.
+- [ ] `client.ts` is documented as the CSS entry it is, in the quickstart file tree.
+- [ ] The quickstart file tree shows `lib/` and its contents.
+- [ ] `createQueryFactories` is the only query dialect taught on the golden path.
+- [ ] Exactly one page documents `createServiceQueryUtils`, naming its differing call shape and its
+ missing KV tier.
+- [ ] `--with-client` is documented in quickstart, cli-reference, and add-a-service.
+- [ ] `service add --with-client` emits symbols derived from ``, not `exampleService*`.
+- [ ] `@contracts` and `@/lib/...` appear in zero code samples.
+- [ ] A CLI test asserts the scaffolded client path and exported symbol names for a non-default
+ service name.
+- [ ] Negative gate: a docs check fails when `lib/api-clients.ts`, `apps/*/client.ts` as a client, or
+ `@contracts` reappears in `docs/site/**`.
+- [ ] Negative gate: a docs check fails when `createServiceQueryUtils` appears outside its one
+ allow-listed page.
+
+## Boundaries
+
+- **#1333** owns making the default scaffolded app idiomatic and deriving its name from the project.
+ This issue does not restructure the default app; it fixes the naming/dialect contract the docs and
+ the client template must both honour. Coordinate the chosen module name with #1333 before either
+ lands.
+- **#1335** owns the repo-wide generated-surface conformance inventory. Do not re-file that
+ inventory here; this is one ratified seam, not the sweep.
+- **#1208** owns tutorials teaching the page builder (phase 1). Rewriting tutorial prose for the
+ page-builder story is theirs; this issue only replaces the module name and query dialect where
+ tutorials already cite them.
+- **#1210** owns per-API deep dives and the competitive benchmark. No new per-API pages here.
+- **#1332** owns DB-schema-first docs; **#1334** owns the home page's capability story. `index.vto`
+ is touched here only to fix its uncompilable `lib/api-clients.ts` import.
+- **#1260** owns which SDK prose enters the MCP corpus; **#1201** owns the export-surface corpus.
+- Not a goal: adding a client/query generator for a second service (that is the T2 pack's
+ contract-derived generator), or changing `resolveProjectRoot` (T2 pack).
+
+## Docs/consumer proof
+
+`docs/site/quickstart.vto` reads end-to-end as one dialect: `contract add` → `service add --with-client`
+→ `apps//lib/.ts` → loader → island, with every path in the file tree existing after a
+real `netscript init`. `docs/site/index.vto` tab 3 compiles against the published entrypoints under
+T5-02's gate. A reader who copies `services-sdk/sdk.md` and `web-layer/query.md` into the same app
+gets one query key shape, not two.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from the Stage-B
+docs/quickstart and MCP/CLI audits and re-verified against the worktree at `fac9e339042c`; finding 7
+(`example*` symbol names from `--with-client`) is new in this pass and is not in the corpus.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-02-compile-the-docs-gate.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-02-compile-the-docs-gate.md
new file mode 100644
index 0000000000..b936f92f78
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-02-compile-the-docs-gate.md
@@ -0,0 +1,120 @@
+# test(docs): docs:accuracy is a fixed-string needle checker — no docs/site code block is ever compiled, and a CLI change never revalidates the site — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T5-02 · **Proposed milestone:** 0.0.6 · **Labels:** `type:test` `area:docs`
+`area:tooling` `priority:p1` `status:triage` · **Depends on:** T5-01 (the gate codifies the ratified
+dialect)
+
+## Summary
+
+`deno task docs:accuracy` passes today while the front door points readers at a CSS file and teaches
+two incompatible query APIs, because it only asserts that certain literal strings are present or
+absent in certain files. No code block anywhere in `docs/site/**` is ever type-checked or executed,
+and the Pages workflow only fires on `docs/site/**` pushes, so a change to the CLI or the SDK never
+revalidates the prose that documents it. Every defect in T5-01 and T5-05 is invisible to CI by
+construction. Until a snippet compiles against the published entrypoints, docs accuracy is an
+unenforced convention.
+
+## Evidence
+
+Corpus: `research/repo-audit/docs-quickstart.md` §2.8 and §4 Tier 3; `SYNTHESIS.md` §2
+(harness/evaluation row), §4 T5.
+
+Verified in the worktree at `fac9e339042c`:
+
+1. `.llm/tools/docs/check-accuracy-and-discoverability.ts:9-19` defines `requireText` / `forbidText`
+ as `text.includes(needle)`; `:152-174` iterates a hardcoded `requiredMutationFamilies` list and
+ asserts only that the string `` `netscript `` appears in `cli-reference.md`. Nothing it
+ asserts is derived from the CLI, the scaffold writers, or the SDK exports.
+2. `.llm/tools/docs/check-exports-drift.ts:13-80` is real code-derived verification but its
+ `AUTHORITATIVE_MAPPING` covers 8 packages (`fresh-ui`, `plugin`, `config`, `contracts`, `queue`,
+ `sdk`, `service`, `telemetry`), and most entries set `checkSymbols: false`. The remaining
+ reference pages and all guide pages are unverified. It is invoked from
+ `check-accuracy-and-discoverability.ts:176-184`.
+3. `.github/workflows/pages.yml:3-11` triggers on `push` to `main` with
+ `paths: [docs/site/**, .github/workflows/pages.yml]`, plus `release: published` and
+ `workflow_dispatch`. A `packages/**` change never rebuilds or re-link-checks the site.
+4. The only precedent cited for executed doc examples,
+ `packages/service/tests/_fixtures/readme-examples_test.ts`, is itself a needle checker: it calls
+ `assertStringIncludes(readme, 'const service = await defineService')` and asserts the absence of
+ `addHealthCheck` / `addReadinessCheck`. It never compiles the README block. So there is currently
+ **no** compile-the-docs precedent in the repo at all.
+5. `deno task docs:links` (`.llm/tools/validation/check-internal-doc-links.ts`) is clean and real —
+ `docs=102 broken-links=0 broken-anchors=0 orphans=0` per the Stage-B audit — but it validates
+ links, not code.
+6. `deno.json:81-83` wires `docs:links` and `docs:accuracy` into `docs:maintenance`; no docs task
+ invokes `deno check`.
+
+## Current surface
+
+Three docs gates, none of which can observe a wrong API: `docs:links` (link/anchor graph),
+`docs:accuracy` (literal needles over `cli-reference.md` and a fixed preferred-path list, plus
+`check-freshRootImports` over `docs/`), and `check-exports-drift` (8 of 32 reference pages, symbol
+checking mostly disabled). Pages CI is path-gated to `docs/site/**`.
+
+## Target contract
+
+1. **A docs-snippet compile gate.** A checked-in tool extracts fenced `ts`/`tsx` blocks from
+ `docs/site/**` and type-checks them against the **published entrypoints** (`@netscript/*` import
+ specifiers, not relative source paths), so a snippet that imports a symbol the package does not
+ export fails.
+2. **Explicit opt-out, not opt-in.** Blocks that are deliberately non-compiling (counter-examples,
+ partial fragments, shell, config) carry an inline marker (e.g. an info string
+ ```` ```ts no-check: ````). The gate fails on an *unmarked* block that does not compile
+ and on a marked block that has no reason. The marker census is reported so the number of exempt
+ blocks is visible and can only shrink.
+3. **Compilation harness, not execution.** Blocks are assembled into synthetic modules with a shared
+ preamble/import map; the gate is `deno check --unstable-kv` over the generated set. No network,
+ no service start.
+4. **Coverage floor.** The gate starts at the T5-01 Tier-1 pages (`quickstart.vto`, `index.vto`,
+ `services-sdk/sdk.md`, `services-sdk/how-to/add-a-service.md`, `web-layer/query.md`,
+ `web-layer/examples.md`, `web-layer/interactive.md`, `web-layer/form.md`,
+ `web-layer/query-bridge.md`) and records a written expansion plan for the rest.
+5. **Trigger fix.** `pages.yml` adds `packages/**` and `plugins/**` to its `paths`, or the same
+ validation runs as a `ci.yml` job on those paths, so a framework change revalidates the site.
+6. **Needle checker demoted.** `docs:accuracy` keeps only the assertions that are genuinely
+ string-shaped (forbidden stale claims, mutation-map columns) and hands API truth to the compile
+ gate and to `check-exports-drift`.
+
+## Acceptance
+
+- [ ] A checked-in tool extracts fenced TS/TSX blocks from `docs/site/**` and type-checks them.
+- [ ] Snippets resolve `@netscript/*` specifiers, not relative source paths.
+- [ ] The gate runs in CI on changes to `docs/site/**`, `packages/**`, and `plugins/**`.
+- [ ] `pages.yml` (or an equivalent CI job) revalidates the site when `packages/**` changes.
+- [ ] Non-compiling blocks require an explicit marker with a stated reason.
+- [ ] The gate reports the count of marked-exempt blocks on every run.
+- [ ] All T5-01 Tier-1 pages compile with zero unmarked failures.
+- [ ] Negative test: a fixture page importing a non-exported symbol makes the gate exit non-zero.
+- [ ] Negative test: a fixture block marked exempt with no reason makes the gate exit non-zero.
+- [ ] Negative test: reintroducing the `queryOptions({ input })` call shape into a dialect-A snippet
+ makes the gate exit non-zero.
+- [ ] `deno task docs:accuracy` no longer asserts API truth by literal needle.
+
+## Boundaries
+
+- **#1278** owns type-soundness ratification across the public surface and the docs, including the
+ `as unknown as` / `any` inventory and its guard-rail. This issue does **not** add a cast guard;
+ where a docs snippet needs a cast to compile, that is #1278's finding, and this gate must not
+ silently legalise it.
+- **#1108** owns verifying generated package references against live export maps; the expansion of
+ `check-exports-drift` from 8 to all reference pages belongs there, not here. This gate covers
+ *prose* code blocks.
+- **#1210** owns per-API deep-dive pages; **#1208** owns tutorial rewrites. This issue does not
+ author prose — it only makes the prose falsifiable.
+- **#767** owns `docs:readme:check` being a dead gate; package READMEs are T5-05.
+- **#1343** owns the installed-consumer canary smoke. This gate compiles against workspace-resolved
+ `@netscript/*`; it is not a published-artifact install proof.
+- Not a goal: executing snippets, starting services, or screenshotting the site.
+
+## Docs/consumer proof
+
+After this lands, the proof that T5-01's rewrite is real is mechanical rather than editorial: a PR
+that reintroduces `lib/api-clients.ts` or dialect-B call shapes on a golden-path page fails CI. The
+exempt-block census, published in the run log, is the honest measure of how much of the site is still
+prose-only.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Corpus claim that
+`packages/service/tests/_fixtures/readme-examples_test.ts` executes README examples was checked and
+corrected in this pass: it is a string-inclusion test, so there is no existing compile precedent.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-03-mcp-docs-root-wiring.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-03-mcp-docs-root-wiring.md
new file mode 100644
index 0000000000..69563b36ac
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-03-mcp-docs-root-wiring.md
@@ -0,0 +1,124 @@
+# fix(agent): emitted .mcp.json carries no --docs-root, so the corpus agent init just installed is invisible and search_docs indexes two documents — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T5-03 · **Proposed milestone:** 0.0.6 · **Labels:** `type:fix` `area:agentic`
+`area:tooling` `area:docs` `priority:p1` `status:triage` · **Depends on:** none
+
+## Summary
+
+`netscript agent init --with-docs` writes an offline documentation bundle into `.netscript/docs/`,
+and `netscript agent mcp` accepts a `--docs-root` flag that switches `search_docs` from the embedded
+corpus to a filesystem corpus. The host config that `agent init` emits never passes `--docs-root`,
+and the MCP server never probes the project for a docs directory, so the bundle the user just
+installed is unreachable and `search_docs` / `list_docs` / `get_doc` see two documents: the MCP
+package README and `help.md`. This is the plumbing under the measured result that six consecutive
+agent runs made zero MCP calls — an agent that tries `search_docs` once and gets two documents will
+not try again.
+
+## Evidence
+
+Corpus: `research/repo-audit/mcp-cli.md` §1.3 and §1.6 M1/M2; `research/github-board-open.md` §6.3;
+`research/wave-5-6-plans.md` §6 (three-run table: MCP 0/0/0 against curl 41/35/38);
+`SYNTHESIS.md` §1.4, §4 T5.
+
+Verified in the worktree at `fac9e339042c`:
+
+1. `packages/mcp/cli.ts:114-121` — the corpus is `FilesystemDocsCorpus` only when
+ `options.docsRoot ?? resolveDocsRoot([], Deno.env.get('NETSCRIPT_DOCS_ROOT'), projectRoot)` is
+ truthy; otherwise `EmbeddedDocsCorpus` with `[{ slug: 'mcp', source: MCP_PACKAGE_README },
+ ...(options.embeddedDocs ?? [])]`.
+2. `packages/mcp/cli.ts:82-92` — `resolveDocsRoot` reads only the `--docs-root` argv flag and the
+ `NETSCRIPT_DOCS_ROOT` environment variable. It performs no project probing.
+3. `packages/cli/src/public/features/agent/mcp/agent-mcp-command.ts:39` — the flag exists:
+ `--docs-root `, "Public NetScript documentation root", plumbed through
+ `AgentMcpInput.docsRoot` (`agent-mcp-input.ts:5`) into `createAgentMcpOptions`
+ (`run-agent-mcp.ts:42`).
+4. `packages/cli/src/public/features/agent/init/init-agent.ts:94-104` — `--with-docs`
+ (`init-agent-command.ts:29`) writes each bundle file to
+ `join(input.projectRoot, ".netscript", "docs", path)`.
+5. `packages/cli/src/public/features/agent/init/init-agent.ts:226-262` — `writeHostConfig` emits
+ `args: ["run", "--config", /deno.json, "-A", cliSpecifier, "agent", "mcp",
+ "--project-root", projectRoot]`. There is no `--docs-root` argument and no
+ `env: { NETSCRIPT_DOCS_ROOT }` block, for either the `mcpServers` (`.mcp.json`) or `servers`
+ (`.vscode/mcp.json`) key.
+6. `packages/cli/src/public/features/agent/mcp/run-agent-mcp.ts:48` — the CLI-hosted server adds
+ exactly one embedded document: `{ slug: "help", source: EMBEDDED_SKILL_FILES["help.md"] }`. Total
+ default corpus via `agent mcp`: 2 documents. Standalone (`deno x jsr:@netscript/mcp/cli`): 1.
+7. The bundle builder exists and is checked in: `.llm/tools/docs/build-agent-docs-bundle.ts`
+ (with `build-agent-docs-bundle_test.ts`), consumed through
+ `packages/cli/src/public/adapters/agent/deno-agent-docs-generator.ts` (wired at
+ `agent-group.ts:21,41`).
+
+## Current surface
+
+- Flag: present on `agent mcp`, honoured by the server, documented nowhere on the golden path.
+- Bundle: installed by `agent init --with-docs` at `.netscript/docs/`.
+- Config: emitted by `agent init` without the flag or the environment variable.
+- Result: two-document corpus for every agent that starts the server from the generated config, with
+ no signal to the agent that the corpus is degraded.
+
+## Target contract
+
+1. **`agent init` wires what it installs.** When the offline bundle is written, the emitted host
+ configs carry `--docs-root /.netscript/docs` (or the equivalent `env`
+ `NETSCRIPT_DOCS_ROOT`), for every host config it writes.
+2. **Auto-detection as the fallback.** `resolveDocsRoot` gains a project probe: with no flag and no
+ environment variable, a `/.netscript/docs` directory that contains at least one
+ indexable document is used. Explicit flag beats environment beats probe.
+3. **The default corpus is bounded and release-matched.** When no filesystem root resolves, the
+ embedded corpus carries a bounded, versioned subset of the published docs rather than one README:
+ the golden-path pages needed to answer "how do I get from a contract to a page" — the corpus set
+ is enumerated in the issue's implementation PR, is generated (not hand-copied), and carries the
+ same framework-version provenance check that the export-surface corpus already enforces
+ (`packages/mcp/src/infrastructure/export-surfaces/embedded-export-surface-corpus.ts:46,58-62`
+ throws on a version mismatch). A size budget is asserted so the package does not grow unbounded.
+4. **Degraded state is observable.** `list_docs` reports the corpus kind (`filesystem` | `embedded`),
+ its document count, and the resolved root, so an agent (and a wave run) can see a two-document
+ corpus instead of inferring it from bad answers.
+
+## Acceptance
+
+- [ ] `agent init --with-docs` emits host configs containing a `--docs-root` pointing at the
+ installed bundle.
+- [ ] Every host config `agent init` writes carries the same docs wiring.
+- [ ] With no flag and no environment variable, the server resolves `/.netscript/docs`
+ when it exists.
+- [ ] Explicit `--docs-root` overrides the environment variable, which overrides auto-detection.
+- [ ] The embedded fallback corpus contains the enumerated golden-path document set, generated at
+ build time.
+- [ ] The embedded corpus carries framework-version provenance and fails closed on mismatch.
+- [ ] A size budget for the embedded corpus is asserted in CI.
+- [ ] `list_docs` reports corpus kind, resolved root, and document count.
+- [ ] Tests cover: bundle installed → filesystem corpus; no bundle → embedded corpus; flag beats
+ env beats probe.
+- [ ] Negative test: a `.netscript/docs` directory with no indexable documents does not silently
+ produce an empty corpus — it falls back to embedded and says so.
+- [ ] Negative test: an embedded corpus whose provenance version differs from the package version
+ fails to construct.
+
+## Boundaries
+
+- **#1260** owns *which SDK prose* enters the shipped `search_docs` corpus. This issue is the
+ **plumbing** — flag wiring, auto-detection, provenance, and observability. The concrete SDK
+ document selection is #1260's call; if #1260 lands first, this issue consumes its set.
+- **#1201** owns serving the generated export surfaces through MCP. Do not add export-surface
+ content here; the corpus in scope is prose.
+- **#1102** owns making capability discovery intent-aware. This issue does not change ranking or
+ retrieval strategy — `EmbeddedDocsCorpus` stays lexical.
+- **#1197** owns re-measuring agent-surface adoption and **#1090** owns observing whether behaviour
+ changed. Do not restate the measurement here; this issue is a precondition for it.
+- **#1324** owns OpenCode ignoring the generated MCP config; a client that never reads `.mcp.json`
+ is not fixed by writing a better `.mcp.json`.
+- Not a goal: new MCP tools (`list_generators`, `plan_generation`), or indexing the full
+ `docs/site/**` tree into the published package.
+
+## Docs/consumer proof
+
+A fresh `netscript init` + `netscript agent init --with-docs` followed by `search_docs "typed client
+for a service"` returns the golden-path page, not the MCP README. `list_docs` on the same project
+reports `filesystem` and a document count in the tens or hundreds. The agent-tooling page documents
+`--docs-root` and the auto-detection order.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. All line citations
+re-verified against the worktree at `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-04-execute-command-version-pin.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-04-execute-command-version-pin.md
new file mode 100644
index 0000000000..2371266e11
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-04-execute-command-version-pin.md
@@ -0,0 +1,122 @@
+# fix(mcp): execute_command spawns a downloaded jsr:@netscript/cli pinned to the MCP package version even when the server is hosted by a local CLI — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T5-04 · **Proposed milestone:** 0.0.6 · **Labels:** `type:fix` `area:agentic`
+`area:tooling` `area:cli` `priority:p1` `status:triage` · **Depends on:** none
+
+## Summary
+
+When an agent calls `execute_command` against the MCP server started by `netscript agent mcp`, the
+command does not re-enter the running CLI. It shells out to
+`deno run -A jsr:@netscript/cli@`, downloading and executing a *different*
+binary at a version pinned to the MCP package rather than to the host CLI. A maintainer working from
+a local or workspace build silently gets published behaviour, and an agent that scaffolds through
+MCP can produce artifacts from a different release than the one it is documenting. The same host
+reports `version: "current"` to `list_commands`, so the agent has no way to notice. `list_commands`
+and `execute_command` are also the only two flows that are not receipt-wrapped, so work performed
+through MCP cannot satisfy the `record_drift` evidence gate.
+
+## Evidence
+
+Corpus: `research/repo-audit/mcp-cli.md` §1.4, §1.6 M3/M5, §4.5 C4; `SYNTHESIS.md` §4 T5.
+
+Verified in the worktree at `fac9e339042c`:
+
+1. `packages/mcp/src/infrastructure/spawn-command-executor.ts:6-14`:
+ ```ts
+ import { MCP_PACKAGE_VERSION } from '../publish-assets.generated.ts';
+ export const DEFAULT_CLI_COMMAND: readonly string[] = Object.freeze([
+ 'deno', 'run', '-A', `jsr:@netscript/cli@${MCP_PACKAGE_VERSION}`,
+ ]);
+ ```
+2. `packages/cli/src/public/features/agent/mcp/run-agent-mcp.ts:44` passes
+ `commandExecutor: new SpawnCommandExecutor()` with no `cliCommand` override, so
+ `DEFAULT_CLI_COMMAND` applies inside the CLI-hosted server.
+3. `run-agent-mcp.ts:27-30` builds the command-catalog program with `version: "current"`, so
+ `list_commands` never reports the real CLI version. The public tree otherwise reports
+ `CLI_PACKAGE_VERSION` (`packages/cli/src/public/features/root/public-command-tree.ts:121`).
+4. `packages/mcp/cli.ts:194-200` binds `list_commands` and `execute_command` **without**
+ `withReceipt`, while every read/analytics/doctor/service flow at `:150-234` is wrapped
+ (`withReceipt` at `:236-264` writes `{resource, command, timestamp, exitStatus}` through
+ `FilesystemDiagnosticEvidence`).
+5. `packages/mcp/src/application/flows/record-drift-flow.ts:5,29-42` refuses unless a receipt for the
+ named resource exists, has `exitStatus === 0`, and is younger than
+ `DIAGNOSTIC_RECEIPT_TTL_MS = 15 * 60 * 1000`. Its refusal text
+ (`diagnosticEvidenceRefusal`, `:17-22`) tells the agent to run `plugin doctor` or a doctor/telemetry
+ tool — which is correct only because `execute_command` cannot produce a receipt.
+6. `packages/mcp/src/domain/command-policy.ts` allows mutating verbs through `execute_command`
+ (`db init|generate|migrate|seed|…`, `generate`, `contract`, `plugin install|sync|doctor`,
+ `ui:add|ui:init|ui:list|ui:update`), so the mismatched binary can write to the project.
+7. Version pinning elsewhere is host-correct by comparison: `agent init` writes
+ `jsr:@netscript/cli@${CLI_PACKAGE_VERSION}`
+ (`packages/cli/src/kernel/constants/jsr-specifiers.ts:33-45`, used at `init-agent.ts:232`), i.e.
+ the CLI's own version — not the MCP package's.
+
+## Current surface
+
+- One process boundary with three independent version identities: the host CLI
+ (`CLI_PACKAGE_VERSION`), the MCP server (`MCP_PACKAGE_VERSION`, also `serverInfo.version`), and the
+ spawned child (`jsr:@netscript/cli@MCP_PACKAGE_VERSION`). They agree today only because both
+ packages read `0.0.4` at this baseline.
+- `list_commands` advertises `version: "current"`.
+- Two mutating tools produce no diagnostic receipt.
+
+## Target contract
+
+1. **The CLI-hosted server executes the CLI that hosts it.** `run-agent-mcp.ts` injects a
+ `cliCommand` derived from the running entrypoint (`Deno.execPath()` / `import.meta.url` for a
+ source run, the installed binary for a global install), so `execute_command` never downloads a
+ second CLI when one is already running.
+2. **Version identity is truthful.** `list_commands` reports `CLI_PACKAGE_VERSION`, and the tool
+ result carries the version and the resolved executor command so an agent can see what it is
+ driving. `serverInfo.version` remains the MCP package version; a mismatch between the two is
+ surfaced, not hidden.
+3. **The standalone default stays pinned, but to the right thing.** When no host CLI exists
+ (`deno x jsr:@netscript/mcp/cli`), `DEFAULT_CLI_COMMAND` remains a pinned JSR specifier; the pin
+ must be documented as MCP-package-derived and asserted equal to the CLI version by the existing
+ publish-assets generation, or explicitly decoupled with a stated policy.
+4. **Mutating tools leave receipts.** `execute_command` is receipt-wrapped on the same terms as the
+ read flows: a successful run writes a receipt for its resource; a failed run writes
+ `exitStatus: 1`. `list_commands` is wrapped or explicitly exempted with a written reason. The
+ `record_drift` refusal text is updated to name `execute_command` once it can authorize.
+
+## Acceptance
+
+- [ ] `netscript agent mcp` injects a `cliCommand` that re-enters the running CLI.
+- [ ] No JSR download occurs for `execute_command` when the server is CLI-hosted.
+- [ ] `list_commands` reports `CLI_PACKAGE_VERSION`, not `"current"`.
+- [ ] `execute_command` results include the resolved executor command and version.
+- [ ] `execute_command` writes a diagnostic receipt on success and on failure.
+- [ ] `record_drift` accepts a receipt produced by a successful `execute_command`.
+- [ ] The `record_drift` refusal message lists the tools that can authorize it, accurately.
+- [ ] Tests cover: CLI-hosted executor resolution; standalone fallback to the pinned specifier;
+ receipt written on both exit paths.
+- [ ] Negative test: a denied command (`deploy`, `init`, `db reset`, `plugin remove`, `ui:remove`)
+ writes no success receipt and cannot authorize `record_drift`.
+- [ ] Negative test: with the host CLI at a version different from the MCP package, no spawn resolves
+ to the MCP-pinned specifier.
+
+## Boundaries
+
+- **#1197** owns the agent-surface adoption fix and re-measurement; **#1090** owns observing whether
+ agent behaviour changed. This issue does not restate or re-measure adoption — it removes one
+ mechanism by which MCP-driven work is silently wrong.
+- **#1093** owns plugin discovery hardcoding official factory callees; unrelated to executor
+ resolution.
+- **#1343** owns the installed-consumer canary smoke; proving the published pin works end to end is
+ theirs.
+- **T5-03** owns docs-corpus wiring in the same host composition root; keep the two changes
+ separable — do not fold either into the other's PR.
+- **#1126 / #1139** own the OpenAPI→MCP tool surface; this issue adds no new tools.
+- Not a goal: adding `list_generators` / `plan_generation` (corpus gap M4), or changing
+ `command-policy.ts` allow/deny sets.
+
+## Docs/consumer proof
+
+The agent-tooling reference states which binary `execute_command` runs in each hosting mode and what
+`list_commands` reports. A wave run's transcript shows an `execute_command` receipt followed by an
+accepted `record_drift`, which is currently impossible.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. All line citations
+re-verified against the worktree at `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-05-reference-completeness.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-05-reference-completeness.md
new file mode 100644
index 0000000000..d5f4fbc317
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T5-05-reference-completeness.md
@@ -0,0 +1,144 @@
+# docs(reference): four publishable plugin-*-core packages have no reference page, the publish gate demands a path the IA does not use, and the JSR landing README teaches the wrong dialect — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T5-05 · **Proposed milestone:** 0.0.6 · **Labels:** `type:docs` `area:docs`
+`area:packages` `priority:p2` `status:triage` · **Depends on:** T5-01 (dialect), T5-02 (gate)
+
+## Summary
+
+The reference tree does not cover the publish set, and the gate that is supposed to enforce coverage
+has never run against it and would demand paths the site does not use. Twenty-two package READMEs —
+including `packages/sdk/README.md`, which is the JSR landing page — have not been touched since a
+single bulk commit and still teach the query dialect the scaffold does not emit. Seven public CLI
+verbs are documented nowhere, one documented claim about `deploy docker`/`compose` is the inverse of
+the code, and the root README hardcodes a scaffold file count that the quickstart explicitly says not
+to trust. Individually these are small; together they are the reason a reader who leaves the golden
+path cannot get back on it.
+
+## Evidence
+
+Corpus: `research/repo-audit/docs-quickstart.md` §2.6, §2.7, §2.9, §2.10, §4 Tier 2;
+`research/repo-audit/mcp-cli.md` §2.1, §4.5 C3; `SYNTHESIS.md` §4 T5.
+
+Verified in the worktree at `fac9e339042c`:
+
+1. **Missing reference pages.** `docs/site/reference/` has 32 package directories.
+ `plugin-ai-core` and `plugin-auth-core` have pages; `plugin-sagas-core`, `plugin-streams-core`,
+ `plugin-triggers-core`, `plugin-workers-core` do not. All four declare an object-valued
+ `publish` key in `deno.json` (`packages/plugin-sagas-core/deno.json:39`,
+ `plugin-streams-core:20`, `plugin-triggers-core:32`, `plugin-workers-core:42`), i.e. they are
+ publishable — contrast `packages/bench/deno.json:33` `"publish": false`, which correctly has no
+ page. The omission is inconsistent, not policy.
+2. **Gate vs IA disagreement.** `.llm/tools/release/publish-readiness.ts:302-306` requires
+ `docs/site/reference/${packageSegment(member.name)}/index.md`, where `packageSegment` is the part
+ after `@netscript/` (`:411-415`). The site documents `@netscript/plugin-sagas` at
+ `docs/site/reference/sagas/index.md` (its front matter reads `title: "@netscript/plugin-sagas"`),
+ and the same for `streams`, `triggers`, `workers`. A first publish under the current rule would
+ demand `/reference/plugin-sagas/`.
+3. **The gate has never checked the existing tree.** It runs only over `newPackages`, computed as
+ members with no registry versions (`publish-readiness.ts:158-180`), through `auditFirstPublish`
+ (`:179`).
+4. **README staleness (measured by `git log -1` per file).** 22 of 30 `packages/*/README.md` and 4 of
+ 6 `plugins/*/README.md` were last touched 2026-07-18, including `packages/sdk/README.md`.
+ `jsr-package-settings.json:6` sets `readmeSource: "readme"`, so that file *is* the JSR landing
+ page. `packages/sdk/README.md:32-33` front-loads `createServiceQueryUtils` as the TanStack story —
+ the dialect T5-01 demotes.
+5. **Root README.** `README.md:41` asserts "The scaffold reports **183 files, 44 directories**",
+ directly contradicted by `docs/site/quickstart.vto:51` ("treat the printed result—not a static
+ number in this guide—as the authority"); the counts vary with `--db/--service/--editor`.
+ `README.md:35` prints the literal `jsr:@netscript/cli@` while the site derives the
+ specifier automatically (`docs/site/_data.ts:30-33`).
+6. **Undocumented verbs** (grep over `docs/site` returns zero files for each):
+ `netscript agent drift`, `netscript deploy desktop`, `netscript deploy package-cli`,
+ `netscript deploy list`, `netscript config list`. `netscript plugin ai` appears only in
+ `docs/site/reference/plugin-ai/index.md`, never as a CLI verb in the command reference. All exist
+ in the public tree (`packages/cli/src/public/features/agent/agent-group.ts:44-51`,
+ `.../deploy/deploy-group.ts:25-97`, `.../config/config-group.ts:13-29`,
+ `.../plugins/plugins-group.ts:33-151`).
+7. **Inverted claim.** `docs/site/cli-reference.md:246-249` states "`netscript deploy docker` and
+ `deploy compose` exist as command groups but are not wired — they only print help." Both are
+ implemented by `AspireComposeDeployTarget`, whose operation list is
+ `plan · up · down · status · logs`
+ (`packages/cli/src/kernel/adapters/aspire/aspire-compose-deploy-target.ts:65-70,92,109-117`).
+8. **Fabricated aliases.** `@contracts` (`web-layer/query.md:143`, `services-sdk/sdk.md:100`,
+ `tutorials/live-dashboard/02-contract-to-service.md:112`, `.../03-sdk-cache-first-query.md:55`)
+ and `@/lib/api-clients.ts` (`services-sdk/sdk.md:189,194,199`) are never generated; the scaffold
+ emits only `'@app/' -> './'` and `'@/contracts'`
+ (`packages/cli/src/kernel/adapters/templates/app/generate-app-deno-json.ts:62-63,125-130`).
+9. **Maintainer version.** `netscript-dev` reports a hardcoded `version('1.0.0')`
+ (`packages/cli/src/maintainer/features/root/maintainer-command-tree.ts:32`) against the real
+ `CLI_PACKAGE_VERSION` train.
+
+## Current surface
+
+32 reference pages for a larger publish set; a first-publish-only docs gate keyed to a path
+convention the IA does not follow; 26 READMEs frozen at 2026-07-18, one of which is the SDK's JSR
+landing page teaching a non-canonical dialect; a root README with a hardcoded file count and a
+literal version placeholder; seven verbs with no prose; one reference claim that is the inverse of
+the code.
+
+## Target contract
+
+1. **Coverage.** Every package whose `deno.json` `publish` key is not `false` has a
+ `docs/site/reference//index.md`, generated or hand-written, listed in the site nav.
+2. **One path convention, written down.** Either the gate's `packageSegment` rule is relaxed to a
+ declared alias map (`@netscript/plugin-sagas` → `sagas`), or the IA moves to the gate's rule. The
+ decision is recorded in the reference index; the gate and the IA agree afterwards.
+3. **The gate runs over the whole publish set**, not only first-publish packages, so a new package
+ without a page and an existing package without a page fail the same way.
+4. **READMEs match the ratified dialect.** `packages/sdk/README.md` leads with
+ `createQueryFactories` per T5-01; every package README's code blocks are covered by T5-02's
+ compile gate; `docs:readme:check` staleness is #767's problem and is not re-litigated here.
+5. **Root README truth.** The file count becomes "the scaffold prints its own count" and the install
+ snippet derives its specifier the way the site does, or states explicitly that the reader should
+ substitute the current release.
+6. **Verb coverage.** `docs/site/reference/cli/commands.md` documents `agent drift`, `plugin ai`,
+ `deploy desktop`, `deploy package-cli`, `deploy list`, `config list`, and corrects the
+ docker/compose row and the "three-verb lifecycle" claim.
+7. **Aliases.** Every sample uses the two aliases the scaffold generates.
+
+## Acceptance
+
+- [ ] Every package with a non-`false` `publish` key has a reference page.
+- [ ] `plugin-sagas-core`, `plugin-streams-core`, `plugin-triggers-core`, `plugin-workers-core` have
+ reference pages.
+- [ ] The reference path convention is stated once and the publish gate matches it.
+- [ ] The docs-reference check runs over the full publish set, not only first-publish packages.
+- [ ] `packages/sdk/README.md` leads with `createQueryFactories`.
+- [ ] `README.md` carries no hardcoded scaffold file or directory count.
+- [ ] `README.md`'s install snippet resolves to a real specifier or states the substitution.
+- [ ] `agent drift`, `plugin ai`, `deploy desktop`, `deploy package-cli`, `deploy list`, and
+ `config list` are documented in the CLI reference.
+- [ ] The docker/compose "not wired" claim is replaced by the real five-verb surface.
+- [ ] `@contracts` and `@/lib/...` appear in zero code samples.
+- [ ] Negative test: adding a publishable package without a reference page fails the docs gate.
+- [ ] Negative test: a CLI command group present in the public tree but absent from the command
+ reference fails a check derived from the command tree, not from a literal list.
+- [ ] [post-merge] The published JSR landing page for `@netscript/sdk` shows the canonical dialect.
+
+## Boundaries
+
+- **#1108** owns verifying generated package references against live export maps — the *content*
+ correctness of reference pages and the expansion of `check-exports-drift`. This issue owns
+ *existence*, path convention, and gate scope; do not re-file #1108's export verification here.
+- **#767** owns `docs:readme:check` being a dead gate (checker/template/house-style divergence). This
+ issue fixes README *content*, not the README standard checker.
+- **#1201** owns serving export surfaces through MCP; **#1260** owns SDK prose in the MCP corpus.
+ Fixing `packages/sdk/README.md` here does not decide what ships in the corpus.
+- **#1210** owns per-API deep dives; **#1208** owns the tutorial page-builder rewrite. No new
+ conceptual pages here — reference and README truth only.
+- **#1277** owns docs-site layout/UI polish.
+- **T5-01** owns the dialect decision itself; this issue applies it to READMEs and reference prose.
+- Not a goal: authoring per-package tutorials, or renaming published packages.
+
+## Docs/consumer proof
+
+`deno task publish:dry-run` and the release readiness check agree with the site tree for every
+publishable member. A reader running `netscript --help` can find prose for every group it prints.
+The JSR pages for `@netscript/sdk` and the four `plugin-*-core` packages link to a reference page
+that exists.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. README staleness re-measured
+with `git log -1 --format=%ad` per file in this pass (22/30 packages and 4/6 plugins at 2026-07-18);
+the corpus figure of 21/30 is superseded by this measurement.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-01-quality-scan-exported-any-docs-snippets.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-01-quality-scan-exported-any-docs-snippets.md
new file mode 100644
index 0000000000..eec8dbba5d
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-01-quality-scan-exported-any-docs-snippets.md
@@ -0,0 +1,123 @@
+# chore(quality): `quality:scan` cannot see an `any` in an exported type, an unbudgeted allowance, or a docs snippet — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T6-01 · **Proposed milestone:** 0.0.6 · **Labels:** `type:chore` `area:tooling`
+`area:packages` `area:docs` `priority:p1` `status:triage` · **Depends on:** none (Part of #1278 —
+this is Inventory C filed as its own trackable child)
+
+## Summary
+
+#1278 Inventory C asks for a fail-closed gate on new `as any` / `as unknown as` / `@ts-ignore`
+"outside an allowlist that requires a linked issue id", covering docs snippets and not only source.
+None of those three properties exists today. `quality:scan` is a line-regex scanner whose
+`explicit-any` rule cannot distinguish an exported type from a local one, whose `quality-allow:`
+suppression accepts any free-text reason with no issue id and no budget, and whose file filter never
+opens a Markdown file. The type-soundness selling point is therefore defended by a gate that a
+single trailing comment disables. Now: #1278 is milestoned 0.0.6 and its other inventories are
+nearly discharged (see Evidence), so the guard rail is the remaining durable value.
+
+## Evidence
+
+Executed at baseline `fac9e339042c` on 2026-08-08; corpus refs
+`research/repo-audit/web-layer.md` §11, `research/github-board-open.md` §6.4 (#1278 Inventory C/D,
+#1276 tranche T6).
+
+- **Scope.** `.llm/tools/quality/scan-code-quality.ts:18` — `DEFAULT_ROOTS =
+ ['packages/cli/src', 'plugins']`. `deno.json:50-52` defines `quality:scan` (defaults),
+ `quality:scan:repo` (`--root packages --root plugins`), `quality:gate`.
+ **Corpus correction:** #1276 T6 states `quality:scan` "covers `packages/cli/src` + `plugins`
+ only". That is true of the *default* task; `quality:scan:repo` already covers all of
+ `packages/` + `plugins/` and runs on push-to-main and a Monday 07:17 UTC cron
+ (`.github/workflows/code-quality.yml:50-59`). The PR gate scans only changed files
+ (`code-quality.yml:36-42`). Root scope is **not** the gap; rule power and suppression discipline
+ are.
+- **Exported types are invisible.** `scan-code-quality.ts:51` — `explicit-any` is
+ `/(?:<|:\s*)any(?:\s*[,>;)\]}]|\b)/` against a raw line. It has no notion of `export`.
+- **A rule that does know about exports exists and never fails.**
+ `.llm/tools/fitness/check-doctrine.ts:467-484` emits `A1/F-5: 'any' in exported declaration` — but
+ at `WARN` level only, matching only `export function` / `export type` / `export interface` line
+ starts (not `export const`, class members, generic defaults, or re-exports), and only under the
+ 16 roots `arch:check` names (`deno.json:155`).
+- **Allowances are unbudgeted and unlinked.** `scan-code-quality.ts:136` accepts
+ `// quality-allow: `. `--max-allow` exists (`:173-181`) and is passed by
+ **no** task and **no** workflow (grep over `deno.json` + `.github/workflows/`). Executed counts:
+ `deno task quality:scan` → `findings: []`, `allowCount: 7`; `--root packages --root plugins` →
+ `ok: true`, `findings: 0`, `allowCount: 10`. So the repo-wide surface already carries **3
+ allowances beyond the 7 that #1276 ratified**, and nothing reports the delta.
+- **Docs snippets are never opened.** `scan-code-quality.ts:87` — `isScannable` matches only
+ `/\.[cm]?[jt]sx?$/`. No `.md`, and no `.template` (so scaffold templates are unscanned too).
+ `scan-code-quality.ts:47` additionally skips any line beginning with a quote or backtick.
+- **What is actually left in docs (re-measured, contradicts #1278 Inventory A).** The only real
+ hit repo-wide is `docs/site/reference/triggers/index.md:310` `const observedEvents: any[] = [];`
+ with its executable twin `docs/site/reference/triggers/examples_test.ts:65`.
+ `docs/site/web-layer/query-bridge.md` no longer contains `as unknown as`, and `~orpc` /
+ `BaseContractProcedure = Readonly<{ ~orpc: any }>` no longer appears in
+ `docs/site/reference/contracts/index.md` (grep returns nothing at this baseline).
+- **Inventory D is already satisfied and must stay that way.** `scan-code-quality.ts:87` excludes
+ `_test.ts` / `.test.ts` / `.spec.ts`, so soundness tests are exempt by construction. There are
+ **6**, not "~19": `packages/plugin-{workers,sagas,triggers,auth,ai}-core/tests/contracts/
+ *-contract-soundness_test.ts` and `plugins/workers/services/src/routers/health-soundness_test.ts`.
+
+## Current surface
+
+`quality:scan` reports five rules (`explicit-any-ignore`, `unsafe-cast`, `explicit-any`,
+`plugin-name-check`, `ts-error-suppression`), all line-regex, over `.ts/.tsx/.js/.jsx` non-test,
+non-`.generated.ts` files. Any finding is silenced by appending `// quality-allow: `. The scan
+is green today at 0 findings / 7 default / 10 repo-wide allowances. `check-doctrine.ts` carries a
+separate, warn-only, export-aware `any` rule over a hand-listed 16-root subset.
+
+## Target contract
+
+One gate, three added properties, no third scanner:
+
+1. **Export-aware severity.** An `any` reachable from a package's published entrypoints is a
+ failure, not a warning. Reuse and generalize `check-doctrine.ts:467-484` (or drive it from
+ `deno doc --json` on each package's `exports` map, which already answers "is this symbol
+ published") rather than adding a fourth regex pass. Non-exported `any` keeps its current
+ severity.
+2. **Registered allowances.** `// quality-allow:` requires a linked issue reference (`#`) that
+ is open and milestoned; a reason without one is a finding. The allowance list is budgeted with
+ `--max-allow` wired into `quality:scan` and `quality:scan:repo`, so the count can only fall.
+3. **Docs snippets in scope.** Fenced TypeScript blocks under `docs/site/**` are extracted and
+ scanned by the same rule set; `*_test.ts` companions under `docs/site/**` are treated as docs
+ fixtures, not exempt tests.
+
+Exempt by explicit rule, asserted by test: the 6 `*-soundness_test.ts` files whose
+`@ts-expect-error` lines *are* the assertion.
+
+## Acceptance
+
+- [ ] A new `any` in an exported type fails `quality:scan` on a red-first fixture.
+- [ ] A new `as unknown as` without a linked open issue id fails `quality:scan`.
+- [ ] An `as any` inside a fenced TS block under `docs/site/**` fails `quality:scan`.
+- [ ] The 6 `*-soundness_test.ts` files stay green with their `@ts-expect-error` lines unchanged.
+- [ ] `--max-allow` is wired into `quality:scan` and `quality:scan:repo` at the measured count.
+- [ ] The allowance budget cannot be raised without an accompanying issue link in the same PR.
+- [ ] `docs/site/reference/triggers/index.md:310` and `examples_test.ts:65` are typed, not `any`.
+- [ ] Tests cover: exported vs local `any`, linked vs unlinked allowance, docs fence, soundness-test
+ exemption, and budget overflow.
+- [ ] `gate:` `deno task quality:scan:repo` and `deno task arch:check` are green after the change.
+
+## Boundaries
+
+- Do **not** re-file #1278; this is its Inventory C child and carries `Part of #1278`.
+- Do **not** absorb #1278 Inventory B (the 12 production assertion sites) or #1276's tranches
+ T1–T5 — this issue only builds the rail that keeps them from regrowing.
+- Do **not** touch #1245 (island query types) or #1249 (`controlProps` / Zod 4 constraints); both
+ are `packages/fresh` source defects with their own owners.
+- Do **not** change `arch:check`'s root list here — that is T6-03's `arch:check:repo` closure plan.
+- Do **not** extend `quality:scan` into `packages/fresh-ui` before T6-02 decides its lock policy.
+
+## Docs/consumer proof
+
+The scanned corpus is the published docs corpus: after this lands, no page under `docs/site/**`
+can teach a cast, and the "documented cast is a framework bug with a paragraph attached" claim in
+#1278 becomes machine-enforced rather than editorial. The consumer-visible proof is that
+`docs/site/reference/triggers/index.md` and its `examples_test.ts` compile with real types, so an
+agent copying the triggers reference no longer inherits `any[]`.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/github-board-open.md` §6.4 and `research/repo-audit/web-layer.md` §11, with every count
+re-measured at `fac9e339042c` because the #1278/#1276 inventories were measured 2026-08-04 and are
+stale in three places (docs items discharged, soundness-test count, allowance count).
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-02-fresh-ui-root-check-lint.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-02-fresh-ui-root-check-lint.md
new file mode 100644
index 0000000000..7c6ef95117
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-02-fresh-ui-root-check-lint.md
@@ -0,0 +1,120 @@
+# chore(ci): `packages/fresh-ui` is excluded from root check and lint, runs in no workflow, and its own check task rewrites its lock — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T6-02 · **Proposed milestone:** 0.0.6 · **Labels:** `type:chore` `area:tooling`
+`area:fresh-ui` `priority:p1` `status:triage` · **Depends on:** none (T6-01 must not extend
+`quality:scan` into `packages/fresh-ui` until this issue settles the lock policy)
+
+## Summary
+
+`packages/fresh-ui` is a published `@netscript/fresh-ui` package with 6 subpaths and a 66-item
+registry, and it is the surface the scaffold's `/design` routes and every `ui:add` slice consume.
+It is excluded from the root `check` task, excluded from the root `lint` task, and named by no step
+in any workflow — so no CI job type-checks or lints it. It is not broken: its own `check` task
+passes today. It is ungated, which means the next change to it is the one that breaks silently.
+The reason it was excluded is real and must be solved, not bypassed: it carries its own
+`deno.lock`, and running its check **mutates that lock**.
+
+## Area
+
+fresh-ui / tooling
+
+## Reproduction steps
+
+1. `rtk grep -n "fresh-ui" deno.json` → the name appears in the `check` exclude regex and the
+ `lint` exclude regex.
+2. `rtk grep -rn "fresh-ui" .github/` → hits are `CODEOWNERS:26` and the two issue-template `area`
+ dropdowns only. No workflow step names it.
+3. `md5sum packages/fresh-ui/deno.lock` → record.
+4. `deno task --cwd packages/fresh-ui check` → exits **0**.
+5. `md5sum packages/fresh-ui/deno.lock` → **changed**; `git status --porcelain` reports
+ ` M packages/fresh-ui/deno.lock`.
+6. `git checkout -- packages/fresh-ui/deno.lock` to restore.
+
+Executed 2026-08-08 at `fac9e339042c`: step 4 exit `0`; lock md5 `93b0bf5e…` → `5a83f729…`.
+
+## Evidence
+
+Corpus: `research/repo-audit/web-layer.md` §11.3 and gap register item 12; verified at source.
+
+- `deno.json:34` — root `check` runs `.llm/tools/run-deno-check.ts --root packages --root plugins`
+ with `--exclude "^(packages/(fresh-ui)|…)"`.
+- `deno.json:143` — root `lint` excludes `^(packages/(fresh-ui|cli)|…)`.
+- `.github/workflows/ci.yml` runs the root `check`; nothing runs
+ `deno task --cwd packages/fresh-ui check`.
+- `deno.json` workspace globs are `packages/*`, `packages/cli/e2e`, `plugins/*`, `examples/*`,
+ `apps/*` — so `packages/fresh-ui` **is** a workspace member, yet it ships
+ `packages/fresh-ui/deno.lock` and its tasks pass `--lock=deno.lock`
+ (`packages/fresh-ui/deno.json` tasks `check`, `test`, `tokens:build`).
+- The structural cause of the private lock: `packages/fresh-ui/deno.json` imports resolve the SDK
+ from the **registry**, not the workspace — `"@netscript/sdk/auto-update": "jsr:@netscript/sdk@0.0.4/auto-update"`,
+ `"@netscript/sdk/desktop": "jsr:@netscript/sdk@0.0.4/desktop"`.
+- One fresh-ui artifact is already gated: `deno.json:108` `check:assets-barrel` git-diff-gates
+ `packages/fresh-ui/registry.generated.ts`. The generated barrel is protected; the 80 source files
+ that produce it are not.
+- Consumer stake: `research/repo-audit/web-layer.md` §7.1 — the scaffolded `/design/components`
+ gallery lists 50 of the registry's 66 items with no sync gate. An ungated registry package and an
+ ungated gallery snapshot are the same blind spot at two ends of one seam.
+
+## Current surface
+
+`packages/fresh-ui` type-checks and lints only when a human runs its package-local tasks. Its lock
+is a second lock in a workspace whose other 29 packages share the root lock, and any invocation of
+its `check` rewrites it, so a naive "just delete the exclusion" fix would make every CI run dirty
+the working tree and fail lock-hygiene review.
+
+## Target contract
+
+`packages/fresh-ui` is type-checked and linted by CI on every PR that touches it, with a lock policy
+that is decided explicitly and asserted, choosing one of:
+
+- **(a) Join the root lock.** Drop `packages/fresh-ui/deno.lock`, let the workspace root lock cover
+ it, and repoint `@netscript/sdk/*` at the workspace member. Requires confirming the published-pin
+ is not load-bearing for the registry's consumer story.
+- **(b) Keep the private lock, gate it frozen.** Keep the pins, and run the package check with a
+ frozen-lock flag so a lock rewrite is a CI failure rather than a silent mutation, with the
+ regeneration step named in the failure message.
+
+Either way, the package-local check runs in CI, the lint exclusion is removed or narrowed to a
+named rule set with a reason, and the working tree is clean after the job.
+
+## Acceptance
+
+- [ ] `packages/fresh-ui` is type-checked by a CI job on PRs that touch it.
+- [ ] `packages/fresh-ui` is linted by a CI job on PRs that touch it.
+- [ ] The lock policy is recorded in the PR body as (a) join-root-lock or (b) frozen-private-lock.
+- [ ] Running the new CI step leaves `git status --porcelain` empty.
+- [ ] A lock rewrite during the check fails the job instead of being committed.
+- [ ] A deliberately broken type in `packages/fresh-ui/registry.ts` fails the new job (red-first).
+- [ ] A deliberately introduced lint violation in `packages/fresh-ui` fails the new job.
+- [ ] `deno.json:34` and `deno.json:143` no longer exclude `packages/fresh-ui`, or the remaining
+ exclusion names the specific rule and a linked issue.
+- [ ] Tests cover the frozen-lock failure path if option (b) is chosen.
+- [ ] `gate:` root `deno task check`, `deno task lint`, and `deno task fmt:check` stay green.
+
+## Boundaries
+
+- Do **not** fix the `/design/components` 50-vs-66 registry drift here — that is a separate
+ scaffold-generation defect owned by the T2 pack's `/design` registry sync draft, referenced from
+ #1335's inventory.
+- Do **not** reopen #1328 (closed 2026-08-07): that issue owns the *generated consumer project's*
+ quality gates; this issue owns the *framework repo's* CI coverage of one package.
+- Do **not** duplicate #1335 — the umbrella owns generated-surface conformance, not framework CI
+ wiring.
+- Do **not** widen `quality:scan` roots here; T6-01 owns the quality gate and is blocked on this
+ issue's lock decision.
+- Do **not** remove `packages/cli` from the `lint` exclusion in the same change; it is a separate,
+ larger backlog with its own noise profile.
+
+## Docs/consumer proof
+
+`@netscript/fresh-ui` is published to JSR and consumed by every scaffolded app. After this lands,
+the package's README archetype/permissions claims and its `deno doc` surface are backed by a CI
+type-check, so a consumer reading the registry reference is reading a gated surface. The
+consumer-visible receipt is a CI run link on a PR that touches only `packages/fresh-ui`.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Source:
+`research/repo-audit/web-layer.md` §11.3 ("New issues worth filing … the `packages/fresh-ui` CI
+check/lint exclusion" — no board owner found by the §7 dedup sweep). Lock mutation and green-check
+status re-verified by execution at `fac9e339042c`; the lock was restored with `git checkout --`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-03-doctrine-verdict-refresh.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-03-doctrine-verdict-refresh.md
new file mode 100644
index 0000000000..7f0f214bba
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.6-verification-docs-rfcs/T6-03-doctrine-verdict-refresh.md
@@ -0,0 +1,143 @@
+# docs(doctrine): the codebase verdict table names five deleted packages, omits 14 live units, and its `arch:check:repo` gate has been accepted-red since 2026-06-21 — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T6-03 · **Proposed milestone:** 0.0.6 · **Labels:** `type:docs` `area:docs`
+`area:tooling` `area:packages` `priority:p2` `status:triage` · **Depends on:** none (this is the
+denominator the rest of the remediation train measures against)
+
+## Location
+
+`docs/architecture/doctrine/10-codebase-verdict-and-handoff.md`, `.llm/harness/debt/arch-debt.md`
+(entry "repo doctrine task — full historical scan remains red"), `.llm/tools/fitness/check-doctrine.ts`,
+`deno.json:155-156`, `rfcs/`.
+
+## Kind of issue
+
+Out-of-date content with governance consequences — the doctrine's own definition of done cannot be
+evaluated against the current package set, and its mechanical gate cannot be read as a verdict.
+
+## Summary
+
+The doctrine's per-package verdict table is the roadmap's denominator: `10-…md:197-208` defines
+doctrine completion as "`arch:check` passes for every package … and the codebase walk above shows
+zero Restructure or Rewrite verdicts". That walk names five packages that no longer exist, omits 14
+that do, the promised engineering reference is ~20% written, and the repo-wide gate that would
+adjudicate it has been accepted-red for seven weeks. Now: a remediation roadmap that schedules
+refactor work against this table would schedule work on deleted units and skip the entire auth and
+`plugin-*-core` tiers.
+
+## Details
+
+All counts executed at baseline `fac9e339042c` on 2026-08-08. Corpus:
+`research/repo-audit/scaffold-doctrine.md` §3.1-3.5, divergences D6/D7/D8/D9/D10.
+
+**D6 — verdict table vs reality.** `10-…md:22-51` holds 29 rows.
+- Rows naming units absent from `ls packages/`: `@netscript/shared`, `@netscript/streams`,
+ `@netscript/triggers`, `@netscript/workers`, `@netscript/sagas`. Row naming a unit absent from
+ `ls plugins/`: `plugins/hello-world`. (Four of the five packages were plausibly renamed into the
+ `plugin-*-core` tier; the re-walk must record rename-vs-deletion per row, not assume.)
+ Note `@netscript/shared` also has a dedicated doctrine subsection at `06-archetypes.md:378-388`
+ and is remediation priority #3 at `10-…md:46,67-69`.
+- Live units: **30** dirs under `packages/` + **6** under `plugins/` = 36. Units with **no row**:
+ `ai`, `auth-better-auth`, `auth-kv-oauth`, `auth-workos`, `bench`, `mcp`, `plugin-ai-core`,
+ `plugin-auth-core`, `plugin-sagas-core`, `plugin-streams-core`, `plugin-triggers-core`,
+ `plugin-workers-core`, `plugins/ai`, `plugins/auth` — **14**, including the whole auth family and
+ the entire `plugin-*-core` tier that `11-plugin-thinness-and-base-seams.md` is built around.
+- The same staleness reaches the archetype assignment table at `06-archetypes.md:368-381`.
+
+**D7 — engineering reference.** `10-…md:79-181` specifies ten required contents. §7 (debt registry)
+exists as `.llm/harness/debt/arch-debt.md`. §6 (fitness-function source) is partial:
+`.llm/tools/fitness/` contains exactly 5 files (`audit-jsr-package.ts`, `check-doctrine.ts`,
+`check-ds-color-utilities.ts`, `check-ds-no-raw-hex.ts`, `check-ds-gates_test.ts`). §1–§5 and
+§8–§10 — archetype recipes, role-folder recipes, pattern skeletons, per-anti-pattern fix catalogue,
+refactor playbooks, review checklist, glossary, phased roadmap — do not exist.
+
+**D8 — the accepted-red gate, decomposed.** `deno task arch:check` exits **0**; it iterates 16
+hand-listed roots (`deno.json:155`) out of 36 live units, so **20 live units have no doctrine gate
+at all**. `deno task arch:check:repo` exits **1** with `FAIL=53 WARN=341 INFO=1`. The 53 failures
+decompose into exactly two mechanical causes, neither of which is package debt:
+- **52 × `FAIL A14: Jest/Vitest globals`** are false positives. `check-doctrine.ts:407` matches a
+ bare `describe(` / `it(` / `expect(` anywhere in a `*_test.ts` file without checking where the
+ identifier came from. Example: `packages/database/tests/migrate-retry_test.ts:10` is
+ `import { describe, it } from 'jsr:@std/testing@^1/bdd';` — the sanctioned Deno BDD API.
+- **1 × `FAIL A1: mod.ts missing`** is structural. `deno.json:156` runs `check-doctrine.ts` with no
+ `--root`, so the checker evaluates the repository root as a single package
+ (`check-doctrine.ts:110-113`) and walks trees that are not packages: 16 of the cited findings are
+ under `.llm/tmp/eis-chat/…`, plus `docs/site/` and `.llm/tools/`.
+The `arch-debt.md` entry recording this is Created **2026-06-21**, `Status: open, DEBT_ACCEPTED`,
+Target "2026-Q3", closing gate "reduce unrelated root failures **or** replace the legacy root scan
+with debt-aware package selection". The decomposition above shows the second branch is the cheap
+one.
+
+**D9/D10 — RFC practice divergence.** `ls rfcs/` → `0000-template.md`, `README.md`. Zero numbered
+RFCs have ever landed. Real design records live at `.llm/runs/plan-*--seed/design/canonical/`,
+produced by merged PRs #891 (deploy plugin family) and #1123 (OpenAPI→MCP) and cited by
+`.github/labels.yml` label descriptions as "RFC #891" / "RFC #1123". `rfcs/README.md:82-86`
+self-flags as provisional, deferring to "a ratified doctrine governance statement" that does not
+exist in `docs/architecture/doctrine/`. Meanwhile `arch-debt.md` carries 5 `DECISION_PENDING`
+entries, 4 of them public-surface questions that `rfcs/README.md:15-24` says *require* an RFC:
+`CRON-SUBSYSTEM-DUP` (1536), `RUN-ARTIFACT-ARCHIVAL-POLICY` (1582), `PAGEBUILDER-LEGACY-COMPAT-TREE`
+(1598), `FORMPAGEPROPS-PLAYGROUND-MIGRATION` (1613), `REDIS-LEGACY-VALUE-FALLBACK` (1628). The
+archival-policy entry proposes pruning the very tree where the de-facto RFCs live.
+
+## Target contract
+
+1. The verdict table enumerates every live unit under `packages/` and `plugins/` and nothing else,
+ with rename-vs-deletion recorded for each removed row.
+2. `arch:check:repo` is a real verdict: it iterates live workspace members rather than treating the
+ repository root as a package, and its A14 rule does not fire on `@std/testing/bdd` imports.
+ Whatever residue remains is either green or listed as named debt entries with owners.
+3. The `arch-debt.md` accepted-red entry either closes or states a dated closure plan naming the two
+ causes above.
+4. The engineering reference has a written, dated plan for §1–§5/§8–§10 — authored *from* the
+ refactors as a byproduct, not as a separate project.
+5. The RFC divergence is recorded and resolved one way: either `.llm/runs/*/design/canonical/`
+ bundles are promoted to numbered `rfcs/NNNN-*.md` at acceptance, or `rfcs/README.md` is retired
+ and the harness path is named as canonical.
+
+## Acceptance
+
+- [ ] The verdict table lists all 36 live units and no deleted ones.
+- [ ] Each removed row is recorded as renamed (with its new name) or deleted.
+- [ ] `06-archetypes.md` archetype assignment table matches the refreshed verdict table.
+- [ ] `arch:check:repo` iterates live workspace members instead of the repository root.
+- [ ] The A14 rule does not fire on a test importing `describe`/`it` from `@std/testing/bdd`.
+- [ ] `arch:check:repo` no longer walks `.llm/tmp/`, `docs/`, or `.llm/tools/`.
+- [ ] The `arch-debt.md` accepted-red entry is closed or carries a dated closure plan.
+- [ ] The doctrine records which of the 36 units `arch:check` gates and why any are excluded.
+- [ ] The engineering-reference gap (§1–§5, §8–§10) is recorded as a dated plan, not silence.
+- [ ] The RFC-location divergence is resolved in `rfcs/README.md` with the 5 `DECISION_PENDING`
+ entries mapped to the chosen location.
+- [ ] A test fails if the verdict table names a directory that does not exist.
+- [ ] A test fails if a live `packages/*` or `plugins/*` directory has no verdict row.
+- [ ] `gate:` `deno task arch:check` stays green and `deno task arch:check:repo` exits 0 or its
+ residue is enumerated in `arch-debt.md`.
+
+## Boundaries
+
+- Do **not** perform the six open verdict-Refactor/Restructure refactors here (`packages/database`,
+ `packages/kv`, `packages/service`, `packages/workers`, `plugins/triggers`, `plugins/workers`) —
+ this issue re-establishes the denominator; the refactors are separate slices.
+- Do **not** file the five `DECISION_PENDING` RFCs here; this issue only records where RFCs live.
+ The generated-workspace governance RFC (D5) is a separate T3/T8 item.
+- Do **not** duplicate #1093 — plugin-discovery hardcoding is its own defect with its own doctrine
+ check requirement.
+- Do **not** touch #1280 (`status:blocked` upstream) or #1320 (blocked on `@ag-ui/core`).
+- Do **not** re-file #232 or #301 (docs/stable umbrellas); this is a doctrine-document refresh, not
+ a docs program.
+
+## Docs/consumer proof
+
+`docs/architecture/doctrine/` is the published governance surface every framework contributor and
+every harnessed agent reads via `.agents/skills/netscript-doctrine`. The consumer proof is that a
+contributor running `deno task arch:check:repo` gets a verdict they can act on rather than 53
+failures they must learn to ignore — and that the doctrine skill's routing no longer points at rows
+for packages that do not exist.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Source:
+`research/repo-audit/scaffold-doctrine.md` §3.1-3.5 and divergence rows D6–D10; §6 Phase A names the
+verdict re-walk as the roadmap's denominator. The `arch:check:repo` failure decomposition (52 A14
+false positives + 1 root-as-package A1) is new to this draft — executed at `fac9e339042c`, not
+present in the corpus, and it converts the seven-week-old "reduce unrelated root failures" gate text
+into two named, cheap fixes.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-02-orpc-seam-re-exposure.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-02-orpc-seam-re-exposure.md
new file mode 100644
index 0000000000..6c92f7b53a
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-02-orpc-seam-re-exposure.md
@@ -0,0 +1,157 @@
+# feat(sdk): createServiceClient seals every oRPC link seam — headers, interceptors, plugins, fetch and the link itself are unreachable from the supported API — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T1-02 · **Proposed milestone:** `0.0.7` (post-rename-shift "Typed seams +
+generation", SYNTHESIS §5.3) · **Labels:** `type:feat` `area:sdk` `area:service` `area:plugins`
+`priority:p1` `status:triage` · **Depends on:** T1-01 (RFC-A ratification), T1-04 (transport-policy
+consolidation must land first or concurrently)
+
+## Summary
+
+`CreateServiceClientOptions` is a closed nine-field record and `ServiceClientContext` is a closed
+interface, so no consumer can add a header, an interceptor, a link plugin, a custom `fetch`, or a
+typed per-call context field. The link factory that would let them work around it is
+package-private, even though the ports module's own doc comment advertises "the transport seam". On
+the server side `RPCHandlerConfig.plugins` exists but the builder never populates it, and the plugin
+type it accepts hands `unknown` to plugin authors. The result is that the only supported way to send
+one extra header is to fork ~90 lines of framework internals per app — and silently lose NetScript's
+client span in the process.
+
+## Evidence
+
+- Corpus: `research/repo-audit/services-sdk.md` §2.1–§2.4, §3.1–§3.3, §3.6, gap register
+ S4/S11/S12/S13/S21; `research/external/orpc.md` §4 (G2, G3, G6, G7), §5;
+ `research/repo-audit/auth.md` §0 proof 1–2, §2.
+- Source at baseline `fac9e339042c` (re-verified for this draft):
+ - `packages/sdk/src/ports/service-client.ts:203-222` — nine fields:
+ `contract, serviceName, routerName, protocol, apiPath, apiVersion, port, timeout,
+ propagateTraceContext`. No `headers` / `fetch` / `interceptors` / `plugins` / `link` / context
+ parameter.
+ - `packages/sdk/src/ports/service-client.ts:129-155` — `ServiceClientContext` is a concrete
+ interface (`signal`, `cache`, retry knobs, `traceHeaders`); `:160-171` hard-codes it into
+ `ServiceClientMethod`.
+ - `packages/sdk/src/client/service-client.ts:41-49` — `port` and `timeout` are never destructured;
+ `packages/sdk/src/presets/define-services.ts:106-116` forwards both into that ignoring function,
+ so the dead options look live at L2 and L3.
+ - `packages/sdk/src/client/http-client-link.ts:82` (`method:` literal), `:82-101` (`headers`
+ literal: `Content-Type` + optional trace), `:102-126` (`plugins` literal:
+ `ClientRetryPlugin{retry:0}` + `DedupeRequestsPlugin` with frozen filter/groups), `:127+`
+ (hard-coded `fetch` carrying the CLIENT span).
+ - `packages/sdk/src/client/mod.ts:15-36` — exports only `createServiceClient`, `isDefinedError`,
+ `safe`, and types. `createHttpClientLink` is private.
+ - `packages/sdk/src/ports/mod.ts:7` — "…discovery metadata, and the transport seam" — while
+ `packages/sdk/src/ports/client-link-factory.ts:18-25` (`ClientLinkPort`,
+ `ClientLinkCallOptions`) is not exported from that module.
+ - `packages/service/src/primitives/handlers.ts:41-58` — `RPCHandlerConfig` declares `plugins`,
+ `tracing`, `errorHandling`, `deduplication`, `warnOnlyCodes`, `logging`, `debug`;
+ `packages/service/src/builder/service-rpc.ts:57` calls
+ `createRPCHandler(options?.rpcRouter ?? router, { serviceName, debug })` — nothing else is ever
+ passed.
+ - `packages/service/src/types.ts:216-225` — `ServiceHandlerPlugin.init?(options: unknown, router:
+ unknown)`; `packages/logger/orpc-plugin.ts:11-42` redeclares shim option types because of it.
+ - `packages/plugin/src/config/domain/plugin-contributions.ts:12-39` — no client-side contribution
+ group.
+- Upstream (pinned 1.14.6, surface identical to 1.14.15 per `research/external/orpc.md` §0):
+ `StandardLinkOptions` (`interceptors`, `clientInterceptors`, `plugins`),
+ `LinkFetchClientOptions` (`fetch`, `adapterInterceptors`, `plugins`),
+ `StandardRPCLinkCodecOptions` (`headers`, `method`, `fallbackMethod`, `maxUrlLength`,
+ `customJsonSerializers`), `ClientContext` as a free type parameter.
+
+## Current surface
+
+One constructor with no seams. `createServiceClient` builds `rpcPath`, calls the private
+`createHttpClientLink`, and wraps the result in `createORPCClient`
+(`packages/sdk/src/client/service-client.ts:41-66`). Consumers get exactly what that function
+decided: two frozen client plugins, one header set, one `fetch`. `@netscript/sdk/desktop` proves the
+link is swappable in principle (`packages/sdk/src/desktop/application/desktop-rpc-client.ts:18-20`
+builds a MessagePort `RPCLink` with `customJsonSerializers`) and equally closed in practice. On the
+server, `withRPC()` (`packages/service/src/builder/service-builder.ts:91-104`) and
+`DefineServiceOptions` (`packages/service/src/presets/define-service.ts:112-143`) accept no
+`plugins`, so a plugin that wants a server oRPC plugin must drop to L1 and hand-mount into Hono.
+
+## Target contract
+
+Per RFC-A §3.1–§3.5, §3.9, §3.10:
+
+1. **Client construction opens.** `CreateServiceClientOptions` gains an optional,
+ ordered `with?: TWith` contribution chain and a mutually exclusive `link?: ClientLinkPort<…>`
+ escape hatch. Omitting both yields byte-identical behaviour and types to today.
+2. **Context becomes a parameter.** `ServiceClient` and
+ `ServiceClientMethod`; `BaseServiceClientContext` is today's
+ shape; `ServiceClientContext` survives one minor as a `@deprecated` alias.
+3. **The transport seam is exported.** `createHttpClientLink` from `@netscript/sdk/client`;
+ `ClientLinkPort` / `ClientLinkCallOptions` from `@netscript/sdk/ports` — closing the
+ doc-vs-export contradiction at `packages/sdk/src/ports/mod.ts:7`.
+4. **Framework defaults become contributions.** Retry, dedupe, and the CLIENT span are composed
+ through the same public path, so there is no private fast lane.
+5. **Dead options die.** `port` and `timeout` are removed from `CreateServiceClientOptions` and
+ `DefineServiceConfig`; `timeout` is re-expressed as a contribution over `AbortSignal.timeout`.
+6. **Server reachability + typing.** `withRPC()` and `DefineServiceOptions` accept `plugins` and
+ `warnOnlyCodes` and pass them to `createRPCHandler`; `ServiceHandlerPlugin` is typed to upstream's
+ `init?(options: StandardHandlerOptions, router: Router)`; the never-read
+ `deduplication` option is either wired or removed.
+7. **Env boundary.** Contribution `environment` is checked at construction; `server`-only in a
+ browser build throws.
+
+## Acceptance
+
+- [ ] `createServiceClient` accepts an ordered contribution chain and composes headers, interceptors,
+ client interceptors, adapter interceptors, link plugins and `fetch` from it.
+- [ ] `createServiceClient` called without a chain produces the same request on the wire as today.
+- [ ] A type fixture proves an existing nine-field call site compiles unchanged after the change.
+- [ ] `ServiceClient` and `ServiceClientMethod` carry a client-context type parameter with
+ `BaseServiceClientContext` as the default.
+- [ ] `createHttpClientLink`, `ClientLinkPort` and `ClientLinkCallOptions` are exported from their
+ documented subpaths.
+- [ ] Retry, dedupe and the CLIENT span are composed through the public contribution path, not
+ inline literals.
+- [ ] `port` and `timeout` are removed from the client and `defineServices` option records.
+- [ ] `withRPC()` and `defineService()` forward `plugins` and `warnOnlyCodes` to
+ `createRPCHandler`, proven by a server test asserting a custom plugin's `init` ran.
+- [ ] `ServiceHandlerPlugin.init` is typed to upstream's `StandardHandlerOptions`, and
+ `packages/logger/orpc-plugin.ts` deletes its shim option types.
+- [ ] `RPCHandlerConfig.deduplication` is either honoured by `createRPCPlugins` or removed.
+- [ ] NEGATIVE: a test asserts a request carries no contribution-supplied header when the
+ contribution is absent from the chain.
+- [ ] NEGATIVE: a test asserts two contributions declaring the same header key fail at construction
+ with both names in the message.
+- [ ] NEGATIVE: a test asserts two contributions supplying `fetch` fail at construction.
+- [ ] NEGATIVE: a type fixture asserts a contribution built against a future envelope version is not
+ assignable to the current host.
+- [ ] NEGATIVE: a test asserts a `server`-only contribution throws when constructed in a browser-like
+ environment.
+- [ ] `gate:` `deno task publish:dry-run` passes for `@netscript/sdk` and `@netscript/service` with
+ `--isolatedDeclarations` intact.
+- [ ] `gate:` `deno task check` and `deno task test` pass at the repo root.
+
+## Boundaries
+
+- Do **not** implement the auth contribution here — that is T1-05.
+- Do **not** move trace-context propagation onto the chain here — that is T1-06.
+- Do **not** repair `safe`/`isDefinedError` or `baseContract` here — that is T1-03.
+- Do **not** change HTTP method inference or GET dedupe policy semantics here — that is T1-04.
+- Do **not** duplicate **#451** (in-process link-mode adapter) — this issue creates the public link
+ seam #451 needs; #451 stays open and separate.
+- Do **not** duplicate **#1093** (discovery hardcodes official plugin factories). If a
+ `PluginContributions` client group is added here rather than in T1-05, it must not use a closed
+ literal union like `plugin-contributions.ts:16`.
+- Do **not** duplicate **#928** / **#934** (frontend contribution contracts, deny-by-default
+ gateway).
+- Do **not** touch the query-key algebra, `createQueryFactory`'s dropped abort signal, or nested
+ routers — T2 generation pack.
+- Do **not** touch `PluginContractRouter = object` or the Hono-vs-oRPC middleware seam — T3.
+
+## Docs/consumer proof
+
+`docs/site/services-sdk/sdk.md` and `docs/site/reference/sdk/index.md` gain one worked contribution
+example that compiles under the docs gate; `packages/sdk/README.md`'s export table lists the newly
+exported transport seam; and the "escape hatch = fork the link" paragraph is deleted rather than
+softened. Consumer proof is that an app can add one header without importing anything from
+`@netscript/telemetry` and without losing its client span — assert the span is still emitted in the
+same test that asserts the header.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/services-sdk.md` (S4, S11, S12, S13, S21), `research/external/orpc.md` (G2, G3,
+G6, G7) and `research/repo-audit/auth.md` (G1); all cited lines re-verified against worktree baseline
+`fac9e339042c`. No GitHub mutation was performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-03-typed-error-repair.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-03-typed-error-repair.md
new file mode 100644
index 0000000000..7901cad9c8
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-03-typed-error-repair.md
@@ -0,0 +1,134 @@
+# fix(sdk): safe() drops the contract error type — isDefinedError narrows to never and the published docs example does not compile — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T1-03 · **Proposed milestone:** `0.0.7` (post-rename-shift "Typed seams +
+generation", SYNTHESIS §5.3) · **Labels:** `type:fix` `area:sdk` `area:contracts` `area:docs`
+`priority:p1` `status:triage` · **Depends on:** T1-01 (RFC-A ratification, §3.7); independent of
+T1-02 and can land first
+
+## Summary
+
+NetScript re-implements oRPC's `safe()` and `isDefinedError()` without the `TError` generic, so the
+error channel is `unknown`, `Extract` evaluates to `never`, and reading
+`error.code` after a successful `isDefinedError` narrow is a type error. This is proven by an
+executed `deno check`, and it is the exact snippet the published docs tell users to write
+(`docs/site/services-sdk/sdk.md:199`). A contributing cause sits one layer down: `baseContract` is
+annotated `ReturnType`, which instantiates the generic at its constraint and erases
+the six declared error codes to the open `ErrorMap` index signature — so even a correct `safe()`
+could not narrow to NetScript's error vocabulary today. oRPC's headline typed-error DX is fully lost
+while three published documents assert it works.
+
+## Evidence
+
+- Corpus: `research/repo-audit/services-sdk.md` §3.4, §3.5, gap register S1/S2/S3;
+ `research/external/orpc.md` §4 (G4) with the executed probe transcript.
+- Source at baseline `fac9e339042c` (re-verified for this draft):
+ - `packages/sdk/src/client/errors.ts:75-77` —
+ `export function isDefinedError(error: T): error is Extract`.
+ - `packages/sdk/src/client/errors.ts:86-92` —
+ `export async function safe(promise: PromiseLike): Promise>`.
+ - `packages/sdk/src/client/errors.ts:49` — `SafeResult`, so `TError`
+ defaults to `unknown` at every call site.
+ - `packages/sdk/src/ports/service-client.ts:160-171` — `ServiceClientMethod` returns
+ `Promise`, discarding upstream's `ClientPromiseResult>`.
+ - `packages/contracts/src/application/contract-primitives.ts:81` —
+ `export const baseContract: ReturnType = oc.errors(commonErrorMap);` (the six
+ codes are declared at `:21-52`); the doc comment at `:54-69` claims the contract is "genuinely
+ typed rather than erased to `any`" — true for input/output, false for the error map.
+ - `docs/site/services-sdk/sdk.md:199` — tab "Safe error narrowing" ships
+ `if (isDefinedError(error)) return { code: error.code, status: error.status };`.
+ - `docs/site/services-sdk/how-to/discover-services.md:138-147` and `:212-224` — the same pattern,
+ with the comment "`error.code` and `error.data` are typed from the contract".
+ - `packages/sdk/tests/readme-doctest_test.ts:36-37` — the guard that should have caught this
+ **re-declares** `safe` and `isDefinedError` with different signatures instead of importing them,
+ so the doctest passes against a fiction.
+- Executed probe (reproduced from `research/repo-audit/services-sdk.md` §8; re-run before filing):
+
+ ```ts
+ import { isDefinedError, safe } from 'packages/sdk/src/client/mod.ts';
+ declare const p: Promise<{ ok: boolean }>;
+ const [error] = await safe(p);
+ if (error && isDefinedError(error)) { const c: string = error.code; }
+ ```
+
+ `deno check --unstable-kv --config deno.json .ts` →
+ `TS2339 [ERROR]: Property 'code' does not exist on type 'never'.`
+
+- Upstream signature (pinned 1.14.6): `safe(promise:
+ ClientPromiseResult): Promise>`.
+
+## Current surface
+
+Three published documents and one README export table advertise contract-typed error narrowing;
+the shipped helpers cannot provide it, and the only test that touches them tests re-declared
+fictions. Consumers who follow the docs get a compile error, and the workaround the compiler pushes
+them toward is `error as { code: string }` — an unsound cast the type-soundness epic (#1278) then
+counts as debt.
+
+## Target contract
+
+Per RFC-A §3.7:
+
+1. `safe` and `isDefinedError` regain the upstream shape, with
+ `SafeResult`'s failure arms discriminating on `isDefined` exactly as upstream
+ does.
+2. `ServiceClientMethod` carries the error channel so `TError` reaches `safe()` from the contract —
+ either by aliasing to oRPC's derived client type or by threading
+ `ErrorFromErrorMap` through the existing structural derivation.
+3. `baseContract`'s annotation preserves the six literal error-map keys. `ReturnType` is replaced with a spelling that is both literal-preserving and
+ `--isolatedDeclarations`-safe, following the precedent already set by `BaseContractRoute` /
+ `BaseContractOutputRoute` (`packages/contracts/src/application/contract-primitives.ts:125-159`).
+4. `packages/sdk/tests/readme-doctest_test.ts` imports the real helpers; the local `declare
+ function` shims are deleted.
+
+## Acceptance
+
+- [ ] `safe` and `isDefinedError` carry the upstream error generic and `SafeResult` discriminates on
+ `isDefined`.
+- [ ] A type fixture proves `error.code` narrows to the contract's declared code union after
+ `isDefinedError`.
+- [ ] `baseContract`'s type preserves the six literal error-map keys.
+- [ ] NEGATIVE: a type fixture asserts a code that is not in the contract's error map is rejected
+ (today `'TOTALLY_MADE_UP_CODE'` is assignable to `keyof` the error map).
+- [ ] NEGATIVE: a type fixture asserts `isDefinedError` does not narrow a non-oRPC thrown value to a
+ defined error.
+- [ ] The docs snippet at `docs/site/services-sdk/sdk.md:199` compiles as written, proven by an
+ executed check rather than by inspection.
+- [ ] The equivalent snippets in `docs/site/services-sdk/how-to/discover-services.md` compile as
+ written.
+- [ ] `packages/sdk/tests/readme-doctest_test.ts` imports `safe` and `isDefinedError` from the
+ package instead of re-declaring them.
+- [ ] `gate:` `deno task publish:dry-run` passes for `@netscript/sdk` and `@netscript/contracts`
+ with `--isolatedDeclarations` intact.
+- [ ] `gate:` `deno task check` and `deno task test` pass at the repo root.
+
+## Boundaries
+
+- Do **not** open the client construction seam here — that is T1-02.
+- Do **not** duplicate **#1263** (`service: generated by-id handler returns 500 {defined:false} for a
+ missing row instead of a defined 404`, `0.0.6`). #1263 is the **server** raising the wrong error;
+ this issue is the **client** being unable to narrow a correctly-raised one. Both should be true
+ before the docs example is honest end to end — cross-reference, do not merge.
+- Do **not** duplicate **#1278** (type soundness ratification, `0.0.6`). #1278's inventory is
+ prose-only; **read its body before filing**. If it already lists `safe`/`isDefinedError` or the
+ `baseContract` widening, file this as a child with `Part of #1278` instead of standalone. Either
+ way, do not turn this issue into a soundness sweep — it fixes two symbols and one annotation.
+- Do **not** duplicate **#1296** (`contracts/ai` source-side rows) or **#1108** (generated package
+ references vs live export maps).
+- Do **not** rewrite the SDK docs page wholesale — the Tier-1 docs rewrite is the T5 pack. This issue
+ changes only what is needed to make the existing snippets true.
+- Do **not** extend the error map with new codes.
+
+## Docs/consumer proof
+
+The proof is executable: the two published snippets compile unmodified, and the README doctest
+exercises the real exports. Consumer-side, a scaffolded service-to-service call can branch on a
+contract error without a cast — demonstrate it in the fixture so the pattern is greppable, and state
+in the SDK reference that `error.data` is typed from the contract's Zod schema.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/services-sdk.md` (S1, S2, S3) and `research/external/orpc.md` (G4), both of
+which carry executed `deno check` transcripts; all cited lines re-verified against worktree baseline
+`fac9e339042c`. No GitHub mutation was performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-04-transport-policy-consolidation.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-04-transport-policy-consolidation.md
new file mode 100644
index 0000000000..e26c95be1f
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-04-transport-policy-consolidation.md
@@ -0,0 +1,125 @@
+# refactor(sdk): HTTP method and GET-cache policy are decided inline inside the link — oRPC v2's POST-only default would break every client call path — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T1-04 · **Proposed milestone:** `0.0.7` (post-rename-shift "Typed seams +
+generation", SYNTHESIS §5.3) · **Labels:** `type:refactor` `area:sdk` `area:contracts` `area:deps`
+`priority:p1` `status:triage` · **Depends on:** T1-01 (RFC-A ratification, §3.11); must land before
+or with T1-02
+
+## Summary
+
+NetScript's HTTP transport policy — which procedures are GET, and which requests are deduplicated
+and cached — is three literals sitting inside `createHttpClientLink`. oRPC v2 is in public beta and
+its `main` branch already carries `feat(rpc): restrict RPC handlers to POST, PUT, PATCH and DELETE by
+default` plus a `MethodOverrideHandlerPlugin`. Once T1-02 makes the link publicly composable, that
+policy becomes observable to every contribution and every app, and the v2 migration stops being a
+one-function change. Consolidating it behind one NetScript-owned function now is cheap; doing it
+after a public extension seam exists is not.
+
+## Evidence
+
+- Corpus: `research/external/orpc.md` §0 (v2 `beta` dist-tag `2.0.0-beta.25`; the two v2 commits
+ verified absent from the 1.14.15 tarball), §6 "Upgrade implications" — *"Plan the remediation so
+ the transport policy lives behind one NetScript-owned function, not scattered across link
+ construction — otherwise the v2 migration touches every client call path"*;
+ `research/repo-audit/services-sdk.md` §0, §3.1, gap register S21/S22.
+- Source at baseline `fac9e339042c` (re-verified for this draft):
+ - `packages/sdk/src/client/http-client-link.ts:17,82` — `inferRPCMethodFromContractRouter` is
+ imported from `@orpc/client` and applied inline as `method:`. Repo-wide, `grep -rn
+ 'inferRPCMethodFromContractRouter\|StrictGetMethodPlugin\|fallbackMethod' packages plugins`
+ returns **only** these two lines — there is no owned policy function anywhere.
+ - `packages/sdk/src/client/http-client-link.ts:109` —
+ `filter: ({ request }) => request.method === 'GET'`, i.e. the dedupe policy re-derives the method
+ decision from the wire instead of from the contract.
+ - `packages/sdk/src/client/http-client-link.ts:110-125` — the `force-cache` group condition is a
+ second, independent literal keyed off `context?.cache`.
+ - `packages/sdk/src/client/http-client-link.ts:103-107` — `ClientRetryPlugin` default frozen at
+ `retry: 0`; `research/repo-audit/services-sdk.md` §3.2 lists the three upstream client plugins
+ (`BatchLinkPlugin`, `RetryAfterPlugin`, `SimpleCsrfProtectionLinkPlugin`) that are unreachable.
+ - `packages/sdk/src/desktop/application/desktop-rpc-client.ts:18-20` — the second transport makes
+ its own independent decisions (`customJsonSerializers`), so there is already policy divergence
+ across two links with no shared source of truth.
+ - Version state: `deno.json:215-221` pins `^1.14.6` (`@orpc/otel` `^1.14.7`); `deno.lock` resolves
+ `1.14.6`, which drags `@orpc/shared@1.14.6` **and** `@orpc/shared@1.14.7` into one graph — a
+ known `instanceof ORPCError` hazard the pinned client already ships a `Symbol.hasInstance`
+ workaround for (`research/external/orpc.md` §6).
+
+## Current surface
+
+Three coupled decisions with no owner: the contract-derived method, the GET-only dedupe filter, and
+the `force-cache` grouping. They are consistent today only because one function wrote all three. A
+consumer reading the SDK cannot discover the policy without reading the link body, and nothing
+prevents the desktop link, a future in-process link (#451), or a contribution from disagreeing with
+it. `deno task deps:latest` reports all seven oRPC packages at `1.14.6/1.14.7 → 1.14.15`, and the
+1.14.6 → 1.14.15 public export list for `@orpc/server` is byte-identical, so the bump itself is
+mechanical.
+
+## Target contract
+
+Per RFC-A §3.11:
+
+1. One exported-internal function — e.g. `resolveTransportPolicy(contract, options)` — returns the
+ full policy object: `method`, `fallbackMethod`, `maxUrlLength`, the dedupe predicate, and the
+ cache-group descriptors. Every link (HTTP, desktop, and any future one) consumes it; no link
+ re-derives policy from `request.method`.
+2. The policy is derived from the **contract and procedure metadata**, not from the wire. When
+ `NetScriptProcedureMeta.policy.cache` is present (RFC-A §3.6) it is an input to the policy
+ function, not a second mechanism.
+3. **Contributions never observe the HTTP method.** They observe procedure path, input, context and
+ metadata. This is the forward-compat rule that keeps the v2 migration inside one function.
+4. A documented policy-override point exists for the v2 transition (a single `transportPolicy?`
+ option, resolved before contributions compose), so `MethodOverrideHandlerPlugin`-style adaptation
+ is a config change rather than a code change.
+5. oRPC is bumped `1.14.6 → 1.14.15` and the duplicated `@orpc/shared` copies collapse to one,
+ using the `.llm/tools/deps/` wrappers rather than hand-rolled registry reads.
+
+## Acceptance
+
+- [ ] One NetScript-owned function returns the complete client transport policy for a contract.
+- [ ] `createHttpClientLink` and the desktop link both consume that function; neither contains a
+ method or cache literal.
+- [ ] The dedupe predicate is derived from the resolved policy, not from `request.method`.
+- [ ] Tests cover GET-inferred, POST-inferred, and metadata-overridden procedures against the policy
+ function directly.
+- [ ] NEGATIVE: a test asserts a contribution cannot read or alter the resolved HTTP method.
+- [ ] NEGATIVE: a test pins the current wire behaviour (which procedures are GET, which requests
+ dedupe) so the refactor is proven behaviour-preserving.
+- [ ] A simulation test flips the policy to "POST for everything" and asserts every client call path
+ still succeeds, standing in for oRPC v2's default.
+- [ ] `deno task deps:latest` shows the oRPC family at `1.14.15` and `deno why @orpc/shared` shows a
+ single resolved copy.
+- [ ] `gate:` `deno task check`, `deno task test`, and `deno task publish:dry-run` pass.
+- [ ] `gate:` `deno task e2e:cli run scaffold.runtime --cleanup --format pretty` passes, proving the
+ generated app's client calls are unchanged.
+
+## Boundaries
+
+- Do **not** migrate to oRPC v2 in this issue. This makes the future migration a one-function change;
+ it does not perform it.
+- Do **not** open the client construction seam here — that is T1-02. If T1-02 lands first, this issue
+ must additionally prove no contribution can reach the method decision.
+- Do **not** unfreeze the retry/dedupe defaults or expose `BatchLinkPlugin` / `RetryAfterPlugin` /
+ `SimpleCsrfProtectionLinkPlugin` here — those become reachable through the contribution chain in
+ T1-02.
+- Do **not** duplicate **#1320** (`deps: collapse to a single Zod instance`, `0.0.6`, blocked) — that
+ is a different duplicated dependency; this issue's dedup target is `@orpc/shared` only.
+- Do **not** duplicate **#451** (in-process link-mode adapter) — it becomes a third consumer of the
+ policy function, and stays its own issue.
+- Do **not** change server-side handler method policy (`StrictGetMethodPlugin` and friends are
+ unused today); server-side transport policy is out of scope.
+
+## Docs/consumer proof
+
+The SDK reference gains one short "transport policy" section stating where the method and cache
+decisions are made and that they are contract-derived — replacing the current situation where the
+only answer is "read `http-client-link.ts`". Consumer proof is the scaffold runtime E2E: the
+generated app's showcase calls behave identically before and after, and the pinned-behaviour test
+names the exact procedures whose method would change under oRPC v2, so the migration's blast radius
+is a list rather than a guess.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/external/orpc.md` §0/§6 (the explicit forward-compat recommendation) and
+`research/repo-audit/services-sdk.md` (S21, S22); all cited lines re-verified against worktree
+baseline `fac9e339042c`, including the repo-wide grep proving there are exactly two policy call
+sites. No GitHub mutation was performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-05-auth-contribution-dogfood.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-05-auth-contribution-dogfood.md
new file mode 100644
index 0000000000..ad07153f71
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-05-auth-contribution-dogfood.md
@@ -0,0 +1,151 @@
+# feat(sdk/auth): the typed service client cannot send any credential — prove the contribution chain with a first-party auth contribution — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T1-05 · **Proposed milestone:** `0.0.7` (post-rename-shift "Typed seams +
+generation", SYNTHESIS §5.3) · **Labels:** `type:feat` `area:sdk` `area:auth` `area:plugins`
+`priority:p1` `status:triage` · **Depends on:** T1-01 (RFC-A ratification, incl. answers to Q1
+cookie topology and Q2 contribution-group shape), T1-02 (the chain), T1-04 (transport policy)
+
+## Summary
+
+`@netscript/service/auth` reads `Authorization: Bearer …` and `x-api-key`, and
+`createServiceClient` cannot send either — the SDK has no concept of a credential at any layer.
+NetScript's own CLI proves the consequence: it calls the auth service with raw `fetch` and
+hand-rolled JSON shape-sniffing rather than the typed client it already depends on. This issue makes
+auth the first dogfood consumer of the RFC-A contribution chain: a first-party `authClient(…)` /
+`authContribution(…)` that attaches credentials, reads procedure policy metadata, surfaces a defined
+credential error, and is declared from the auth plugin's manifest — so the seam is validated by the
+sharpest real consumer rather than by a toy.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §0 (three independent proofs), §2 (the capability table),
+ §4.2 (cross-origin + `__Host-` cookie topology), §5 (no procedure policy metadata), gaps
+ G1/G2/G8/G9/G10; `research/repo-audit/services-sdk.md` §2.4 (S5);
+ `research/external/orpc.md` §1.5, §4 (G1, G3).
+- Source at baseline `fac9e339042c` (re-verified for this draft):
+ - `packages/service/src/auth/static-credential-authenticator.ts:108-117` — server reads
+ `Authorization: Bearer …` and `x-api-key`.
+ - `packages/sdk/src/client/http-client-link.ts:82-101` — the client's entire header authorship is
+ `Content-Type` plus optional `traceparent`/`tracestate`. `grep -rn 'Authorization' packages/sdk/src`
+ → no match.
+ - `packages/sdk/src/ports/service-client.ts:129-155,203-222` — no credential field on the options
+ record or on per-call context.
+ - `packages/cli/src/public/features/plugins/auth/auth-session-client.ts:7-23,27-37` — the
+ first-party CLI calls the auth service with raw `fetch` and no credential; hardcoded URLs at
+ `auth-plugin-command.ts:87,98-100` (the first is already broken — #1243).
+ - `grep -rn 'authClient' packages plugins` → **no such symbol exists**; this issue creates it.
+ - `packages/plugin/src/config/domain/plugin-contributions.ts:12-39` — no client contribution group;
+ `:16` is the closed-literal precedent to avoid.
+ - `grep -rnE '\$meta<|\.meta\(' packages plugins` → no oRPC procedure metadata anywhere, so
+ `policy.public` has no current consumer to conflict with.
+ - `packages/plugin-streams-core/src/application/stream-url-resolver.ts:136-150` — `getStreamsAuth()`
+ is the repo's only working credential header, built outside the typed client; existence proof
+ that a header seam fits this architecture.
+- Board: #872 (`[enterprise-auth S1]` capability discovery, `0.0.8`), #884 (org-aware policy
+ contracts, `0.0.12`), #885 (auth conformance kit, `0.0.12`), #1243 (broken CLI default URL,
+ `0.0.6`), #942 (auth v1 frontend, `0.0.11`).
+
+## Current surface
+
+Server-side auth is real and correctly ordered — `withAuthn`/`withAuthz` install before RPC routes
+mount, authz fails closed, and the principal reaches the oRPC handler context
+(`research/repo-audit/auth.md` §1.1). Client-side there is nothing: no credential option, no
+per-call override, no cookie forwarding, no emitter for the trusted-header authenticator that
+`packages/service/src/auth/trusted-header-authenticator.ts:32-54` is waiting for. The docs cannot
+show an authenticated typed call because none can be written — the authz tutorial can only show
+`curl -H 'authorization: Bearer read'` (`docs/site/tutorials/workspace/05-route-authz.md:248-258`).
+
+## Target contract
+
+A first-party contribution shipped from the auth plugin, consumed through the RFC-A chain:
+
+```ts
+import { authContribution } from '@netscript/plugin-auth/sdk';
+
+const users = createServiceClient({
+ contract: UsersContractV1,
+ serviceName: 'users',
+ with: [authContribution({ scheme: 'bearer' })],
+});
+await users.list({ limit: 20 }, { context: { auth: { token } } });
+```
+
+1. **`authContribution(options)`** declares `{ auth: { token: string } }` as per-call context, so
+ oRPC's `ClientRest` makes it required at the call site — omitting it is a compile error, not a
+ 401. A server-only variant (`@netscript/plugin-auth/sdk/server`) may close over a resolver that
+ reads a secret; the isomorphic descriptor never does.
+2. **`authClient(...)`** is the convenience wrapper the CLI and generated apps use: a
+ `createServiceClient` pre-composed with the auth contribution against `authContractV1`, replacing
+ the raw-`fetch` client at
+ `packages/cli/src/public/features/plugins/auth/auth-session-client.ts`.
+3. **Policy-aware behaviour.** The contribution reads `NetScriptProcedureMeta.policy` (RFC-A §3.6):
+ it does not attach a credential to a `policy.public` procedure, and it raises a defined
+ `CREDENTIAL_UNAVAILABLE` rather than sending an unauthenticated request to a non-public one.
+4. **Declared from the manifest.** The auth plugin declares the contribution through the
+ `PluginContributions` client group added by RFC-A — with a nameable, non-closed shape (contrast
+ `plugin-contributions.ts:16`).
+5. **Scheme scope is bounded by RFC-A Q1.** Bearer and `x-api-key` land here. Cookie/session
+ transport does **not** — `research/repo-audit/auth.md` §4.2 proves it cannot work across the
+ current cross-origin discovery + `__Host-` prefix + `origin:'*'` CORS combination, and the
+ topology decision is the auth pack's.
+
+## Acceptance
+
+- [ ] `authContribution()` ships from the auth plugin and composes through `createServiceClient`'s
+ contribution chain.
+- [ ] A call to a guarded service with the contribution present carries the expected credential
+ header, proven against a service configured with `createStaticCredentialAuthenticator`.
+- [ ] `authClient(...)` replaces the raw-`fetch` client in the CLI's auth session commands.
+- [ ] The contribution declares its per-call context so omitting the credential is a compile error.
+- [ ] The contribution skips credential attachment for procedures marked `policy.public`.
+- [ ] A defined `CREDENTIAL_UNAVAILABLE` error is raised instead of an unauthenticated request to a
+ non-public procedure.
+- [ ] NEGATIVE: a test asserts the same client without the contribution sends no credential header.
+- [ ] NEGATIVE: a test asserts an unauthenticated call to a guarded service is rejected with 401,
+ and an authenticated one succeeds — on `/api/rpc/*`, not only on `/api/openapi.json`.
+- [ ] NEGATIVE: a type fixture asserts the credential context cannot be supplied to a client built
+ without the contribution.
+- [ ] NEGATIVE: a test asserts the server-only contribution variant throws when constructed in a
+ browser-like environment.
+- [ ] Docs show one authenticated typed-client call end to end, replacing the `curl`-only example.
+- [ ] `gate:` `deno task check`, `deno task test`, and `deno task publish:dry-run` pass.
+
+## Boundaries
+
+- **Not here: the scaffold protecting `/api` by default.** The generated service template ships with
+ no `auth` option and a framework test codifies it
+ (`packages/service/tests/auth/define-service-auth_test.ts:11-22`) — that defect, the `plugin add
+ auth` starter surface, and the CORS `origin:'*'` default all belong to the **auth pack**, not to
+ this issue.
+- **Not here: making plugin services guardable.** `createPluginService` has no `auth` option and
+ never calls `withAuthn`/`withAuthz`
+ (`packages/plugin/src/service/presentation/create-plugin-service.ts`) — auth pack.
+- **Not here: the discarded `Set-Cookie` / `outputStructure: 'detailed'` defect** or the
+ unauthenticated `POST /api/v1/auth/signout` — auth pack.
+- Do **not** duplicate **#884** (organization-aware identity and authorization policy contracts) —
+ no `tenantId` is added to `Principal`, `AuthSession`, or any contract here; the contribution
+ context is merely extensible enough that #884 can add one later.
+- Do **not** duplicate **#885** (auth conformance/mocking/scaffold test kit) — the two negative
+ gates above are this issue's own proof, not the kit.
+- Do **not** duplicate **#872** (auth capability discovery) or **#942** (auth v1 frontend).
+- Do **not** duplicate **#1243** (`session list --stream-url` pins a dead localhost port) — replacing
+ the CLI's transport here will touch that file; fix the URL under #1243 and reference it.
+- Do **not** implement the server-side authorizer that consumes `policy.scopes` — auth pack.
+- Do **not** add cookie/`credentials: 'include'` support until RFC-A Q1 is answered.
+
+## Docs/consumer proof
+
+`docs/site/identity-access/how-to/add-authentication.md` and `docs/site/services-sdk/sdk.md` gain the
+first authenticated typed-client example in the repo's history, and
+`docs/site/tutorials/workspace/05-route-authz.md:248-258` swaps its `curl` for that example. Consumer
+proof is that NetScript's own CLI stops hand-rolling `fetch` against the auth service — the deleted
+`auth-session-client.ts` request/parse code is the adoption evidence, and the two negative gates
+prove the credential is actually load-bearing rather than decorative.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` (G1, and §9's ordering that names auth as the first dogfood) and
+`research/repo-audit/services-sdk.md` (S5); the absence of an `authClient` symbol and of any oRPC
+`.meta()` usage was re-verified by grep against worktree baseline `fac9e339042c`. No GitHub mutation
+was performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-06-second-non-auth-contribution.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-06-second-non-auth-contribution.md
new file mode 100644
index 0000000000..4287d0825e
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T1-06-second-non-auth-contribution.md
@@ -0,0 +1,130 @@
+# feat(sdk): trace-context propagation is hardcoded inside the link — re-express it as the second, non-auth contribution that proves the seam is general — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T1-06 · **Proposed milestone:** `0.0.7` (post-rename-shift "Typed seams +
+generation", SYNTHESIS §5.3) · **Labels:** `type:feat` `area:sdk` `area:telemetry` `priority:p1`
+`status:triage` · **Depends on:** T1-01 (RFC-A ratification), T1-02 (the chain), T1-05 (first
+dogfood)
+
+## Summary
+
+A contribution seam validated by one consumer encodes that consumer's shape. Auth (T1-05) is
+credential-shaped; without a structurally different second consumer, RFC-A's claim that the chain is
+general is unproven. Trace-context propagation is the right second consumer because it is already
+hard-coded inside `createHttpClientLink` behind a boolean, with a per-call override field and
+existing regression coverage — so re-expressing it as a contribution is a **migration, not an
+addition**, and the negative test therefore has teeth: remove the contribution and `traceparent` must
+disappear from the wire. It also removes the framework's last private fast lane through the link, so
+NetScript's own default path goes through the public composition path rather than beside it.
+
+## Evidence
+
+- Corpus: `research/external/orpc.md` §1.5, §5 (the "header/trace/tenant contribution from a plugin"
+ row), §7 item 1; `research/repo-audit/services-sdk.md` §1.3, §2.3 (losing the client span is the
+ documented cost of the current escape hatch), §3.1.
+- Source at baseline `fac9e339042c` (re-verified for this draft):
+ - `packages/sdk/src/client/http-client-link.ts:82-101` — the `headers` callback: `Content-Type`,
+ then `traceparent`/`tracestate` from `options.context.traceHeaders` if present, else from
+ `getTraceHeaders()`. Gated by the `propagateTraceContext` boolean.
+ - `packages/sdk/src/client/service-client.ts:41-49,55-64` — `propagateTraceContext = true` default,
+ threaded into the link along with `getTraceHeaders`.
+ - `packages/sdk/src/ports/service-client.ts:149-155` — `ServiceClientContext.traceHeaders` is the
+ per-call override, i.e. this concern already exercises the exact "typed per-call context" axis
+ the envelope claims to generalise.
+ - `packages/sdk/src/client/http-client-link.ts:127+` — the custom `fetch` opens the CLIENT span
+ with `rpc.system=orpc` / `server.address` attributes; this is the behaviour a hand-rolled client
+ silently loses today.
+ - `packages/sdk/src/presets/define-services.ts:106-116` — `propagateTraceContext` is forwarded from
+ L3, so the migration must preserve the L3 surface too.
+- Rejected alternative, with its own citations: the AI/streams header contribution
+ (`getStreamsAuth()` → `{ Authorization: 'Bearer ' + STREAMS_SECRET }`,
+ `packages/plugin-streams-core/src/application/stream-url-resolver.ts:136-150`, consumed at
+ `packages/fresh/src/runtime/streams/create-stream-db.ts:111` and
+ `packages/fresh/src/runtime/ai/stream-proxy.ts:162`).
+
+## Current surface
+
+Trace propagation works and is not a defect — it is a *hard-code*. Two of its three axes
+(header authorship, per-call typed override) are exactly the axes RFC-A's envelope exists to
+generalise, and the third (server-side context resolution) is the environment boundary. Meanwhile
+the only other credential-shaped header seam in the repo, `getStreamsAuth()`, was built entirely
+outside the typed client with raw `fetch`, because there was nowhere else to put it.
+
+## Target contract
+
+1. **`traceContextContribution()`** ships from `@netscript/sdk` (or `@netscript/telemetry`, per the
+ layering decision recorded in the implementing PR) as an `SdkClientContribution` declaring
+ `headerKeys: ['traceparent', 'tracestate']` and per-call context
+ `{ trace?: { traceparent?: string; tracestate?: string } | null }`.
+2. **It is composed by default.** `createServiceClient` prepends it when `propagateTraceContext` is
+ not `false`, so the shipped default behaviour and the L3 `defineServices` surface are unchanged.
+3. **The link body no longer authors trace headers.** After this issue,
+ `createHttpClientLink` contains no `traceparent` literal; `ServiceClientContext.traceHeaders`
+ becomes a `@deprecated` alias forwarding to the contribution's context field for one minor.
+4. **It carries no secret and needs no server-only variant for header authorship**, which is
+ precisely what makes it a generality proof rather than a second credential test.
+5. **The CLIENT span stays attached to the framework default chain**, so composing extra
+ contributions cannot silently drop it.
+
+### Why this consumer, and not the AI/streams headers
+
+Both candidates were evaluated (RFC-A §9). `getStreamsAuth()` is attractive because it is a real,
+shipped, out-of-band header that today lives outside the typed client. It was rejected as the
+*second* consumer for three reasons: it is still a **credential** (a process-global shared secret),
+so it re-tests auth's axis rather than a new one; it is entangled with the streams/SSE transport and
+with #1329's envelope work, so a failure would not distinguish "the seam is wrong" from "streams is
+wrong"; and it is purely additive, so a decorative seam could pass its test. Trace context fails
+loudly if the seam is decorative, because its behaviour already exists and must survive the move. The
+streams/AI header contribution remains a good **third** consumer once the streams envelope settles.
+
+## Acceptance
+
+- [ ] `traceContextContribution()` ships as an `SdkClientContribution` declaring its header keys and
+ per-call context.
+- [ ] `createHttpClientLink` contains no trace-header authorship; the contribution is the only
+ producer.
+- [ ] `createServiceClient` and `defineServices` behave identically to today when
+ `propagateTraceContext` is unset or `true`.
+- [ ] The per-call trace override continues to work through the contribution's context field.
+- [ ] The CLIENT span is still emitted with `rpc.system=orpc` and `server.address` when other
+ contributions are composed.
+- [ ] NEGATIVE: with the contribution removed from the chain, a request carries no `traceparent` and
+ no `tracestate` — asserted on the wire, not on the options object.
+- [ ] NEGATIVE: with `propagateTraceContext: false`, no trace header is sent (pins today's behaviour).
+- [ ] NEGATIVE: a test asserts the auth contribution and the trace contribution compose in either
+ order without either header being lost.
+- [ ] NEGATIVE: a type fixture asserts the composed per-call context is the intersection of both
+ contributions' declared contexts.
+- [ ] The contribution declares no credential and requires no server-only module for header
+ authorship.
+- [ ] `gate:` `deno task check`, `deno task test`, and `deno task publish:dry-run` pass.
+
+## Boundaries
+
+- Do **not** migrate `getStreamsAuth()` onto the chain here — it is the deliberately deferred third
+ consumer, and it depends on the streams envelope work.
+- Do **not** duplicate **#1329** (`fix(streams)`: documented SSE consumer shape differs from the wire
+ protocol and does not specify the standard event/OTEL envelope, `0.0.5`, p0) — the streams
+ telemetry envelope is its scope.
+- Do **not** change OTEL span names, attributes, or the tracer identity — telemetry semantics are out
+ of scope; this issue only moves *who authors the headers*.
+- Do **not** add a tenancy or session context field "while we are here" — #884 owns tenancy.
+- Do **not** implement the auth contribution here — T1-05.
+- Do **not** open the chain here — T1-02.
+- Do **not** duplicate the observability/runtime-truth work in the T4 pack (saga span call-sites,
+ child liveness, E2E span assertions).
+
+## Docs/consumer proof
+
+The SDK reference documents trace propagation as a *contribution* — the first place a reader sees the
+same mechanism used twice, by two unrelated concerns, which is the whole adoption argument. Consumer
+proof is a diff: `http-client-link.ts` loses its trace block, `@netscript/sdk`'s public surface gains
+one small value, and the two-contribution composition example in the docs is the one an app author
+copies. If the negative gate ("remove it and the header disappears") cannot be written, the seam is
+decorative and RFC-A's ratification should be reconsidered rather than the test weakened.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Consumer choice and its
+rejected alternative are recorded in `rfcs/RFC-A-sdk-client-composition.md` §9; sourced from
+`research/external/orpc.md` §5/§7 and `research/repo-audit/services-sdk.md` §1.3/§2.3, with all cited
+lines re-verified against worktree baseline `fac9e339042c`. No GitHub mutation was performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-01-route-slice-generator.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-01-route-slice-generator.md
new file mode 100644
index 0000000000..54a5251aaf
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-01-route-slice-generator.md
@@ -0,0 +1,145 @@
+# feat(cli): no verb generates a resource route slice — the typed contract, cache-first loader and withResource page must be hand-copied from init — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-01 · **Proposed milestone:** 0.0.7 (new "Typed seams + generation" cut per the
+Stage-E rename-shift; if the train is not shifted, `Backlog / Triage`) · **Labels:** `type:feat`
+`area:cli` `area:fresh` `area:fresh-ui` `priority:p1` `status:triage` · **Depends on:** T2-03
+(app-root targeting — hard blocker, this generator inherits the wrong write root without it);
+T2-02 (query/invalidation module the generated island binds to); T1-01 / RFC-A **only** for the
+optional contribution-inclusion clause below
+
+## Summary
+
+Every element of the canonical resource slice exists in `packages/fresh` and is demonstrated once,
+as a frozen `init` template for the single scaffolded example service — but **no re-runnable verb
+emits it**. A user who adds a resource must hand-transcribe the example: typed route contract,
+`definePage` root, route-local `(_components)`/`(_islands)`/`(_shared)` layout, cache-first loader,
+`QueryIsland`, deferred partial. The measured consequence is that agents do not transcribe it: the
+Wave-6 `rickylabs/loom` run received the registry, `/design`, `AGENTS.md` and `WEB-LAYER.md` and
+still hand-rolled tables, direct service calls and a 676-line island (#1333 body). Generation, not
+more prose, is the remaining lever.
+
+## Evidence
+
+- `research/repo-audit/mcp-cli.md` §4.2 — element-by-element table: `defineRouteContract`,
+ `withResource`, cache-first loader, form slice, stream slice, `State` extension and
+ `router.ts`/`appRoutes` registration are all **framework ✅ / generated ❌**.
+- `research/repo-audit/web-layer.md` §2.1 — verified in-worktree: `withResource`, `withForm`,
+ `withParams`/`withPathParams`/`withSearchParams`, `withRouteContract`, `withStreaming`,
+ `definePartial` have **zero occurrences** in `packages/cli/src/kernel/assets/app/`.
+- `research/external/eis-chat.md` §11 (S1–S12) and §12 — the twelve conventions a
+ product-grade NetScript app invents in app space because the scaffold does not emit them.
+- Repo, verified at `fac9e339042c`: framework surface present —
+ `packages/fresh/src/application/route/mod.ts:69` (`bindRoutePattern`), `:94`
+ (`defineRouteContract`); sidecar convention `packages/fresh/src/application/route/manifest.ts:44`
+ (`isRouteContractSidecar`), `:53` (`isRouteHelperDirectoryName`);
+ `packages/fresh/src/application/builders/define-page/builder/state.ts:55` (`withResource`).
+- Generator surface absent: `packages/cli/src/kernel/application/ui/web-scaffold.ts:15-42` is the
+ entire page generator (see T2-04); `packages/cli/src/public/features/generate/generate-group.ts`
+ registers only `aspire`, `runtime-schemas`, `plugins`.
+- Issues: #1333 (default-app modernization), #1335 (conformance inventory), #946 (plugin frontend
+ convention generation), #1208/#1210 (docs for these APIs).
+
+## Current surface
+
+`netscript ui:add page --island` writes three files, none of which loads data (T2-04).
+`netscript service add --with-client` writes one app-side module (T2-02). Nothing in between
+exists: there is no verb that takes a contract procedure and emits a working screen. The reference
+shape lives only in `packages/cli/src/kernel/assets/app/routes/examples/service/` +
+`(_shared)/service-showcase.ts.template` + `(_islands)/ServiceShowcaseLab.tsx.template` +
+`routes/partials/examples/service-summary.tsx.template`, rendered once by `init` and never again.
+Route ids in the one generator are hand-derived dotted strings
+(`web-scaffold.ts:20`) with no link to `apps//.generated/routes.ts`, and the generated manifest
+is only seeded by `init` and regenerated by the Vite plugin — there is no `generate routes` verb, so
+a CI/agent flow that never runs Vite has a stale typed route surface
+(`packages/cli/src/kernel/application/scaffold/writers/app-route-seeds.ts`;
+`packages/fresh/src/application/route/manifest.ts:280-292`).
+
+## Target contract
+
+One composable generator, addressed at an app (T2-03), that emits a **complete, type-checking,
+runnable** resource slice and can be re-run:
+
+1. **Core (always):** `.route.ts` sidecar declaring the route contract via
+ `defineRouteContract` (path/search schemas typed, not `Record`); `index.tsx`
+ containing a `definePage()` root **with zero presentational JSX** — `withRouteContract`/
+ `withRoute`, `withResource` for the shared read, `withLayer` per region, `withLayout` slots,
+ `withMeta`; route-local `(_components)/-view.tsx` holding all markup;
+ `(_shared)/-loaders.ts` holding the contract-derived cache-first loader
+ (`createNetScriptQueryClient` → `fetchQuery` → `dehydrateQueryClient` → `cachedAt`);
+ `(_islands)/Island.tsx` binding `QueryIsland` + `useIslandQuery` to the **generated**
+ query factory from T2-02 (`clientKey`, not a string literal) and passing
+ `initialDataUpdatedAt` (T2-07); registration in `router.ts`/`appRoutes` derived from the
+ generated manifest, not an inline `createRouteReference`.
+2. **Optional flags, composable and independently testable:** `--form` (a `withForm` region with a
+ Zod schema whose messages are user-facing copy, `csrf: true`, `redirectTo` through the typed
+ route map, `spanName`, and a component reading `firstFieldError`), `--partial` (a deferred
+ `withLayer` + a `routes/partials/...` partial whose name is derived, not a matching string
+ literal), `--stream` (a `@netscript/fresh/streams` consumer).
+3. **State:** when a slice needs request-scoped state, the generator extends the app's
+ `utils.ts` `State` type instead of leaving `Record`.
+4. **Components:** all markup composes app-owned Fresh-UI primitives from
+ `components/ui/mod.ts` — the generator never emits raw Tailwind for something the registry ships.
+5. **Ownership comments:** each emitted route-local directory carries a one-line header stating what
+ belongs in it (`(_components)` = markup only, `(_islands)` = hydration only, `(_shared)` =
+ loaders/types, `(_lib)` = route-local pure helpers), so the vocabulary survives the first edit.
+6. **Generator semantics match the rest of the CLI:** `--dry-run`, `--force`, and a
+ content-compare `written`/`skipped` result like `generate runtime-schemas`
+ (`packages/cli/src/public/features/generate/runtime-schemas/generate-runtime-schemas.ts:44-77`)
+ — not the current hard refuse at `web-scaffold.ts:60`.
+7. **Optional contribution inclusion (RFC-A-gated):** when installed plugins contribute route/SDK
+ surfaces, the generator includes them. This clause and **only** this clause depends on T1-01;
+ the core generator ships without it and must not be sequenced behind the RFC.
+
+## Acceptance
+
+- [ ] A single documented verb emits the core slice listed in Target contract §1 for a named
+ contract procedure.
+- [ ] The emitted slice type-checks and lints inside a freshly scaffolded project with no manual
+ edits.
+- [ ] The emitted page file contains no presentational JSX outside `withLayout` slots.
+- [ ] The emitted island binds the generated query factory's `clientKey`, not a string-literal key.
+- [ ] The emitted route registers through `router.ts`/`appRoutes` derived from the generated
+ manifest, not an inline `createRouteReference`.
+- [ ] `--form`, `--partial` and `--stream` each add exactly their own files and are independently
+ re-runnable.
+- [ ] The verb supports `--dry-run` and `--force` and reports `written`/`skipped` on re-run.
+- [ ] A second run with no input change writes zero files and exits 0.
+- [ ] Negative test: generation into a project whose contract lacks the named procedure fails
+ non-zero with the missing procedure named, and writes no files.
+- [ ] Negative test: a golden test fails if any emitted file contains `any`, a raw `fetch(` call,
+ a hand-written query-key array literal, or manual `JSON.parse` of a service response.
+- [ ] Negative test: removing the app-root resolution (T2-03) makes the generator fail loudly
+ rather than write outside `apps//`.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` covers generating a slice and
+ type-checking the app that contains it.
+
+## Boundaries
+
+- **#1333** owns the *default app's* content and its acceptance; this issue owns the **re-runnable
+ verb**. Do not restate #1333's ten boxes here, and do not close #1333 with this PR.
+- **#1335** owns the repo-wide generated-surface inventory; this issue adds one generator to it.
+- **#946** owns plugin-contributed frontend convention generation; contribution inclusion here is
+ the consumer side of that seam, gated on T1-01.
+- **T2-02** owns the client/query/invalidation module; this generator consumes it and must not
+ emit its own client wiring.
+- **T2-04** owns fixing the existing `ui:add page` emission; if the owner prefers one command, T2-04
+ becomes this generator's `--minimal` mode — that consolidation decision is stated, not assumed.
+- **#1208/#1210** own the docs for `withResource`/`withForm`/partials; this issue ships examples,
+ not the manual.
+- Not in scope: the missing `generate routes` verb (mcp-cli.md §4.5 C2) beyond whatever the slice
+ registration needs; a service-side command slice (T3).
+
+## Docs/consumer proof
+
+Adoption is proved by a generated project, not by prose: the `scaffold.runtime` E2E generates a
+slice and type-checks it; the app `AGENTS.md`/`WEB-LAYER.md` reference the verb as the first step of
+the one-screen path; and a consumer-shaped check re-derives the eis-chat conventions S1–S4/S7 from
+generated output rather than from `docs/design/BUILD-CONTRACT.md` in a downstream repo
+(`research/external/eis-chat.md` §11).
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/mcp-cli.md` §4.2/§5, `research/repo-audit/web-layer.md` §2.1,
+`research/external/eis-chat.md` §11–12; all repo claims re-verified against worktree
+`fac9e339042c`. No GitHub mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-02-sdk-query-invalidation-generator.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-02-sdk-query-invalidation-generator.md
new file mode 100644
index 0000000000..6c61c4cb4b
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-02-sdk-query-invalidation-generator.md
@@ -0,0 +1,126 @@
+# feat(cli): app-side client/query wiring is a one-shot template with hardcoded names, colliding 'service' cache keys and a no-op invalidation — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-02 · **Proposed milestone:** 0.0.7 (new "Typed seams + generation" cut per the
+Stage-E rename-shift; if the train is not shifted, `Backlog / Triage`) · **Labels:** `type:feat`
+`area:cli` `area:sdk` `area:contracts` `priority:p1` `status:triage` · **Depends on:** T1-01 /
+RFC-A **only** for including installed plugin SDK contributions; the manifest-derived generator for
+first-party services is independent and must not be sequenced behind the RFC
+
+## Summary
+
+`netscript service add --with-client` renders one 27-line template that is wrong in three
+structural ways at once: every service exports the same `exampleService*` symbols, every service's
+cache keys live under the literal resource string `'service'`, and the generated invalidation
+constant addresses a key prefix that no generated query ever produces — so the flagship showcase's
+"Invalidate list cache" button and its optimistic `onSettled` reconciliation are **silent no-ops**.
+There is no verb at all for the second service. Priority note: the `'service'` collision and the
+dead invalidation are runtime-correctness defects shipped in generated user code; they are kept
+inside this generator issue because fixing them without changing the generator would only re-emit
+them, but the owner may split them out as a separate p0 fix.
+
+## Evidence
+
+- `research/repo-audit/services-sdk.md` §4.1 (a)–(d) and gap register S6, S7, S15, S16 — all rated
+ High/Medium with citations; §7 states **no open issue covers S6/S7**.
+- `research/repo-audit/mcp-cli.md` §4.1 — "no CLI verb emits `apps//lib/.ts` for a
+ second service"; the generated `bridgeInvalidation` pair is hand-written, so a renamed procedure
+ fails at runtime, not at type-check.
+- Repo, verified at `fac9e339042c`:
+ - `packages/cli/src/kernel/assets/app/lib/example-service.ts.template:8-27` — exports
+ `exampleServiceName`, `exampleServiceRouterName`, `exampleServiceContract`,
+ `exampleServiceListInvalidation`, `exampleServiceClient`, `exampleServiceQueries`; the factory
+ group is literally `createQueryFactories({ service: { … } }).service`.
+ - `packages/sdk/src/query/query-factory.ts:41-46` (`createQueryFactory(resource, …)`), `:54-58`
+ (`invalidate` uses `[resource]`), `:147` (`queryKey: [resource, action, { input }]`), `:174-178`
+ (`clientKey`), `:218` (`createQueryFactories` passes the **object key** as `resource`).
+ - `packages/sdk/src/query-client/key-bridge.ts:19-23,32-37` — `bridgeInvalidation(resource,
+ action)` returns `{ queryKey: [resource, action] }`, i.e. `['users','list']` for the default
+ service name, while the real keys start with `'service'`.
+ - Consumed at
+ `packages/cli/src/kernel/assets/app/routes/examples/(_islands)/ServiceShowcaseLab.memory.tsx.template:85`
+ (`onSettled` after an optimistic mutation) and `:115` (an "Invalidate list cache" button).
+ - `packages/cli/src/kernel/adapters/service/client-scaffolder.ts:9-21,45-49` — the single
+ template render; `packages/cli/src/public/features/services/add/add-service.ts:69-79`;
+ flag declared at `add-service-command.ts:37`.
+- Verified negative: grep for `bridgeInvalidation|invalidateQueries` under `packages/sdk/tests` and
+ `packages/cli/e2e/src` returns nothing (`services-sdk.md` §4.1 (c)).
+
+## Current surface
+
+One template, one service, one shot. Two services ⇒ two files exporting identical symbol names ⇒
+any module importing both must alias every symbol; both services' `list` actions share the cache
+key `['service','list',…]` in **both** tiers (server keys via
+`packages/sdk/src/ports/query-key.ts`, client keys via `query-factory.ts:143`), so
+`factory.invalidate()` invalidates every service at once and a cache read can be served another
+service's payload. `defineServices()` — the advertised L3 preset — appears on no scaffolded path
+(`services-sdk.md` S16).
+
+## Target contract
+
+A `generate`-family verb (name to lock in implementation; `generate sdk` / `contract sync` are the
+candidates from `mcp-cli.md` §5) that regenerates the app-side data layer from the contract
+manifest:
+
+1. **Names derive from the service/contract**, never from `example*`: `Client`,
+ `Queries`, `Contract`, one module per service at
+ `apps//lib/.ts`. Two services never collide.
+2. **Resource keys derive from the service/router name**, not the literal `'service'`. Cross-service
+ collision becomes impossible by construction, and `factory.invalidate()` is scoped to one
+ service.
+3. **Invalidation is generated, not hand-written**: a per-procedure invalidation map derived from
+ the same contract that produced the keys, so a renamed procedure is a **type error**, not a
+ runtime no-op. The showcase's two call sites consume the generated map.
+4. **Idempotent and drift-reporting**, matching `generate runtime-schemas`: content-compare with
+ `written`/`skipped`, `--dry-run`, `--force`; a second run is byte-identical.
+5. **Integrates with the existing verbs**: `service add --with-client` and `service generate` call
+ the same generator rather than rendering a template, so there is one code path.
+6. **Installed contributions (RFC-A-gated, T1-01):** plugin-contributed SDK/query surfaces are
+ included in the generated module when the contribution contract exists. Without T1-01 the
+ generator emits first-party services only — that is a shipping configuration, not a blocked one.
+7. **The L1/L2 vs L3 (`defineServices`) choice is decided and documented once** in the generator, so
+ the generated path and the docs teach the same dialect (S16/S17).
+
+## Acceptance
+
+- [ ] A documented verb regenerates `apps//lib/.ts` for every service in the manifest.
+- [ ] Generated export names are derived from the service name; a two-service project imports both
+ modules with no aliasing.
+- [ ] Generated query-factory resource keys are per-service; a two-service fixture proves the
+ `list` keys differ in both cache tiers.
+- [ ] The generated invalidation map is contract-derived and a renamed procedure fails `deno check`.
+- [ ] The showcase island's invalidation call sites use the generated map and actually invalidate.
+- [ ] Re-running with no input change writes zero files; output is byte-identical.
+- [ ] `--dry-run` reports the plan and writes nothing; `--force` rewrites unchanged files.
+- [ ] Negative test: a fixture asserting `bridgeInvalidation`-style keys that do not match the
+ factory keys fails the suite (the S6 regression is locked out).
+- [ ] Negative test: a two-service fixture whose factories share a resource key fails the suite.
+- [ ] Negative test: generating for a service with no contract exits non-zero and writes no files.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` adds a second service and proves both
+ generated modules type-check and their caches do not collide.
+
+## Boundaries
+
+- **T1-01 / RFC-A** owns the `SdkClientContribution` contract and the reopened oRPC seams
+ (headers/interceptors/plugins/link/context/typed errors). Do not design that contract here.
+- **#1333** owns making the default app idiomatic; this issue owns the generator it calls.
+- **#1335** owns the conformance inventory.
+- **T2-01** owns the route slice that consumes these factories; it must not emit client wiring.
+- **#1245** owns island query *type* gaps; the `clientKey → queryKey` convergence landed in #1265.
+- Not in scope: `AbortSignal` forwarding (S8), nested-router factories (S9), `port`/`timeout`
+ dead options (S11) — those are SDK-surface items for T1, referenced here so they are not
+ re-filed.
+
+## Docs/consumer proof
+
+A generated two-service project is the proof: both modules import cleanly, `deno check` passes, the
+invalidation button changes observable cache state in the runtime E2E, and the services-SDK docs
+page teaches exactly the dialect the generator emits (one dialect, per T5). The eis-chat
+cache-key-collision note (`research/external/eis-chat.md` §3, `lib/channel-service.ts:12-24`,
+"factory-group name is the cache-key prefix and must be unique per router") becomes a generated
+invariant instead of app-space folklore.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/services-sdk.md` §4.1 + S6/S7/S15/S16 and `research/repo-audit/mcp-cli.md`
+§4.1/§5; all repo claims re-verified against worktree `fac9e339042c`. No GitHub mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-03-ui-app-root-targeting.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-03-ui-app-root-targeting.md
new file mode 100644
index 0000000000..eddaded10e
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-03-ui-app-root-targeting.md
@@ -0,0 +1,107 @@
+# fix(cli): every ui:* command writes to the workspace root instead of apps/, and the E2E gate asserts the wrong root — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-03 · **Proposed milestone:** 0.0.7 (new "Typed seams + generation" cut per the
+Stage-E rename-shift; if the train is not shifted, `0.0.6`) · **Labels:** `type:fix` `area:cli`
+`area:fresh-ui` `area:docs` `priority:p1` `status:triage` · **Depends on:** none — this is the
+blocker T2-01 and T2-04 sit on top of
+
+## Summary
+
+`init` writes the Fresh app to `apps//` and installs the UI registry **there**, but every
+`ui:*` command resolves its write root through `resolveProjectRoot`, which walks up to the
+**workspace** root. So `netscript ui:add data-table` in a scaffolded project copies components into
+`/components/ui/` — outside any Fresh app, invisible to the app's barrel and to the
+Vite route generator. There is no `--app` flag to correct it, yet the docs already document one.
+The repo's own E2E gate passes the workspace root and then asserts the copied paths relative to it,
+so CI is green **because it encodes the defect**.
+
+## Evidence
+
+- `research/repo-audit/mcp-cli.md` §4.4 (titled "App-targeting seam — BROKEN TODAY") and §3
+ preamble; `research/repo-audit/web-layer.md` §8 secondary notes.
+- Repo, verified at `fac9e339042c`:
+ - `packages/cli/src/public/features/root/public-command-dependencies.ts:195-197` —
+ `resolveProjectRoot` = `findDeployProjectRoot(host.cwd())`.
+ - `packages/cli/src/kernel/adapters/config/deploy-config.ts:59-84` — `findProjectRoot` returns the
+ first ancestor containing `netscript.config.ts`, `dotnet/AppHost/appsettings.json`, **or a
+ `deno.json` with a `workspace` array**, i.e. always the workspace root.
+ - `packages/cli/src/public/features/ui/add/add-ui-command.ts:54-57` — every `ui:add` path
+ (`page`, `island`, registry item) uses `requireProjectRoot(dependencies.resolveProjectRoot, …)`;
+ `:38-52` is the full option list and contains **no `--app`**.
+ - `packages/cli/src/kernel/application/scaffold/writers/write-app-files.ts:182-185` — `init`
+ installs the registry with `projectRoot: appDir`, i.e. `apps//`.
+ - `packages/cli/src/kernel/application/ui/web-scaffold.ts:15-42` — `ui:add page` writes
+ `/routes//…`, so a scaffolded project gets `/routes/…`.
+ - Gate encoding the wrong root:
+ `packages/cli/e2e/src/application/gates/scaffold/ui-ai-gates.ts:23-24` passes
+ `--project-root context.project.projectRoot`, and `:95-102` asserts
+ `islands/ui/McpUiWidget.tsx`, `lib/ai/render-ui.tsx`, `assets/styles.css` relative to that same
+ root.
+ - Docs that document a flag which does not exist / a path the command does not write:
+ `docs/site/web-layer/how-to/build-a-desktop-frontend.md:31`
+ (`netscript ui:add desktop --app dashboard`);
+ `docs/site/web-layer/how-to/customize-fresh-ui.md:26` and `:256` ("component files go to
+ `apps/dashboard/components/ui/`").
+
+## Current surface
+
+Two resolution rules for one artifact: `init` installs into the app, `ui:*` installs into the
+workspace. Nothing warns. A user who follows the published how-to gets files in a directory the
+Fresh app never reads, and re-running `ui:update` compares drift against that same wrong tree.
+`ui:add page` compounds it: the emitted route lands outside the app so the Vite route generator
+never sees it and `appRoutes` never gains the entry.
+
+## Target contract
+
+1. **`ui:*` commands resolve an app root, not the workspace root.** Resolution order: explicit
+ `--app ` (or `--project-root` pointing at an app) → single Fresh app workspace member →
+ error listing the candidates when more than one exists. The error names the flag to use.
+2. **`--app ` exists on every `ui:*` command** (`ui:init`, `ui:add`, `ui:list`, `ui:update`,
+ `ui:remove`) with one shared implementation, and is documented in `--help`.
+3. **Running inside `apps//` works with no flag.**
+4. **The E2E gate is corrected, not preserved**: `ui-ai-gates.ts` targets the app and asserts the
+ app-relative paths. The corrected assertions must fail against today's behavior.
+5. **The docs stop describing a flag that does not exist**: the two how-to pages are updated to the
+ shipped surface in the same change, and their commands are copy-runnable.
+6. **The public input type describes the public CLI**: `UiAddCommandInput`
+ (`packages/cli/src/public/features/ui/add/add-ui-input.ts`) gains the fields the action already
+ accepts (`route`, `island`, `query`, and the new `app`).
+
+## Acceptance
+
+- [ ] `ui:*` commands write into the resolved Fresh app, never the workspace root.
+- [ ] `--app ` is accepted by every `ui:*` command and documented in `--help`.
+- [ ] Running a `ui:*` command from inside `apps//` needs no flag.
+- [ ] A multi-app workspace with no `--app` fails non-zero and lists the candidate apps.
+- [ ] `UiAddCommandInput` declares every option the command accepts.
+- [ ] The two how-to pages match the shipped flags and paths.
+- [ ] Negative test: a regression test asserts that no `ui:*` command writes to the workspace root
+ when an app member exists — this test must fail on the pre-fix build.
+- [ ] `ui-ai-gates.ts` asserts app-relative paths and its assertions fail against the pre-fix
+ behavior.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` proves a `ui:add` item lands in
+ `apps//components/ui/` and is reachable from the app's barrel.
+
+## Boundaries
+
+- **#1333** owns the default app's content and dynamic app naming; this issue owns *where the CLI
+ writes*. Do not fold the app-name derivation into this fix.
+- **#1335** owns the conformance inventory that will record the corrected paths.
+- **T2-01/T2-04** consume this seam; they must not each invent their own app resolution.
+- **#1328 (CLOSED)** owned generated quality-gate coverage — do not reopen it.
+- Not in scope: `ui:add page`'s emitted *content* (T2-04), the missing `--force`/`--dry-run` on
+ `ui:add page` (T2-01/T2-04), or `netscript-dev`'s stale `version('1.0.0')`
+ (`mcp-cli.md` §4.5 C3).
+
+## Docs/consumer proof
+
+A scaffolded project is the proof: run every documented `ui:*` command from the published how-tos
+verbatim and show the files landing where the how-to says they land, with the app barrel and the
+generated route manifest picking them up. The corrected `ui-ai-gates.ts` is the standing regression
+proof that the wrong root cannot come back green.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/mcp-cli.md` §4.4 and `research/repo-audit/web-layer.md` §8; every path and line
+re-verified against worktree `fac9e339042c`. No GitHub mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-04-ui-add-page-data-screen-triad.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-04-ui-add-page-data-screen-triad.md
new file mode 100644
index 0000000000..943a84e356
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-04-ui-add-page-data-screen-triad.md
@@ -0,0 +1,113 @@
+# fix(cli): ui:add page --island emits a useSignal counter and an empty queryLoaders object instead of the advertised data-screen triad — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-04 · **Proposed milestone:** 0.0.7 (new "Typed seams + generation" cut per the
+Stage-E rename-shift; if the train is not shifted, `0.0.6`) · **Labels:** `type:fix` `area:cli`
+`area:fresh` `priority:p1` `status:triage` · **Depends on:** T2-03 (app-root targeting), T2-01
+(the slice contract this command is the minimal mode of), T2-02 (the query factory the island binds)
+
+## Summary
+
+`ui:add` describes itself as scaffolding "the Fresh page + island + query-loader triad for a data
+screen", and the generated app `AGENTS.md` tells every coding agent to reach for it first. What it
+emits is a page with a `() => ({})` layer loader, an island that is a `useSignal(0)` counter button,
+and a file containing `export const queryLoaders = {} as const;`. The one command the framework
+points agents at produces a counter where a data screen was promised — which is the measurable
+mechanism behind agents hand-rolling their own screens.
+
+## Evidence
+
+- `research/repo-audit/web-layer.md` §8 (full emission breakdown) and gap register item 9;
+ `research/repo-audit/mcp-cli.md` §3.5.
+- Repo, verified at `fac9e339042c`, `packages/cli/src/kernel/application/ui/web-scaffold.ts`:
+ - `:15-42` — `scaffoldUiPage` writes exactly three files; the page's only layer is
+ `.withLayer('', () => , () => ({}))` with no loader, no `withResource`, no query.
+ - `:34` + `:66-68` — the island content is `signalIslandTemplate`, i.e.
+ `const count = useSignal(0); return ;`.
+ - `:37` — the third file is literally
+ `export const queryLoaders = {} as const;`.
+ - `:51` — `ui:add island --query` emits `
Name
`: a provider
+ around a static div, no `useQuery`, no key, no factory.
+ - `:60` — any pre-existing target file is a hard refuse; there is no `--force` and no `--dry-run`,
+ unlike every other generator (`install-plugin-command.ts` has both).
+ - `:20` — the route id is a hand-derived dotted string with no link to the generated manifest, and
+ the page declares `createRouteReference` inline instead of registering in `router.ts`.
+- Advertised behavior it contradicts:
+ `packages/cli/src/public/features/ui/add/add-ui-command.ts:26-28` (the description quoted above)
+ and `packages/cli/src/kernel/templates/app/agent-conventions.ts:137-139` (the three commands
+ agents are told to use; line 137 promises "a typed Fresh route, a colocated hydrating island, and
+ its query-loader seam").
+- Behavioral consequence measured in the field: #1333 body (Wave-6 `rickylabs/loom` built routes
+ with hand-rolled tables and a 676-line island); #1090 holds the observational box "an agent
+ building a data screen runs `ui:add` or records why not".
+
+## Current surface
+
+Three files, none of which loads data. Two island conventions from one command: `ui:add page
+--island` writes to `routes//(_islands)/`, `ui:add island` writes to top-level `islands/`.
+`UiAddCommandInput` (`add-ui-input.ts`) omits `route`, `island` and `query`, so the exported public
+type under-describes the public CLI.
+
+## Target contract
+
+`ui:add page --island` emits a **working data screen** — the minimal composable core of the
+T2-01 slice, not a second generator:
+
+1. The page uses the typed route surface (registered through `router.ts`/`appRoutes`, not an inline
+ `createRouteReference`) and a real layer loader.
+2. The `(_shared)` file contains a cache-first loader bound to a real query factory and returning
+ `cachedAt`; it is not an empty object.
+3. The island is a `QueryIsland` + `useIslandQuery` bound to the factory's `clientKey`, seeded with
+ the loader's data and `initialDataUpdatedAt` (T2-07) — not a counter.
+4. `ui:add island --query` emits an island that actually issues a query.
+5. When the target app has no service/contract to bind, the command **says so and names the verb to
+ run first** rather than emitting a placeholder that type-checks and does nothing.
+6. Island placement is one documented convention, with the divergence between the two commands
+ resolved explicitly.
+7. `--force` and `--dry-run` exist, matching the rest of the CLI.
+8. `UiAddCommandInput` declares every accepted option.
+
+## Acceptance
+
+- [ ] `ui:add page --island` emits a page, loader and island that fetch and render real
+ contract-derived data in a scaffolded project.
+- [ ] The emitted island contains no `useSignal` counter and the emitted `(_shared)` file contains
+ no empty `queryLoaders` object.
+- [ ] `ui:add island --query` emits an island that issues a query with a factory-derived key.
+- [ ] The emitted page registers its route through `router.ts`/`appRoutes`.
+- [ ] Island placement follows one documented convention across both commands.
+- [ ] `--force` and `--dry-run` are supported and documented.
+- [ ] `UiAddCommandInput` declares `route`, `island`, `query` (and `app`, per T2-03).
+- [ ] Running the command in an app with no bindable contract exits non-zero with the prerequisite
+ verb named, and writes no files.
+- [ ] Negative test: a golden test fails if the emitted island imports `@preact/signals`' `useSignal`
+ as its only behavior, or if the emitted loader module exports an empty object.
+- [ ] Negative test: the command's own `--help` text is asserted against the emitted file set, so
+ the description and the emission cannot diverge again.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` scaffolds a page via this command and
+ type-checks the app containing it.
+
+## Boundaries
+
+- **T2-01** owns the full slice generator (`--form`/`--partial`/`--stream`, route-local
+ `(_components)`, ownership comments). If the owner consolidates, this issue is that generator's
+ minimal mode — state the decision, do not ship two generators.
+- **T2-03** owns where the files land; do not re-fix app resolution here.
+- **T2-02** owns the query factory this island binds; do not emit ad-hoc client wiring.
+- **#1333** owns the default app's own routes; this issue changes the *generator*, not the shipped
+ example routes.
+- **#1090** owns the observational measurement of whether agents adopt the command — do not add an
+ agent-run box to this issue's acceptance.
+- **#1102/#1197** own MCP/agent discovery; this is not a discovery fix.
+
+## Docs/consumer proof
+
+The proof is a generated screen, not a paragraph: `scaffold.runtime` runs the command and
+type-checks the result, the app `AGENTS.md` example matches the real emission, and the Web Layer
+how-to that names the triad shows the actual generated files. #1090 separately observes whether an
+unfamiliar agent reaches for the command — that measurement stays on #1090.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/web-layer.md` §8 and `research/repo-audit/mcp-cli.md` §3.5; every line
+re-verified against worktree `fac9e339042c`. No GitHub mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-05-design-registry-sync-gate.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-05-design-registry-sync-gate.md
new file mode 100644
index 0000000000..e04f690021
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-05-design-registry-sync-gate.md
@@ -0,0 +1,98 @@
+# fix(scaffold): the generated /design/components gallery lists 50 of 66 registry items — the whole AI collection is invisible and no gate compares them — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-05 · **Proposed milestone:** 0.0.6 (small, self-contained, and a stated
+prerequisite for #1333's `/design` acceptance; drafted inside the new-0.0.7 pack because it belongs
+to the generation pillar, but it does **not** depend on the generator train and should not wait for
+it) · **Labels:** `type:fix` `area:cli` `area:fresh-ui` `priority:p1` `status:triage` ·
+**Depends on:** none
+
+## Summary
+
+The scaffolded `/design/components` gallery reads a hand-copied snapshot of the `@netscript/fresh-ui`
+registry that declares 50 items while the live manifest has 66. The 16 missing entries are the
+entire `ai` collection plus `donut` and `dropzone`. `netscript ui:add` / `ui:list` resolve against
+the **live** manifest, so the CLI can install components the generated "living design reference"
+says do not exist — and the generated `AGENTS.md` points coding agents at exactly that gallery. No
+task, test or CI step compares the two, so the snapshot re-rots on the next registry addition.
+
+## Evidence
+
+- `research/repo-audit/scaffold-doctrine.md` §2.1 (D1) and `research/repo-audit/web-layer.md` §7.1 —
+ both computed the same 16-item diff.
+- Repo, verified at `fac9e339042c`:
+ - `packages/cli/src/kernel/assets/app/routes/(design)/design/(_shared)/registry.ts.template:1-4`
+ self-describes as a snapshot "regenerate when the registry changes"; `:28` declares
+ `total: 50`.
+ - Live manifest: `deno eval` on `packages/fresh-ui/registry.manifest.ts` →
+ `items 66 collections 8 0.1.0`; collections are
+ `foundation, ai, forms-core, surface-core, feedback-core, layout-foundations, dashboard-blocks,
+ desktop`.
+ - Missing from the snapshot: `avatar`, `citation-chip`, `code-block`, `model-selector`,
+ `tool-call-card`, `chart-block`, `donut`, `prompt-input`, `message`, `markdown`,
+ `command-palette`, `search`, `dropzone`, `chat-render`, `mcp-ui-widget`, `render-ui`.
+ - The CLI reads the live manifest:
+ `packages/cli/src/kernel/application/ui/registry.ts` imports `freshUiRegistryManifest` from
+ `@netscript/fresh-ui` and selects items from it.
+ - The only existing drift test — `packages/fresh-ui/tests/registry-doc-drift.test.ts:4-18` —
+ compares `registry.ts` JSDoc **collection names** against the manifest and never looks at the
+ CLI snapshot.
+ - Agent-facing pointer: `packages/cli/src/kernel/templates/app/agent-conventions.ts:37` routes
+ agents to `/design/composition`.
+
+## Current surface
+
+Two sources of truth for one catalog: `packages/fresh-ui/registry.manifest.ts` (authoritative, read
+by the CLI) and the CLI's hand-copied `registry.ts.template` (read by the generated gallery). They
+disagree by 16 items and by construction will disagree again after the next registry change.
+
+## Target contract
+
+1. The generated `/design/components` catalog is **derived from the manifest**, not hand-copied —
+ either generated at scaffold time from `freshUiRegistryManifest` or checked against it by a gate
+ that fails on any difference in item names, kinds, collections, `layer`, or the declared `total`.
+2. The gate runs in CI on the same lane as the other scaffold checks and names the drifting items in
+ its failure output.
+3. The gate is symmetric: adding a registry item without updating the generated catalog fails, and
+ listing a catalog item that the manifest does not have fails.
+4. `registryMeta.version`/`total` cannot silently disagree with the manifest.
+
+## Acceptance
+
+- [ ] The generated `/design/components` catalog contains all 66 current registry items, including
+ the full `ai` collection.
+- [ ] The catalog's item names, kinds, layers and collection membership match the manifest exactly.
+- [ ] `registryMeta.total` and `registryMeta.version` are derived from the manifest.
+- [ ] A drift gate compares the generated catalog against `freshUiRegistryManifest` and names the
+ differing items on failure.
+- [ ] Negative test: adding a fixture item to the manifest without regenerating the catalog fails
+ the gate.
+- [ ] Negative test: removing an item from the manifest while the catalog still lists it fails the
+ gate.
+- [ ] gate: the drift gate runs in CI on every change to `packages/fresh-ui/registry.manifest.ts` or
+ the CLI design assets.
+
+## Boundaries
+
+- **#1333** owns the acceptance item "`/design` and `/design/composition` are named and linked as
+ the living design/component reference" — this issue makes that reference *true* and must not
+ restate #1333's other boxes or close it.
+- **#1335** owns the repo-wide conformance inventory; this is one row of it, filed separately
+ because it is a concrete mechanical defect with a gate.
+- **#946 / #922** own plugin-contributed UI; contributed items are out of scope until that seam
+ exists.
+- Not in scope: the `packages/fresh-ui` exclusion from root `check`/`lint`
+ (`web-layer.md` §11.3) — that belongs to the T6 quality pack; and the token pipeline
+ (`tokens:check`), which already has a gate.
+
+## Docs/consumer proof
+
+A scaffolded project whose `/design/components` page lists every installable item is the proof: a
+reader can pick any name from the gallery, run `netscript ui:add `, and get it. The negative
+gate is the durable proof that the gallery cannot drift back.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/scaffold-doctrine.md` §2.1/D1 and `research/repo-audit/web-layer.md` §7.1;
+the 66-item count and the 16-item diff re-verified against worktree `fac9e339042c`. No GitHub
+mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-06-crud-example-route-alias.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-06-crud-example-route-alias.md
new file mode 100644
index 0000000000..f342846128
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-06-crud-example-route-alias.md
@@ -0,0 +1,88 @@
+# fix(scaffold): appRoutes.crudExample aliases serviceExample so /examples/crud is unreachable, and a template test asserts the alias — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-06 · **Proposed milestone:** 0.0.6 (a one-line generated-output defect plus a test
+correction; drafted inside the new-0.0.7 pack for topical grouping but independent of the generator
+train) · **Labels:** `type:fix` `area:cli` `area:fresh` `priority:p1` `status:triage` ·
+**Depends on:** none
+
+## Summary
+
+The generated app's route alias map points `crudExample` at the service-example route. Both example
+cards on the home page and the examples index therefore navigate to `/examples/`, and
+the CRUD example route that the scaffold also emits is unreachable from the generated UI. The defect
+is locked in by a template test that asserts the aliasing line verbatim, so any correct fix fails
+the suite first — the test must change with the template.
+
+## Evidence
+
+Repo, verified at `fac9e339042c`:
+
+- `packages/cli/src/kernel/assets/app/router.ts.template:33-34`:
+ ```ts
+ serviceExample: routes.examples.serviceExample,
+ crudExample: routes.examples.serviceExample, // same target
+ ```
+- `packages/cli/src/kernel/assets/app/routes/examples/crud.tsx.template:6` binds the page to
+ `routes.examples.crud.$route` — a real, distinct route.
+- Link sites that consume the wrong alias:
+ `packages/cli/src/kernel/assets/app/routes/examples/index.tsx.template:15` and
+ `packages/cli/src/kernel/assets/app/routes/index.tsx.template:24`, both
+ `href: appRoutes.crudExample.href()`.
+- The bug is asserted as expected output:
+ `packages/cli/src/kernel/templates/app/route-templates_test.ts:76` —
+ `assertStringIncludes(output, 'crudExample: routes.examples.serviceExample,');`
+- Corpus: `research/repo-audit/web-layer.md` §10 and gap-register item 3; `§13` records that no
+ board issue covers it.
+
+## Current surface
+
+`appRoutes` exposes two names for one route. The generated app ships a "CRUD" card that does not
+lead to the CRUD page, and the CRUD page has no inbound link. A regression test enforces the alias,
+which is why the defect survived template edits.
+
+## Target contract
+
+1. `appRoutes.crudExample` resolves to the CRUD route (`routes.examples.crud`), so every generated
+ link reaches the page it names.
+2. `route-templates_test.ts` asserts the corrected alias, and the assertion is written so that a
+ future alias collapse fails rather than passes.
+3. A structural check makes the class of defect visible, not just this instance: no two distinct
+ `appRoutes` keys may resolve to the same route target unless the duplication is explicit and
+ commented.
+4. Adjacent naming/IA note recorded, not fixed here: `routes/examples/crud.tsx.template` renders
+ three hard-coded records with no create/update/delete, while the real CRUD flow lives in
+ `ServiceShowcaseLab` on the service-example route. Renaming or reworking that example is a
+ separate product decision (see Boundaries).
+
+## Acceptance
+
+- [ ] `appRoutes.crudExample` targets the CRUD route in the generated `router.ts`.
+- [ ] Both generated link sites navigate to `/examples/crud` in a scaffolded project.
+- [ ] `route-templates_test.ts` asserts the corrected mapping and no longer asserts the alias.
+- [ ] Negative test: a check fails when two `appRoutes` keys resolve to the same route target
+ without an explicit annotation.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` type-checks the generated app with
+ the corrected router.
+
+## Boundaries
+
+- **#1333** owns whether the example routes are redesigned at all; this issue only fixes the alias
+ so the *existing* example is reachable. Do not fold the "is `crud.tsx` really CRUD?" product
+ question into this fix — record it as an amendment to #1333 instead.
+- **#1335** owns the conformance inventory that would have caught this class.
+- **T2-01** owns route registration for *newly generated* slices; this is the shipped template.
+- Not in scope: the layer→partial stringly-typed binding (`web-layer.md` §2.3) and the inline
+ `createRouteReference` in `ui:add page` (T2-04).
+
+## Docs/consumer proof
+
+A scaffolded project where clicking the "CRUD" card lands on `/examples/crud` is the proof; the
+corrected template test plus the duplicate-target check keep it true. No docs page currently claims
+otherwise, so no docs change is required — if one is found during implementation, it changes in the
+same PR.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/web-layer.md` §10; every line re-verified against worktree `fac9e339042c`.
+No GitHub mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-07-cachedat-initialdataupdatedat-wiring.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-07-cachedat-initialdataupdatedat-wiring.md
new file mode 100644
index 0000000000..2d751a7984
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.7-typed-seams-generation/T2-07-cachedat-initialdataupdatedat-wiring.md
@@ -0,0 +1,95 @@
+# fix(scaffold): the canonical island never passes initialDataUpdatedAt, so the loader's cachedAt is computed, displayed and discarded — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T2-07 · **Proposed milestone:** 0.0.6 (one template line plus a regression test and a
+migration note; drafted inside the new-0.0.7 pack because T2-01/T2-04 must emit the same wiring, but
+it does not depend on them) · **Labels:** `type:fix` `area:cli` `area:fresh` `area:docs`
+`priority:p2` `status:triage` · **Depends on:** none (T2-01 and T2-04 must inherit the fixed shape)
+
+## Summary
+
+PR #1265 added `initialDataUpdatedAt` to `IslandQueryOptions` specifically so a server-rendered
+snapshot keeps its real cache age across hydration. The generated app's canonical island computes
+`cachedAt` in its loader, passes it through as a prop — and then uses it only as a display label,
+never as `initialDataUpdatedAt`. The differentiator seam ships unexercised in the one example the
+scaffold designates as canonical, so first paint tells TanStack the snapshot is fresh as of
+hydration. The most polished downstream consumer made the same mistake independently, which is
+evidence the seam is undiscoverable rather than unwanted.
+
+## Evidence
+
+- `research/repo-audit/web-layer.md` §4.2 and gap-register item 11;
+ `research/external/eis-chat.md` §6 and §11 S10.
+- Repo, verified at `fac9e339042c`:
+ - `packages/fresh/src/application/query/query-types.ts:135-136` — the option exists:
+ "Timestamp when the server loaded `initialData`, used to preserve its cache age."
+ - `packages/cli/src/kernel/assets/app/routes/examples/(_shared)/service-showcase.ts.template:67`
+ computes `const cachedAt = Date.now();` and `:77` returns it.
+ - `packages/cli/src/kernel/assets/app/routes/examples/(_islands)/ServiceShowcaseLab.tsx.template:43-49`
+ calls `useQuery({ queryKey, queryFn, initialData: props.initialList,
+ staleTime: 15_000 })` — **no `initialDataUpdatedAt`**; `:105` uses `props.cachedAt` only to
+ render a "Cached at" stat.
+- Downstream consumer evidence (`research/external/eis-chat.md` §6): `cachedAt` is threaded through
+ three files (`routes/skills/index.tsx:49` → `(_components)/skills-view.tsx:40-48` →
+ `islands/SkillsPanel.tsx:34`) and then discarded at `SkillsPanel.tsx:74`
+ (`cachedAt: _cachedAt`).
+
+## Current surface
+
+The seam exists, is documented in the type, and is used nowhere: not in the scaffold, not in the
+reference consumer. Nothing fails when it is omitted, so the omission is invisible.
+
+## Target contract
+
+1. The canonical generated island passes `initialDataUpdatedAt: props.cachedAt` alongside
+ `initialData`, so the hydrated cache entry carries its true server age and `staleTime` behaves as
+ documented.
+2. Both showcase variants (`ServiceShowcaseLab.tsx.template` and the memory variant) use the same
+ shape, so the two generated paths teach one dialect.
+3. A regression test asserts that any generated island seeded with `initialData` from a loader that
+ returns `cachedAt` also passes `initialDataUpdatedAt` — the assertion is on the generated output,
+ not on prose.
+4. T2-01/T2-04 emit the same wiring by construction.
+5. **One migration note** for consumers upgrading from beta-era pins tells them to (a) consume a
+ threaded `cachedAt` into `initialDataUpdatedAt` instead of dropping it, and (b) delete the
+ `clientKey → queryKey` `as unknown as` casts that #1265 made unnecessary. The cast half is
+ #1245's remnant scope; this issue contributes the `initialDataUpdatedAt` half and links to it
+ rather than duplicating it.
+
+## Acceptance
+
+- [ ] The generated canonical island passes `initialDataUpdatedAt` from the loader's `cachedAt`.
+- [ ] Both showcase island variants use the same option shape.
+- [ ] A regression test asserts the generated island passes `initialDataUpdatedAt` whenever it
+ passes loader-seeded `initialData`.
+- [ ] Negative test: removing `initialDataUpdatedAt` from the template fails that test.
+- [ ] A migration note documents consuming `cachedAt` into `initialDataUpdatedAt` for apps upgrading
+ from beta-era pins.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` type-checks the generated app with
+ the corrected island.
+
+## Boundaries
+
+- **#1245** owns the remaining island-query type work — the `getIslandQueryClient()` `@throws`
+ JSDoc that documents a guard the body does not implement
+ (`packages/fresh/src/application/query/query-client.ts:26-27` vs its body), the `clientKey`
+ asymmetry, and the consumer note about deleting the six copied casts. #1245 is ~75% landed via
+ #1265 and is proposed for **rescope, not re-implementation**; do not re-file its scope here and do
+ not close it with this PR.
+- **#1333** owns the default app's broader modernization; this is one line inside it that can land
+ independently.
+- **T2-01/T2-04** own the generators; this issue fixes the shipped example they should mirror.
+- Not in scope: the duplicate `useLiveQuery` exports and `IslandLiveQueryResult.details`
+ (`web-layer.md` §4.4), or any change to `packages/fresh` source — this is scaffold + docs only.
+
+## Docs/consumer proof
+
+The proof is behavioral: in a scaffolded project, a page rendered from a server snapshot older than
+`staleTime` refetches immediately after hydration, and one rendered from a fresh snapshot does not.
+The Web Layer query/cache-first documentation shows the same three lines the scaffold emits, and the
+migration note gives a beta-era consumer an exact diff to apply.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from
+`research/repo-audit/web-layer.md` §4.2 and `research/external/eis-chat.md` §6/S10; every line
+re-verified against worktree `fac9e339042c`. No GitHub mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-02-service-layout-child.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-02-service-layout-child.md
new file mode 100644
index 0000000000..69c0e71383
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-02-service-layout-child.md
@@ -0,0 +1,126 @@
+# fix(scaffold/service): generated services have no internal layering — the v1 router template teaches ORM-in-handler and `deno task test` fails on an empty tree — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T3-02 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice") ·
+**Labels:** `type:fix` `area:cli` `area:service` `area:docs` `priority:p1` `status:triage` ·
+**Depends on:** none
+
+Part of #1335.
+
+## Summary
+
+A scaffolded service has exactly one internal subdivision — `src/routers/` — and no guidance
+anywhere about where business logic, repositories, external clients, or domain types belong. The
+generated `v1` router therefore embeds Prisma query construction, a sort-field allow-list, and
+pagination arithmetic directly in the oRPC handler, and `service add-handler` appends every new
+procedure into that same file. That is the handler-as-god-object shape the Architecture Doctrine
+forbids for framework code, generated as the example every consumer copies. The same generated
+`deno.json` declares `test: deno test -A src/` against a tree that contains no test module, so
+`deno task test` exits 1 on a freshly scaffolded service.
+
+## Evidence
+
+- `packages/cli/src/kernel/adapters/service/scaffolder.ts:39-102` — the scaffolder creates exactly
+ three directories (`services//`, `src/`, `src/routers/`) and four files (`deno.json`,
+ `src/main.ts`, `src/router.ts`, `src/routers/health.ts`, `src/routers/v1.ts`). No `domain/`,
+ `application/`, `ports/`, `adapters/`, `tests/`, or `README.md`.
+- `packages/cli/src/kernel/assets/service/routers/v1.ts.template:12-53` — `SORTABLE_FIELDS`, the
+ delegate accessor, `orderBy()`, `skip`/`take` math and `Promise.all([findMany, count])` all live
+ inside `list.handler`.
+- `packages/cli/src/kernel/templates/service/generate-service-deno-json.ts:65` —
+ `test: 'deno test -A src/'`. Executed check on a directory containing only a non-test module:
+ `error: No test modules found`, exit code **1**.
+- `packages/cli/src/public/features/services/add-handler/add-service-handler.ts:29-60` — the command
+ resolves `services//src/routers/.ts` and appends into it;
+ `packages/cli/src/kernel/adapters/service/router-source.ts:26-35` shows the emitted stub is a bare
+ `.handler(async ({ input }) => { void input; throw new Error('Not implemented: …'); })` with no
+ seam to place logic anywhere else.
+- Doctrine vocabulary the framework holds itself to but never generates:
+ `docs/architecture/doctrine/05-folder-structure.md` (role table + strict layering direction) and
+ `docs/architecture/doctrine/06-archetypes.md:50-62` (minimum viable shapes). Both chapters are
+ scoped to `packages/`/`plugins/`; there is no consumer-facing equivalent.
+- `grep` of `docs/site/services-sdk/services.md` for `internal/|src/routers|folder` returns nothing.
+ The only structure documentation is the file tree at
+ `docs/site/services-sdk/how-to/add-a-service.md:77-88`, which matches the generator and stops there.
+- Corpus: `research/repo-audit/services-sdk.md` §5.2, §5.3 (S18, S19);
+ `research/preplan-package.md` ("Service-layout child under #1335").
+
+## Current surface
+
+```
+services//
+├── deno.json # tasks: check | dev | start | test (test → src/, no test modules)
+└── src/
+ ├── main.ts # defineService(router, {...})
+ ├── router.ts # { v1: { : {...V1, health} } }
+ └── routers/
+ ├── v1.ts # ORM + pagination + sort policy inline in handlers
+ └── health.ts
+```
+
+Contracts live in a separate versioned workspace (`contracts/versions/v/.contract.ts`),
+which is the one boundary the scaffold does teach.
+
+## Target contract
+
+A **collapsible** internal vocabulary for generated services, derived from the doctrine but stated
+for consumers, and generated by the CLI rather than described in prose:
+
+| Folder | Role in a generated service | Collapses into |
+| --- | --- | --- |
+| `src/domain/` | Entity types, invariants, pure policy (sortable fields, state machines). No IO. | `src/` for a single-entity service |
+| `src/application/` | Use-cases the router calls. One file per operation or per entity. | `src/routers/.ts` only when a service has one trivial entity |
+| `src/ports/` | Interfaces the service needs (repository, clock, outbound client). | `src/application/` |
+| `src/adapters/` | One file per technology (`prisma--repository.ts`, `http--client.ts`). | — (never collapses once a second technology exists) |
+| `src/routers/` | Thin oRPC adapter: bind contract procedure → use-case, map errors. **No ORM, no pagination math, no policy.** | — |
+| `src/auth/` | Service-local authenticator/authorizer wiring over `@netscript/service/auth`. | `src/main.ts` |
+| `tests/` | Cross-module suites; single-module tests colocated as `*_test.ts`. | colocated only |
+
+Rules: the router is always thin; `domain`/`ports`/`application` may collapse **upward** for a small
+service and the decision table says exactly when; `adapters` never collapses into `routers`. The
+generator emits the collapsed shape for a single-entity service and the expanded shape once a service
+has two or more entities, and `service add-handler` writes the use-case into `src/application/` and a
+binding line into the router instead of appending a logic-bearing stub to `routers/.ts`. A
+migration path documents how an existing generated service moves from the collapsed to the expanded
+shape without touching its contract.
+
+## Acceptance
+
+- [ ] A consumer-facing service-layout page documents the vocabulary and the collapse rules.
+- [ ] The page carries a decision table stating when each folder collapses and when it must not.
+- [ ] The generated single-entity service keeps the collapsed shape and still compiles.
+- [ ] A generated two-entity service emits `domain/`, `application/`, `ports/`, `adapters/`.
+- [ ] The generated `v1` router contains no ORM call, pagination math, or sort policy.
+- [ ] `service add-handler` writes a use-case file and a thin router binding.
+- [ ] A generated service ships at least one test module and `deno task test` exits 0.
+- [ ] Negative: a router template containing a `db.`/delegate call fails a scaffold golden test.
+- [ ] Negative: `deno test -A src/` never runs against a tree with no test module.
+- [ ] The generated multi-entity project passes check, lint, fmt and test with no `any` and no `as unknown as`.
+- [ ] A migration note tells existing projects how to expand a collapsed service.
+- [ ] Scaffold golden fixtures pin the emitted tree for both shapes.
+
+## Boundaries
+
+Do not duplicate: **#1335** (this is a child of that umbrella — the umbrella owns the repo-wide
+conformance inventory, this issue owns only the service internal layout); **#1333** (default *app*
+frontend idiomacy and dynamic app naming — frontend tree, not service tree); **#1263** (generated
+by-id handler returns 500 instead of a defined 404 — an error-mapping defect in the same template,
+fixed independently); **#1328** (CLOSED — scaffold-owned quality gate implementation; do not reopen);
+**#829** (official *plugins* ship compile-able `./services` entrypoints — plugin-owned services, not
+generated ones); **#979**/**#980** (Aspire port pinning); **T3-03** (what a command *is*; this issue
+decides only where it lives). This issue does **not** change `@netscript/service` runtime behavior,
+the contracts workspace layout, or the doctrine chapters that govern `packages/`/`plugins/`.
+
+## Docs/consumer proof
+
+A services-sdk page that a stranger can follow to place a repository, a use-case and a router binding
+without reading framework source; the generated two-entity project as the executable example; and the
+scaffold runtime E2E proving the emitted tree matches the documented tree. Adoption is proven when the
+generated router contains no ORM call — i.e. the anti-pattern is no longer copyable.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Pack T3 of the Fable-5
+remediation plan (`SYNTHESIS.md` §4); sources `research/repo-audit/services-sdk.md` S18/S19 and the
+service-layout child named in `research/preplan-package.md`. `research/repo-audit/services-sdk.md` §7
+records that no open issue covers S18; re-checked against `research/github-board-open.md` §7. Draft
+only; no board mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-03-command-kit-implementation.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-03-command-kit-implementation.md
new file mode 100644
index 0000000000..ec99b3b632
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-03-command-kit-implementation.md
@@ -0,0 +1,111 @@
+# feat(service): no supported way to write a production command — transaction, expected version, idempotency, audit and outbox are hand-rolled in every generated app — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T3-03 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice") ·
+**Labels:** `type:feat` `area:service` `area:database` `area:cli` `area:telemetry` `priority:p1`
+`status:triage` · **Depends on:** T3-01 (RFC-B ratified), T3-02 (where the command file lands)
+
+## Summary
+
+Everything a consumer needs to change business state correctly exists in the framework and nothing
+composes it: `withTransaction` has zero callers, idempotency lives only inside the worker runtime,
+and there is no outbox, audit row, expected-version condition, or command span anywhere. Generated
+services write state with a bare ORM call, so the first production requirement a product hits —
+"do not apply this twice, record why it changed, and tell the rest of the system" — is invented per
+app. This issue implements the seam ratified by RFC-B and proves it by generating a representative
+**non-CRUD** command that writes business state, an audit row, an outbox row and an idempotent
+receipt atomically.
+
+## Evidence
+
+- `packages/database/mod.ts:128` — `withTransaction` exported, documented at
+ `docs/site/reference/database/index.md:52`, **zero callers** across `packages/`, `plugins/`,
+ `docs/`. `$transaction` appears in no scaffold template and no services-sdk page.
+- `packages/cli/src/kernel/assets/service/routers/v1.ts.template:25-53` — generated writes are
+ single delegate calls with no transaction and no version condition.
+- `packages/plugin-workers-core/src/ports/worker-idempotency-port.ts` — `claim`/`markApplied`/
+ `release` exists for worker deliveries, KV-backed
+ (`src/stores/kv-worker-idempotency-store.ts`), and cannot join a database transaction.
+- `grep -rln "outbox"` over `packages/`, `plugins/`, `docs/` → no match; no audit-record primitive.
+- `packages/telemetry/src/attributes/helpers.ts` — job/saga/execution attribute helpers exist, no
+ command vocabulary; `src/attributes/spans.ts:45-46` — `rpc.client`/`rpc.server` only.
+- `packages/contracts/src/application/contract-primitives.ts:21-52` — the six shared error codes
+ contain no conflict code, so optimistic-concurrency failure has no declared representation.
+- Wave-6: R3 (billing, the only GO-grade run) owns its entire command layer in app space and needed
+ two evaluator `FAIL_FIX` rounds before scheduled state actually advanced; its relational
+ `trigger_events`/`job_execution_history` projections were empty while authority lived in KV. R2
+ built an app-owned retry/compensation executor and never adopted `plugin-sagas`
+ (`research/wave-6-runs.md` §R2/§R3, X14).
+
+## Current surface
+
+A handler receives `ctx.db`, `ctx.principal` and `ctx.traceHeaders`
+(`packages/service/src/builder/service-builder-impl.ts:259-282`) and is on its own from there. There
+is no unit-of-work object, no receipt store, no side-record buffer, no relay, and no way to express
+"apply only if the row is still at version N" other than a read-modify-write race. `IsolationLevel`
+and `TransactionOptions` exist (`packages/database/ports/database-client.ts:59-77`) and are never
+selected. Multi-commit work has an owner (`sagaCompensate`,
+`packages/plugin-sagas-core/src/public/mod.ts:43`); single-commit work has none.
+
+## Target contract
+
+RFC-B's kit, shipped as ratified: a `UnitOfWorkPort` with published `UnitOfWorkCapabilities` and a
+Prisma adapter over the existing `withTransaction`; `expectVersion` lowering to a conditional update
+with an affected-row check and a **typed** conflict error added to the shared error map; an
+idempotency receipt keyed by `(scope, commandName, idempotencyKey)` and guarded by a canonical
+request hash, replaying the stored response instead of repeating the effect and rejecting key reuse
+under a different hash; buffered `audit()` and `publish()` writes flushed inside the same commit; a
+generated outbox relay worker job that delivers at-least-once with a dedupe key; a `command` span
+plus `createCommandAttributes` carrying the same `correlationId` written into the audit and outbox
+rows; and a fault-injection conformance suite with named seams. Stores that cannot provide
+same-commit side records refuse composition at build time with a message naming the store, rather
+than degrading silently.
+
+The generation half: the CLI emits one representative non-CRUD command in the generated project — an
+operation that is not create/read/update/delete on a single row, that carries an idempotency key,
+that asserts an expected version, and that commits business state + audit + outbox + receipt in one
+transaction — plus its relay job and its tests.
+
+## Acceptance
+
+- [ ] `@netscript/service` exposes the command kit on a documented subpath with `deno doc --lint` clean.
+- [ ] A Prisma unit-of-work adapter delegates to `withTransaction` and selects an isolation level.
+- [ ] `UnitOfWorkCapabilities` is public and read by composition, not by prose.
+- [ ] The shared error map declares the concurrency-conflict code used by `expectVersion`.
+- [ ] A version mismatch returns a typed, contract-declared error, never a 500.
+- [ ] A replayed idempotency key returns the first response and produces no second effect.
+- [ ] Key reuse with a different request hash returns a typed conflict.
+- [ ] Audit and outbox rows are absent after a fault injected before commit.
+- [ ] The generated relay redelivers an unpublished outbox row after a publish fault.
+- [ ] The `command` span carries the documented attributes and the audit/outbox `correlationId`.
+- [ ] A command requiring same-commit side records refuses to compose on a KV-only store.
+- [ ] The generated project ships a non-CRUD command using the kit end to end.
+- [ ] Removing the kit from that command makes the conformance suite fail.
+- [ ] The generated project passes check, lint, fmt and test with no `any` and no `as unknown as`.
+- [ ] `gate:e2e` — scaffold runtime E2E exercises the command and observes the relay publish.
+
+## Boundaries
+
+Do not duplicate: **T3-01/RFC-B** (this implements it; it does not re-ratify it); **T3-02** (service
+folder placement); **#742** (saga definition versioning); **#884**/**#885** (org-aware authorization
+contracts and the auth conformance kit — the kit consumes `Principal` and never decides
+authorization); **#1332** (DB-schema-first docs); **#1263** (defined-404 defect in the CRUD template);
+**#1326**/**#1329** (stream producer durability and SSE envelope — the relay may publish to a stream
+but does not change stream semantics); **#1280** (backing-service health, blocked upstream); **#1278**
+(type-soundness inventory — this issue must satisfy it, not restate it). Explicit non-goals: no
+billing/ledger/money vocabulary, no event-sourcing runtime, no audit taxonomy, no ORM abstraction, no
+distributed-transaction emulation, and no exactly-once delivery claim.
+
+## Docs/consumer proof
+
+A services-sdk how-to that replaces the hand-rolled pattern with the kit and type-checks against the
+published export map; the generated non-CRUD command as the executable reference; and a measured
+Wave-7 row showing an unfamiliar agent either using the generated command path or recording an
+explicit rejection. The negative proof is the load-bearing one: deleting the kit from the generated
+command must turn the suite red.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Pack T3 of the Fable-5
+remediation plan (`SYNTHESIS.md` §4); source item **H** in `research/preplan-package.md`, evidence
+from `research/wave-6-runs.md` and `research/repo-audit/services-sdk.md`. Checked against
+`research/github-board-open.md` §7 — no existing owner. Draft only; no board mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-04-outbound-webhook-recipe.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-04-outbound-webhook-recipe.md
new file mode 100644
index 0000000000..0783f35578
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T3-04-outbound-webhook-recipe.md
@@ -0,0 +1,101 @@
+# docs(orchestration-runtime): outbound webhook delivery has no recipe or template — triggers ship inbound ingress only, so every product hand-rolls signing, retry and dead-lettering — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T3-04 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice") ·
+**Labels:** `type:docs` `area:docs` `area:plugins` `area:cli` `priority:p2` `status:triage` ·
+**Depends on:** T3-03 (soft — the outbox relay is the recipe's preferred delivery substrate; the
+recipe can ship without it with a stated at-most-once caveat)
+
+## Summary
+
+NetScript receives webhooks well and says nothing about sending them. `defineWebhook` plus the
+ingress/verifier seam is a first-class inbound capability with a tutorial chapter, but there is no
+guidance, template, or primitive for *outbound* delivery — signing a payload, retrying with backoff,
+capping attempts, dead-lettering, and letting a receiver deduplicate. Agent-built products therefore
+invent it: the Wave-6 workflow builder wrote `fire-webhook` jobs by hand and the billing run
+simulated outbound calls entirely. This was adjudicated **not a framework defect** — the capability
+is composable from `plugin-workers` today — so the fix is a first-party **recipe plus a generated
+worker template**, not a new plugin or a new primitive.
+
+## Evidence
+
+- Inbound is complete: `packages/plugin-triggers-core/README.md:7-12,60-97,142-145`
+ (`defineWebhook`, `TriggerIngressPort`, `WebhookVerifierPort`, HMAC verification, `enqueueJob`);
+ config surface `packages/config/src/domain/schemas/netscript-config-schema.ts:85,107-117`
+ (`type: 'webhook'`, `basePath: '/api/v1/webhooks'`);
+ docs `docs/site/orchestration-runtime/how-to/author-a-plugin.md:206,223,235` and the tutorial
+ chapter `docs/site/tutorials/storefront/05-shipping-webhook.md` — all inbound.
+- Outbound is absent: `grep -rn "outbound"` filtered to webhook/delivery over `packages/`,
+ `plugins/`, `docs/site` returns nothing; `grep -rln "deliverWebhook|WebhookDelivery|webhook-delivery"`
+ returns nothing.
+- The composable parts already exist: `defineJob` / `defineJobHandler` / `createJobTools`
+ (`packages/plugin-workers-core/mod.ts:17-24`), delivery idempotency
+ (`packages/plugin-workers-core/src/ports/worker-idempotency-port.ts`), and a dead-letter vocabulary
+ (`packages/queue/mod.ts:70-72` — `DeadLetterReason`, `DeadLetterRecord`, `DeadLetterStorePort`).
+- Wave-6: R2 hand-built `fire-webhook` jobs, which failed before token validation because the worker
+ had no service endpoint (`research/wave-6-runs.md` §R2 D2); R3 shipped "simulated webhooks"
+ (`research/wave-6-runs.md` §R3 product shape).
+- Adjudication: `SYNTHESIS.md` §3.5 — "Not a framework defect. Disposition: first-party **recipe +
+ worker template** (docs + generation), not a new plugin; p2".
+
+## Current surface
+
+A consumer who needs to notify a third party has: `defineJob` for the background execution,
+`enqueueJob` from a trigger or a service, KV/queue-backed retry inside the worker runtime, a
+dead-letter store, and `crypto.subtle` for HMAC. What they do not have is any page, example, or
+generated file telling them how to assemble those into a delivery that a receiver can trust and
+deduplicate — nor a statement of which guarantees the assembly does and does not provide.
+
+## Target contract
+
+1. **A recipe page** under the orchestration-runtime how-tos: outbound webhook delivery, covering the
+ endpoint registry (consumer-owned rows: url, secret reference, active flag), the signed request
+ shape (timestamp + payload HMAC in a documented header, receiver-side verification snippet), retry
+ with capped exponential backoff and a maximum attempt count, dead-lettering through the existing
+ `DeadLetterStorePort` vocabulary, a stable per-delivery id the receiver deduplicates on, and an
+ explicit guarantee statement: **at-least-once, never exactly-once**.
+2. **A generated worker template** the CLI can emit — one `defineJob` delivery job plus its handler,
+ parameterized by endpoint lookup, that reads its target from injected configuration rather than a
+ hardcoded URL (the exact failure R2 hit), records attempt/outcome, and dead-letters on exhaustion.
+3. **The outbox seam.** When the command kit (T3-03) is present, the recipe's canonical source of
+ deliveries is the outbox relay, so a webhook is announced only for state that actually committed.
+ Without it, the recipe states plainly that enqueueing from inside a request is at-most-once.
+4. **Non-goals stated on the page:** this is not a plugin, not a delivery SLA, not a subscription
+ management API, and not a receiver-side framework.
+
+## Acceptance
+
+- [ ] An outbound-webhook how-to exists under the orchestration-runtime section.
+- [ ] The page documents signing, retry/backoff caps, dead-lettering and receiver deduplication.
+- [ ] The page states at-least-once delivery and explicitly disclaims exactly-once.
+- [ ] The page's code samples type-check against published entrypoints.
+- [ ] A generated delivery worker template ships and reads its endpoint from injected config.
+- [ ] The template dead-letters after the configured maximum attempts.
+- [ ] Negative: a template with a hardcoded endpoint URL fails a scaffold golden test.
+- [ ] Negative: a delivery that exhausts retries is provably in the dead-letter store, not dropped.
+- [ ] Tests cover signature generation, backoff schedule, attempt cap and dead-letter routing.
+- [ ] The page cross-links inbound `defineWebhook` and names it as the opposite direction.
+- [ ] No new package or plugin is created by this issue.
+
+## Boundaries
+
+Do not duplicate: **#1329** (streams SSE envelope + OTEL propagation); **#1325** (triggers Redis
+adapter glue); **#554**/**#555** (`TriggerDlqPort` contract route and `DeadLetterStore` CLI/contract
+API — this recipe *uses* the dead-letter vocabulary and must not define a second one); **#1208**
+(tutorial phase 1) and **#1210** (per-API web-layer deep dives) — this is an orchestration-runtime
+how-to, not a tutorial rewrite or a page-builder page; **#742** (saga versioning); **#878**
+(enterprise auth audit events). Explicitly out of scope: inbound webhook behavior, a webhooks plugin,
+a subscription/endpoint management API, and any change to `plugin-triggers` runtime semantics.
+
+## Docs/consumer proof
+
+The recipe is proven by the generated template compiling and running in the scaffold runtime E2E, by
+its samples type-checking against the published export map, and by a Wave-7 row recording whether an
+unfamiliar agent used the recipe or hand-rolled delivery again. If an agent still hand-rolls it, the
+recipe failed and the disposition (docs vs generation vs primitive) is re-opened with that evidence.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Pack T3 of the Fable-5
+remediation plan; disposition adjudicated in `SYNTHESIS.md` §3.5 from the verify-before-filing list in
+`research/preplan-package.md`. Checked against `research/github-board-open.md` §7 — no existing owner
+for outbound webhook delivery. Draft only; no board mutation performed.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-01-saga-publication-receipts.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-01-saga-publication-receipts.md
new file mode 100644
index 0000000000..45fb66d613
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-01-saga-publication-receipts.md
@@ -0,0 +1,124 @@
+# fix(sagas): publish receipts are discardable and the publisher silently falls back to 127.0.0.1:8092 — jobs report success while the saga never starts — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-01 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice", per
+SYNTHESIS §5.3 rename-shift) · **Labels:** `type:fix` `area:plugins` `area:aspire` `area:docs`
+`priority:p0` `status:triage` · **Depends on:** none (sequenced with T4-06 for port resolution and
+T4-08 for the detecting gate)
+
+## Summary
+
+`SagaPublisherResult` is a well-designed non-throwing discriminated union, but it is not a checked
+result: `await publisher.publish(m)` type-checks with the receipt thrown away, and the first-party
+sample job the scaffold writes into every new project does exactly that. Composed with the
+publisher's silent `http://127.0.0.1:8092` endpoint fallback, an unreachable `sagas-api` produces a
+rejected receipt that nobody reads, so the worker job returns `createSuccessResult(...)` while the
+saga never starts — no log, no telemetry event, no failed job. The identical code is embedded
+verbatim in the canonical documentation with the comment "a typed receipt comes back". This is the
+highest-severity composed defect in the runtime audit and no open issue owns it.
+
+## Evidence
+
+- Corpus: `research/repo-audit/runtime-plugins.md` §2.2 (silent-drop chain), §1.4 (fixed-port
+ fallback), §1.5 (discovery-key asymmetry), §8 ledger row 2; `SYNTHESIS.md` §1.4, §2 "Runtime
+ correctness", §6 (T4 pack, "saga receipt p0" — no existing owner).
+- `plugins/sagas/src/runtime/saga-publisher.ts:295-307` — `resolveServiceUrl()` ends
+ `?? \`http://127.0.0.1:${SAGAS_API_DEFAULT_PORT}\``; `plugins/sagas/src/constants.ts:11` sets
+ `SAGAS_API_DEFAULT_PORT = 8092`. Called from `saga-publisher.ts:164`.
+- `packages/plugin-sagas-core/src/integration/publisher/saga-publisher-port.ts:24-46` — the
+ `SagaPublisherReceipt | SagaPublisherRejected` union. Nothing forces a caller to discriminate it.
+- `plugins/workers/src/cli/official-sample-configuration.ts:393-403` — the scaffold-emitted job:
+ `await sagaPublisher.publish({ type: 'UserSettingsCreated', payload: { userId } });` followed
+ directly by `createSuccessResult({ userId, settingsCreated: true, source: 'scaffold-sample' })`.
+- `docs/site/durable-workflows/sagas.md:418` — the same body embedded in a `tabbedCode` block
+ labelled "verbatim from the scaffold", carrying the comment
+ `// This is the message the saga below consumes — a typed receipt comes back.`
+- `packages/sdk/src/discovery/service-url.ts:55-61` builds `services____http__0` with the raw
+ hyphen; `packages/aspire/src/application/build-vite-env-var-name.ts:50-66` normalizes every
+ non-alphanumeric to `_`. Only one form can match what Aspire exports for `sagas-api`; if the
+ server form is normalized too, every discovery lookup misses and drops to the 8092 fallback.
+- Contrast (the correct pattern already in-repo):
+ `packages/plugin-streams-core/src/application/stream-url-resolver.ts:99-133` throws a diagnostic
+ error rather than falling back to a port.
+- Adjacent open issues: #1326 (streams producer, p0), #1329 (SSE envelope, p0), #1325 (triggers KV
+ adapter), #979 (plugin resources pin host ports, unmilestoned).
+
+## Current surface
+
+`HttpSagaPublisher.publish()` never throws: non-2xx becomes `rejectedResult(..., retryable = status
+∈ {408,409,425,429,500,502,503,504})`, a transport throw becomes `rejectedResult(...,
+isRetryable(cause))` (`saga-publisher.ts:106-134`). `traceparent`/`tracestate` are propagated
+(`:280-289`). Endpoint resolution tries `baseUrl`, `services__sagas-api__https__0`,
+`services__sagas-api__http__0`, `SAGAS_API_URL`, `NETSCRIPT_SAGAS_URL`, then the 8092 literal. The
+same literal appears in `plugins/sagas/src/cli/adapters/runtime-api-client.ts:27` and
+`plugins/sagas/src/e2e/probes/probe-context.ts:3`. Since #1211 (`0b11ca47a`) scaffold ports are
+allocated from 49152–65535 (`packages/cli/src/kernel/domain/scaffold/default-port-allocation.ts:4-7`),
+so 8092 is not a plausible default in a generated project — it is a guess that connects to nothing,
+or worse, to another workspace's saga API.
+
+## Target contract
+
+1. **Publication result is non-ignorable.** Either `publish()` returns a type the compiler forces a
+ caller to discriminate before the value is dropped, or the port gains an explicit
+ throw-on-rejection entry point and a repo gate rejects a bare `await publisher.publish(...)`
+ whose result is unused. The chosen mechanism is stated once in the port's doc comment.
+2. **No silent endpoint guessing under an AppHost.** When an Aspire environment is detected (any
+ `services__*` key present, or an explicit `NETSCRIPT_ASPIRE` marker), a failure to resolve the
+ sagas endpoint raises a diagnostic error naming each source tried and the exact env key expected
+ — the `stream-url-resolver.ts` pattern. Outside an AppHost, a fixed default is permitted only
+ when it is explicitly configured, never as a trailing `??`.
+3. **Discovery keys are decided, not assumed.** One documented normalization for hyphenated
+ resource names, applied identically on the server and browser paths, with a test that records
+ what Aspire actually exports.
+4. **The shipped sample models the correct pattern.** The scaffold's `create-user-settings` job
+ discriminates the receipt, logs/telemeters the rejection, and fails the job on a non-retryable
+ rejection.
+5. **Correlation is provable from durable state.** For an accepted publish, the correlation key
+ written to the saga store and the `netscript.correlation.id` on the emitted spans agree, and that
+ agreement is asserted — not inferred from logs.
+
+## Acceptance
+
+- [ ] Discarding a saga publish result fails type-check or a named lint/quality gate.
+- [ ] The publisher raises a diagnostic error instead of falling back to `127.0.0.1:8092` when an
+ AppHost environment is detected.
+- [ ] The endpoint-resolution error names every source tried and the exact env key expected.
+- [ ] Hyphenated service-discovery keys use one documented normalization on server and browser
+ paths, with a test recording what Aspire exports for `sagas-api`.
+- [ ] The scaffold sample job discriminates the receipt and fails the job on a non-retryable
+ rejection.
+- [ ] `docs/site/durable-workflows/sagas.md` embeds the fixed sample and a docs test proves the
+ embedded code matches the scaffold source.
+- [ ] A negative test proves an unreachable sagas-api makes the sample job fail rather than succeed.
+- [ ] A negative test proves removing the endpoint-resolution error path fails a gate rather than
+ silently restoring a fixed-port default.
+- [ ] Tests cover restart, duplicate publish, out-of-order delivery, and sagas-api unavailable.
+- [ ] Correlation is proven from persisted saga state plus an OTEL trace, not from log text.
+
+## Boundaries
+
+- **#1326** owns `DurableStreamProducer` reconnect/buffer bounds; **#1329** owns the versioned SSE
+ envelope. Do not re-file or absorb either — streams receipts (`upsert()`/`delete()` return `void`,
+ `packages/plugin-streams-core/src/application/create-durable-stream.ts:167,205`) are named here
+ only as context.
+- **#1325** owns the triggers glue KV-adapter omission. **#979** (+ **#980**) own removing pinned
+ host ports and the E2E/docs prerequisites for endpoint resolution; T4-06 sequences behind #979 and
+ covers the *contribution declarations*. This issue covers only the sagas publisher hot path.
+- **#1280** (blocked upstream) owns backing-service health checks. Not in scope.
+- **Saga OOM remains a verify-first investigation row** (SYNTHESIS §3.6 / `preplan-package.md`
+ §Verify-before-filing). It is deliberately **not** part of this issue and must not be folded in;
+ it needs a repro on the current canary before anything is filed.
+- Saga compensation semantics (no prior-step rollback, unpersisted compensation state) are separate
+ runtime rows and are not fixed here.
+
+## Docs/consumer proof
+
+`docs/site/durable-workflows/sagas.md` and the scaffold source are proven identical by an executed
+docs test, so the "verbatim from the scaffold" claim becomes checkable rather than asserted. A
+freshly scaffolded project with `sagas-api` stopped shows the sample job in a failed state in the
+workers API/CLI listing and an error span in the Aspire dashboard — the consumer-visible difference
+between this fix and today's green-job-no-saga behavior.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Drafted from the Stage-B
+runtime-plugins audit; all code claims re-verified against worktree baseline `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-02-plugin-child-liveness-contract.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-02-plugin-child-liveness-contract.md
new file mode 100644
index 0000000000..d8266d6295
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-02-plugin-child-liveness-contract.md
@@ -0,0 +1,132 @@
+# fix(plugins): every declareHealthChecks returns only the API resource — a crash-looping background child leaves the declared health surface green — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-02 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:fix` `area:plugins` `area:aspire` `area:cli` `priority:p1` `status:triage` ·
+**Depends on:** T4-06 (health URLs must resolve real allocated ports), T4-08 (the gate that consumes
+this contract)
+
+## Summary
+
+Each first-party plugin contributes an API resource **and** a background child, but every
+`declareHealthChecks()` implementation returns exactly one entry — always the API resource. The
+background children (`workers-combined`, `sagas-runner`, `trigger-processor`) contribute no health
+signal at all, and the liveness state that already exists in-process (`Worker.healthStatus`, the
+listener supervisor's `restartCount`/`lastError`, the saga supervisor snapshot) is wired to nothing
+Aspire, `plugin doctor`, MCP, or the dashboard reads. The consequence is the framework's most
+expensive failure mode: a child that crash-loops on every start leaves the entire declared health
+surface green. NetScript needs one declared child-liveness contract before any of that state can be
+surfaced.
+
+## Evidence
+
+- Corpus: `research/repo-audit/runtime-plugins.md` §5.1/§5.2 ("Highest leverage item in the audit:
+ it is the *detector* whose absence lets the other defects ship"), §1.2, §3.2;
+ `research/repo-audit/observability-aspire.md` §1.8, GAP-5; `SYNTHESIS.md` §1.4, §6 (T4 pack,
+ "plugin child liveness" — no existing owner).
+- `plugins/workers/src/aspire/workers-contribution.ts:77-86` — `declareHealthChecks()` returns one
+ entry for `WORKERS_API_RESOURCE`. The background child is registered at `:55-61`
+ (`addDenoBackground(WORKERS_COMBINED_RESOURCE, …)`) and has none.
+- `plugins/sagas/src/aspire/sagas-contribution.ts:145-153` — one entry, `sagas-api`. The runner is
+ registered at `:122-128` with none.
+- `plugins/triggers/src/aspire/triggers-contribution.ts:147-155` — one entry, triggers API. The
+ processor has none.
+- `plugins/streams/src/aspire/streams-contribution.ts:48-55` — one entry at a hardcoded
+ `http://localhost:4437/health`.
+- Liveness state that exists and is unread: `plugins/workers/worker/worker.ts:140`
+ (`get healthStatus()`), `plugins/workers/worker/listener-supervisor.ts:75-83` (`snapshot()` with
+ `status`, `healthy`, `restartCount`, `lastError`).
+- Asymmetric child health today: the **sagas** glue stub serves `/health` mapping the supervisor
+ snapshot to 200/503
+ (`plugins/sagas/src/adapter/resources/glue/runtime.stub.ts:19-30`) and the generated AppHost does
+ probe it (`packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:79-86`,
+ `isSagasBackgroundResource`). The **triggers** and **workers** glue stubs are bare
+ `await startCombinedProcess();` shims with no HTTP surface at all
+ (`plugins/triggers/src/adapter/resources/glue/runtime.stub.ts:16,19`;
+ `plugins/workers/src/adapter/resources/glue/runtime.stub.ts:21,24`), and
+ `generate-register-background.ts:90` special-cases triggers for something other than health.
+- The declaration seam itself is unread in production: `composeAppHost` calls only
+ `contribution.contribute(...)` (`packages/aspire/src/application/compose-apphost.ts:47`), and
+ `composeAppHost` has no production caller — the real wiring is the CLI pipeline at
+ `packages/cli/src/public/features/plugins/install/install-plugin.ts:502-512`.
+- Adjacent: #1325 (triggers crash-loop that this blindness let ship), #1280 (`status:blocked`).
+
+## Current surface
+
+Three sources of truth, none joined. (a) `declareHealthChecks()` — declarative, API-only, and read
+by nothing outside tests. (b) The generated AppHost — probes app/service resources via
+`withHttpHealthCheck` and, for sagas only, the background child. (c) In-process supervisor state —
+accurate, structured, and never leaves the process. `netscript plugin doctor`, the MCP tool surface,
+and the dashboard therefore all report "the API answered", which is orthogonal to whether the child
+that does the work is alive.
+
+## Target contract
+
+1. **A declared child state vocabulary.** Every plugin background child reports one of a closed set
+ (proposed: `starting | ready | degraded | crash-looping | stopped | failed`) with, at minimum:
+ process/child state, **registry readiness** (the generated registry module loaded and every entry
+ registered), **dependency readiness** (KV/DB/upstream API reachable), `restartCount`, and
+ `lastFatalError` (message + timestamp, redacted of secrets).
+2. **One transport, generated.** Every KV-backed first-party background runtime serves the contract
+ over the same route shape the sagas glue already uses, and the AppHost generator probes each
+ child by rule — not by an `isSagasBackgroundResource`-style name special case.
+3. **`declareHealthChecks()` becomes truthful or is removed.** Either the declaration seam is read
+ by the production pipeline (and then declares children too), or it is deleted so plugin authors
+ scaffolded from `new-plugin-use-case.ts:524-529` stop writing declarations nothing consumes. One
+ source of truth, decided explicitly.
+4. **Crash-loop and wrapper-alive/child-dead are explicit, not inferred.** `netscript plugin doctor`,
+ the MCP health/doctor tools, and the dashboard each render a distinct, named state for
+ "API resource healthy, background child dead" and for "child restarting above threshold" — never
+ a single aggregate green.
+
+## Acceptance
+
+- [ ] Every first-party plugin background child reports the declared child state vocabulary.
+- [ ] The AppHost generator probes every background child by rule, with no per-plugin name special
+ case.
+- [ ] Child readiness includes registry load and dependency reachability, not just process
+ liveness.
+- [ ] `restartCount` and the last fatal error are exposed on the child health payload.
+- [ ] `plugin doctor`, the MCP health surface, and the dashboard each render "child dead, API alive"
+ as its own state.
+- [ ] A crash-looping child moves to a `crash-looping` state within a documented threshold rather
+ than flapping between ready and failed.
+- [ ] `declareHealthChecks()` is either consumed by the production pipeline or removed, and the
+ plugin-authoring scaffold matches that decision.
+- [ ] A negative test proves a deliberately crash-looping background child turns the health surface
+ red.
+- [ ] A negative test proves an API resource healthy with its child stopped is reported as degraded,
+ not healthy.
+- [ ] Tests cover startup race, registry load failure, dependency-unavailable, restart storm, and
+ clean shutdown.
+
+## Boundaries
+
+- **#1280 is a different problem and is blocked upstream.** It covers *backing* services (Postgres,
+ Redis/Garnet, Deno KV) whose images serve no health route and for which Aspire's TypeScript
+ AppHost cannot register custom checks. This issue covers **NetScript-authored plugin children**,
+ which run our own Deno entrypoint and can serve whatever route we generate. Do not re-litigate
+ #1280, do not mark it satisfied by this work, and do not propose generated probes against backing
+ images here.
+- **#1325** owns the triggers KV-adapter omission itself (the defect); this issue owns the detector
+ contract. **T4-08** owns wiring the E2E gate to that contract. Neither substitutes for the other.
+- **#828 / #512 / #937** (process-manager PM-B supervised-child helper, PM-1 process-graph state
+ vocabulary, doctor frontend five-state taxonomy) own bare-metal supervision and the frontend
+ doctor taxonomy. Reuse their vocabulary where it fits, but do not re-file their scope or block on
+ the process-manager epic — this contract must ship under Aspire-hosted plugins first.
+- **#734 / #429-#431** (dashboard panel contributions) consume this surface; they do not define it.
+- Backing-service health, plugin discovery of third-party factories (#1093), and the streams
+ durability decision (T4-03) are out of scope.
+
+## Docs/consumer proof
+
+`docs/site/orchestration-runtime/**` gains one page describing the child state vocabulary and what
+each state means operationally, generated from the exported type rather than hand-written. Consumer
+proof: on a scaffolded project, stopping a background child makes `netscript plugin doctor` exit
+non-zero and names the child — reproducible in the installed-consumer smoke (#1343) without reading
+container logs.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. All code claims re-verified
+against worktree baseline `fac9e339042c`; the sagas-child-probes-but-others-do-not asymmetry was
+found during this verification and is newer than the Stage-B audit text.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-03-streams-durable-storage-semantics.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-03-streams-durable-storage-semantics.md
new file mode 100644
index 0000000000..3f15478ec1
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-03-streams-durable-storage-semantics.md
@@ -0,0 +1,117 @@
+# fix(streams): STREAMS_DATA_DIR is set by nothing the framework generates, so "durable streams" is always in-memory — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-03 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:fix` `area:plugins` `area:aspire` `area:docs` `priority:p1` `status:triage` ·
+**Depends on:** none · **Coordinate with:** #1326, #1329 (plan as a trio; do not merge scopes)
+
+## Summary
+
+The streams service selects file-backed storage only when `STREAMS_DATA_DIR` is set; unset means
+in-memory, non-durable, and the service warns once and then reports healthy anyway. `STREAMS_DATA_DIR`
+appears in exactly five files repo-wide, all of them the streams service itself, its test, its README
+and one docs paragraph — no scaffold, no Aspire contribution, no generated AppHost, and no template
+ever sets it. The default every user gets is therefore a plugin named "durable streams" whose data
+does not survive a restart, backed by an upstream `DurableStreamTestServer`. Worse, the documented
+escape hatch is itself unproven: a measured wave-4 run found file-backed mode silently swallowing
+producer writes. The roadmap must decide between durable-by-default and honestly-ephemeral; it
+cannot keep shipping the name without the property.
+
+## Evidence
+
+- Corpus: `research/repo-audit/runtime-plugins.md` §4.2, §8 ledger row 7; `research/wave-4.md` §2.2
+ — "`STREAMS_DATA_DIR` file-backed mode **silently swallows all producer writes**; `flush()` after
+ upsert does the same; `flush()` resolves either way"; `SYNTHESIS.md` §1.4, §6 (T4 pack, "stream
+ durability" — no existing owner).
+- `plugins/streams/services/src/main.ts:36-40` — reads `STREAMS_DATA_DIR`, calls
+ `describeStorageDurability(dataDir)`, `console.warn`s when not durable, then starts.
+- `plugins/streams/services/src/durability.ts:16-29` — unset or empty ⇒
+ `{ durable: false, message: 'Streams service storage is non-durable (in-memory)…' }`.
+- `plugins/streams/services/src/main.ts:50-53` — the backing store is
+ `new DurableStreamTestServer({ port, host: '127.0.0.1', dataDir })`, i.e. a *test* server used as
+ the product substrate.
+- Repo-wide grep for `STREAMS_DATA_DIR` (excluding `.llm/`, `.git/`) returns five files only:
+ `docs/site/durable-workflows/streams.md:287`, `plugins/streams/README.md:20-22`,
+ `plugins/streams/services/src/durability.ts`, `.../durability_test.ts`, `.../main.ts`. **Zero hits
+ in `packages/cli/`, zero in any template or generated AppHost.**
+- `plugins/streams/src/aspire/streams-contribution.ts:41-44` — `declareEnv()` sets only
+ `DURABLE_STREAMS_URL`, so even the (unread) declaration seam never mentions persistence.
+- Health check ignores durability:
+ `plugins/streams/services/src/main.ts:58-70` registers `healthChecks.custom('durable-streams-server', …)`
+ which probes upstream reachability only.
+- Adjacent open issues: #1326 (p0, producer never reconnects), #1329 (p0, SSE envelope), #1280
+ (blocked, backing-service health), #431 (dashboard streams panel).
+
+## Current surface
+
+Durability is opt-in via an env var that no generated artifact sets, so it is effectively
+unreachable for the scaffold user; the only documentation of it is a prose callout telling the user
+to set it themselves. The service reports healthy in both modes and advertises no storage mode on
+its health payload, so neither the dashboard nor `plugin doctor` can tell an operator which one they
+are running. On the durable branch there is no restart proof anywhere in the repo: `durability_test.ts`
+asserts only the two message strings.
+
+## Target contract
+
+The generated AppHost must take exactly one of two positions, recorded in the plugin manifest and
+enforced by a gate:
+
+- **(A) Durable by default.** The AppHost mounts an explicit persistent volume/data directory for
+ the streams resource, sets the corresponding service configuration, and proves that events written
+ before a restart are readable after it. Retention, corruption behavior (unreadable/partial data
+ dir), and backup/restore expectations are documented as part of the contract.
+- **(B) Explicitly ephemeral.** The service, its resource name, its README, the docs page and the
+ package's own description state that stream storage is in-process and non-durable; every "durable"
+ claim is corrected; and any durable mode is gated behind an explicitly-opted, restart-proven
+ configuration rather than an undocumented env var.
+
+In both branches: the storage mode appears on the health/status payload; the service refuses to
+report a durability property it has not proven; and the wave-4 write-swallowing symptom on the
+file-backed path is reproduced and fixed (or the path is removed) before that path is advertised.
+
+## Acceptance
+
+- [ ] The generated AppHost either provisions persistent stream storage or declares the service
+ ephemeral — with the choice recorded in the plugin manifest.
+- [ ] Events written before a service restart are readable after it, proven by an executed test
+ (branch A) or explicitly refused as a claim (branch B).
+- [ ] The health/status payload reports the active storage mode.
+- [ ] The file-backed write path is proven to deliver producer writes, or is removed rather than
+ documented.
+- [ ] Retention, data-dir corruption, and backup/restore expectations are documented for the shipped
+ branch.
+- [ ] Every "durable" claim in `plugins/streams/README.md`, `docs/site/durable-workflows/streams.md`
+ and package metadata matches the shipped branch.
+- [ ] A negative test proves an unwritable or missing data directory fails startup or degrades
+ health, rather than silently falling back to memory.
+- [ ] A negative test proves the service cannot report a durability property it has not proven.
+- [ ] `DurableStreamTestServer` is either replaced as the product substrate or its use is recorded
+ as an accepted, named architecture-debt entry with an exit condition.
+
+## Boundaries
+
+- **#1326 owns producer reconnect, buffer bounds, readiness and shutdown semantics.** This issue must
+ not change `DurableStreamProducer` connect/retry behavior. **#1329 owns the versioned SSE event
+ envelope**; storage-mode fields do not enter the wire envelope without #1329's schema.
+ Plan all three together; land them as separate PRs with separate closing keywords.
+- **#1280** (blocked upstream) owns backing-service health checks; the streams service is
+ NetScript-authored and is not covered by that block.
+- **T4-06** owns the hardcoded `4437` in the streams contribution and the generated consumer stub;
+ do not fix ports here.
+- **#431** (dashboard streams panel) consumes the storage-mode signal; it does not define it.
+- Choosing an alternative durable-stream backend, or introducing a new persistence plugin archetype,
+ is out of scope — if branch A cannot be met with the current substrate, the outcome is branch B
+ plus a follow-up, not an unplanned backend swap.
+
+## Docs/consumer proof
+
+`docs/site/durable-workflows/streams.md` states one storage story that matches the shipped branch,
+with the restart behavior demonstrated rather than described. Consumer proof: on a scaffolded
+project, restart the streams resource and re-read a stream — branch A shows the prior events,
+branch B shows an empty stream *and* a status payload that said so before the restart. Either
+outcome is checkable by an unfamiliar agent without reading plugin source.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Grep counts and file:line
+citations re-verified against worktree baseline `fac9e339042c`; the file-backed write-swallowing
+symptom is a wave-4 measurement carried forward, not re-measured in this planning run.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-04-saga-compensation-telemetry-callsites.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-04-saga-compensation-telemetry-callsites.md
new file mode 100644
index 0000000000..cb71af90c5
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-04-saga-compensation-telemetry-callsites.md
@@ -0,0 +1,124 @@
+# fix(sagas): 5 of 6 saga span factories have zero callers — compensation emits no span and deleting the saga telemetry surface keeps CI green — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-04 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:fix` `area:plugins` `area:telemetry` `priority:p1` `status:triage` ·
+**Depends on:** none (T4-08 covers the child/streams legs of the same gate; the saga-span assertion
+lands here)
+
+## Summary
+
+`plugin-sagas-core` defines six saga span factories. Exactly one — `startHandleSpan` — has a
+production caller. The five cascade factories, including `saga.cascade.compensate`, are defined,
+typed, attributed and never emitted, and `SagaCompensator` takes no instrumentation dependency at
+all, so a compensation executes inside its parent's context invisibly. The saga attribute set also
+omits `netscript.correlation.id`, the cross-seam convention every other plane emits, so saga spans
+cannot join the correlation assertion the Flow-B gate already runs. No E2E gate asserts any `saga.*`
+span, which is the structural reason this survived: the whole saga observability surface could be
+deleted today and every gate would stay green.
+
+## Evidence
+
+- Corpus: `research/repo-audit/observability-aspire.md` GAP-1, GAP-2, GAP-3, GAP-4, §3 table,
+ §5 items 1-3; `SYNTHESIS.md` §1.4, §6 (T4 pack, "saga-span call-sites" — no existing owner).
+- `packages/plugin-sagas-core/src/telemetry/instrumentation.ts` — six factories at `:183`
+ (`SagaSpanNames.HANDLE`), `:193` (`CASCADE_SEND`), `:207` (`CASCADE_SCHEDULE`), `:218`
+ (`CASCADE_SPAWN`), `:229` (`CASCADE_COMPENSATE`), `:240` (`CASCADE_COMPLETE`).
+- Repo-wide `grep -rn "startCascade" --include=*.ts` returns **only the five definition lines in
+ that file** — no caller, no test.
+- The single live caller: `packages/plugin-sagas-core/src/runtime/saga-engine.ts:274`
+ (`startHandleSpan`).
+- `packages/plugin-sagas-core/src/runtime/saga-compensator.ts:35-51` —
+ `SagaCompensatorOptions = Readonly<{ id?: string; clock: SagaClockPort }>`; the constructor stores
+ only `#clock`. No instrumentation, no span.
+- `packages/plugin-sagas-core/src/telemetry/attributes.ts:28,52` — the only correlation key is
+ `SAGA_CORRELATION_KEY: 'netscript.saga.correlation_key'`. The cross-seam convention
+ `CORRELATION_ID: 'netscript.correlation.id'`
+ (`packages/telemetry/src/domain/telemetry-convention.ts:52`) is absent from the saga plane, while
+ streams, workers and triggers all emit it.
+- The tracer is live, not unwired: `plugins/sagas/src/runtime/saga-supervisor.ts:199-206`
+ (`withDefaultTelemetry()` → `createSagaTelemetry()`), flowing through `create-saga-runtime.ts:93,102`.
+ Only the call sites are missing.
+- No gate asserts a saga span:
+ `packages/cli/e2e/src/application/gates/scaffold/validate-flow-b-traces.ts` asserts `trigger.*`,
+ `queue.*`, `job.execute`, `flow-b.callback`, `rpc.client`, `stream.subscribe` only; the saga E2E
+ gates (`packages/cli/e2e/src/domain/cli-surface.ts:131-133`) are health/list/instances probes.
+- Known interaction: nested cascaded compensation throws "deferred to phase 7d"
+ (`saga-compensator.ts:103-107`).
+
+## Current surface
+
+An operator or agent debugging a failed distributed transaction sees `saga.handle` spans and then
+nothing. Which steps unwound, why, and how many are unobservable in traces and must be reconstructed
+from logs — precisely the expensive fallback #1197 measured agents taking. The attributes needed
+(`SagaAttributes.COMPENSATION_REASON`, `COMPENSATION_CASCADE_SIZE`) already exist and are never
+populated. Because no assertion covers the plane, a regression is indistinguishable from the status
+quo.
+
+## Target contract
+
+1. **Every cascade kind emits its span at dispatch.** `send`, `schedule`, `spawn`, `compensate` and
+ `complete` each start their existing factory span at the real dispatch site (the
+ `SagaBusBridge` cascade path), parented to the `saga.handle` span of the message that produced
+ the cascade.
+2. **`SagaCompensator` is instrumented.** It accepts a `SagaInstrumentation` dependency and opens
+ `saga.cascade.compensate` around the handler, populating reason and cascade size; the "no handler
+ registered" outcome is recorded on the span rather than being silently dropped.
+3. **Saga spans carry `netscript.correlation.id`.** The cross-seam correlation attribute is added to
+ the saga attribute set alongside the existing domain-level `SAGA_CORRELATION_KEY` — the two are
+ distinct keys with distinct meanings, documented as such.
+4. **The gate makes the plane non-deletable.** `validate-flow-b-traces.ts` gains a saga leg
+ asserting the `saga.handle` → `saga.cascade.compensate` parent edge and a single correlation id
+ shared with the existing seven boundary spans.
+5. **Deferred behavior stays honest.** Nested cascaded compensation continues to fail loudly; the
+ span records the deferral rather than implying a traced unwind.
+
+## Acceptance
+
+- [ ] Each of the five cascade span factories has a production call site at its dispatch point.
+- [ ] `SagaCompensator` accepts instrumentation and emits `saga.cascade.compensate` around the
+ handler.
+- [ ] `COMPENSATION_REASON` and `COMPENSATION_CASCADE_SIZE` are populated on emitted compensation
+ spans.
+- [ ] `netscript.correlation.id` is emitted on saga spans and documented as distinct from
+ `netscript.saga.correlation_key`.
+- [ ] The Flow-B validator asserts the `saga.handle` → `saga.cascade.compensate` parent edge.
+- [ ] The Flow-B validator asserts one correlation id shared across the saga leg and the existing
+ boundary spans.
+- [ ] A negative test proves removing any cascade span call site turns the gate red.
+- [ ] A negative test proves a compensation with no registered handler is visible in traces rather
+ than silent.
+- [ ] The nested-compensation deferral is recorded on the span and documented, not implied as
+ supported.
+- [ ] `packages/plugin-sagas-core/README.md` documents every emitted span name and its attributes.
+
+## Boundaries
+
+- **T4-08** owns extending the merge-readiness E2E to probe background children and streams health,
+ and owns the `'compensating'`-status defect. The **saga span assertions land here**, in the Flow-B
+ validator; do not duplicate them in T4-08's scope.
+- **#1329** owns the stream SSE/OTEL envelope; correlation on the stream wire is its scope, not this
+ one's.
+- **#418 / #413 / #557** (dashboard Live Flow, `TelemetryQueryPort`, seam-event flow plane) **depend
+ on** this work — a Live Flow view cannot render a compensation leg that emits no spans — but they
+ do not own it and must not be re-filed here.
+- **#1197 / #1090** own agent adoption measurement of the observability surface. Not this issue.
+- Saga compensation *semantics* — no prior-step rollback (`saga-compensator.ts:57`), compensation
+ state never persisted (`saga-bus-bridge.ts:44-52`, `:220-227`), and the asymmetric
+ missing-handler failure (`saga-compensator.ts:57-68` vs `:116-120`) — are separate runtime rows
+ and are **not** fixed by adding telemetry. Do not let span work be mistaken for fixing them.
+- GAP-6 (`VALIDATE_TRACES_SCRIPT` dead stringified validator and its lost OTLP-endpoint assertion)
+ is adjacent tooling debt, not this issue.
+
+## Docs/consumer proof
+
+`docs/site/observability/telemetry.md:31-32,82` currently describes a navigable saga sub-tree that
+does not exist; after this work the caption becomes true and is proven by the gate rather than
+softened. `packages/plugin-sagas-core/README.md` — which today contains no mention of telemetry,
+tracing, spans or observability — documents the six span names. Consumer proof: on a scaffolded
+project, force a saga compensation and follow `request → command → saga → compensation` in the
+Aspire dashboard in one trace, with no log reading.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Zero-caller counts and
+`SagaCompensatorOptions` shape re-verified against worktree baseline `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-05-documented-caveat-defects.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-05-documented-caveat-defects.md
new file mode 100644
index 0000000000..3c204d3e08
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-05-documented-caveat-defects.md
@@ -0,0 +1,112 @@
+# fix(plugins): two defects shipped as documented caveats — WORKER_CONCURRENCY never reaches the runtime, and @netscript/plugin-streams root exports always throw — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-05 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:fix` `area:plugins` `area:docs` `area:aspire` `priority:p2` `status:triage` ·
+**Depends on:** none
+
+## Summary
+
+Two live defects were resolved by writing prose that documents the broken behavior instead of fixing
+it. (1) Aspire metadata and the generated `.env` emit `WORKER_CONCURRENCY`; the workers runtime reads
+`WORKERS_CONCURRENCY` and defaults to `1`, so the declared concurrency of `2` is never applied — and
+five separate documentation passages tell the reader to work around it. (2) The package a user
+installs by name, `@netscript/plugin-streams`, re-exports `defineStreamProducer`/`defineStreamConsumer`
+whose runtime operations always throw, with the docs pointing users at a differently-named package
+for the API that works. Both are cheap to fix and both currently teach that a documented caveat is a
+closed defect.
+
+## Evidence
+
+- Corpus: `research/repo-audit/runtime-plugins.md` §3.3, §4.3, §8 ledger rows 10-11, and the
+ cross-cutting observation naming commit `26b851529` ("docs: reframe architectural debt caveats as
+ design boundaries") as the pattern; `SYNTHESIS.md` §6 (T4 pack, "env-name mismatches" — no
+ existing owner).
+
+**(1) Concurrency name mismatch.**
+
+- Emitters (singular): `plugins/workers/src/aspire/workers-contribution.ts:59`
+ (`concurrencyEnvVar: 'WORKER_CONCURRENCY'`) and `:72` (`WORKER_CONCURRENCY: '2'`);
+ `plugins/workers/scaffold.plugin.json:43`;
+ `packages/cli/src/kernel/adapters/windows/environment/env-file-content.ts:228,235`;
+ `packages/aspire/tests/_fixtures/appsettings.json:161`.
+- Reader (plural): `plugins/workers/bin/runtime.ts:96` and `:140` —
+ `parseInt(Deno.env.get('WORKERS_CONCURRENCY') ?? '1')`.
+- Tests already disagree with the emitters:
+ `packages/cli/src/kernel/templates/aspire/helpers/tests/generators-background-app_test.ts:162`
+ asserts `withEnvironment('WORKERS_CONCURRENCY', …)`.
+- Documented as a caveat in five places: `docs/site/orchestration-runtime/how-to/deploy.md:202`;
+ `docs/site/orchestration-runtime/how-to/author-a-plugin.md:137`;
+ `docs/site/background-processing/workers.md:237`;
+ `docs/site/background-processing/how-to/tune-worker-runtime.md:210-227`;
+ `docs/site/tutorials/erp-sync/04-queue-and-cron.md:112-115`.
+
+**(2) Always-throwing streams root exports.**
+
+- `plugins/streams/src/public/stream-api.ts:19-38` — `StreamUnsupportedOperationError` and
+ `unsupportedStreamOperation(...)`; the manifest-layer producer/consumer handles reject or throw it.
+- Re-exported from the package root at `plugins/streams/src/public/mod.ts:4-5,13` and `:87-88`.
+- Documented rather than fixed at `docs/site/durable-workflows/streams.md:126-133` ("they fail loud,
+ by design"), redirecting users to `@netscript/plugin-streams-core`.
+
+## Current surface
+
+A user who follows the Aspire metadata gets concurrency 1 while the dashboard shows a declared 2;
+the only way to get the declared value is to set an env var by hand that no generated artifact
+writes. A user who installs `@netscript/plugin-streams` and imports its advertised producer/consumer
+helpers gets a compiling program that throws at first use. In both cases the documentation is
+accurate about the breakage, which has made the breakage durable.
+
+## Target contract
+
+1. **One concurrency name.** The emitted metadata name and the runtime-read name are the same
+ identifier. Whichever name is chosen, the other is accepted for one deprecation window with a
+ warning, and a test fails if metadata and runtime ever diverge again.
+2. **The five caveat passages are deleted, not reworded.** Each doc location states the single
+ correct variable with no "but the runtime honors…" clause.
+3. **The streams root surface is truthful.** Either the manifest-layer helpers gain a working
+ implementation over the core runtime, or they are removed from the package root export map and
+ the package documents that runtime primitives live in `@netscript/plugin-streams-core`. A
+ compiling import that always throws at runtime is not an acceptable third option.
+4. **A general rule is recorded.** The remediation program adopts and cites the rule that a
+ documented caveat is not a closed defect (`runtime-plugins.md` §8), so this class stops
+ reappearing.
+
+## Acceptance
+
+- [ ] The Aspire-emitted concurrency env name and the workers runtime read name are identical.
+- [ ] A test fails if the emitted metadata name and the runtime-read name diverge.
+- [ ] The generated `.env` writer emits the canonical name.
+- [ ] The declared concurrency value takes effect in a scaffolded project without manual env edits.
+- [ ] All five documentation passages state one variable with no workaround clause.
+- [ ] `@netscript/plugin-streams` root exports either work at runtime or are removed from the export
+ map.
+- [ ] `docs/site/durable-workflows/streams.md:126-133` matches the shipped export surface.
+- [ ] A negative test proves an always-throwing runtime helper cannot be exported from a plugin
+ package root.
+- [ ] An export-drift or doc-lint gate fails if either caveat wording is reintroduced.
+
+## Boundaries
+
+- **T4-03** owns the streams durability decision and **#1329** owns the SSE envelope; this issue
+ touches only the root export map and the concurrency name.
+- **T4-06** owns the hardcoded ports in the same contribution files; keep the two changes in
+ separate PRs so each has an independent verdict.
+- **#1093** owns third-party plugin discovery; **#829** owns official plugins shipping compile-able
+ `./services` entrypoints. Neither is re-filed here.
+- **#1278** (type soundness, epic-of-record) owns public-surface type unsoundness generally; the
+ always-throwing export is a *runtime* honesty defect, referenced there at most as an example.
+- Renaming the `@netscript/plugin-streams` package, or merging it with `plugin-streams-core`, is out
+ of scope — that is a publish-surface change requiring an RFC.
+
+## Docs/consumer proof
+
+The five caveat passages disappear from the docs site, and a docs test asserts that the
+worked-around variable name no longer appears with workaround phrasing. Consumer proof: a scaffolded
+project started from generated metadata runs the declared worker concurrency, verifiable from the
+worker telemetry attribute `netscript.worker.concurrency`
+(`packages/telemetry/src/attributes/worker.ts:6`) without setting any env var by hand.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. All emitter/reader line
+citations and the five documentation locations re-verified against worktree baseline `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-06-prerandomization-port-hardcodes.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-06-prerandomization-port-hardcodes.md
new file mode 100644
index 0000000000..ec445530fe
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-06-prerandomization-port-hardcodes.md
@@ -0,0 +1,114 @@
+# fix(plugins): sagas/triggers/streams contributions and the generated browser consumer stub still publish pre-randomization ports — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-06 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:fix` `area:plugins` `area:aspire` `area:cli` `priority:p2` `status:triage` ·
+**Depends on:** #979 prerequisite (1) — gates resolve endpoints instead of hardcoding
+`127.0.0.1:`; sequenced with T4-01 (publisher fallback) and T4-02 (health URLs)
+
+## Summary
+
+Since #1211 the scaffolder allocates listener ports from the IANA dynamic range 49152–65535, but
+three of four plugin Aspire contributions still bake `8092`/`8093`/`4437` into the URLs and health
+probes they publish, each after correctly allocating a port through `ctx.port(...)`. The generated
+browser consumer stub does the same: it passes a literal `http://localhost:4437` as `baseUrl`, which
+bypasses the stream URL resolver's throw-on-missing behavior entirely. These are currently masked
+because the declaration seam is unread in production — which means fixing the seam without fixing
+these values would ship broken health checks on day one.
+
+## Evidence
+
+- Corpus: `research/repo-audit/runtime-plugins.md` §1.3 (table of hardcoded values), §1.4 (sibling
+ occurrences), §8 ledger row 9; `SYNTHESIS.md` §6 (T4 pack, "stub port hardcodes ... feeds #979").
+- Allocation range: `packages/cli/src/kernel/domain/scaffold/default-port-allocation.ts:4-7`
+ (49152–65535, landed by #1211 / `0b11ca47a`).
+- Contributions that allocate then publish a literal:
+ - `plugins/sagas/src/aspire/sagas-contribution.ts:112` allocates via
+ `ctx.port(SAGAS_API_SERVICE_NAME, SAGAS_API_DEFAULT_PORT)`; `:135` publishes
+ `SAGAS_API_URL: 'http://localhost:8092'`; `:146` probes `http://localhost:8092/health` with
+ `_ctx` unused.
+ - `plugins/triggers/src/aspire/triggers-contribution.ts:139` publishes
+ `TRIGGERS_API_URL: 'http://localhost:8093'`; `:149` probes `http://localhost:8093/health` with
+ `_ctx` unused.
+ - `plugins/streams/src/aspire/streams-contribution.ts:43` publishes
+ `DURABLE_STREAMS_URL: 'http://localhost:4437'` as a bare string rather than an `EnvSource`;
+ `:51` probes `http://localhost:4437/health` with `_ctx` unused.
+ - Correct counter-example: `plugins/workers/src/aspire/workers-contribution.ts:71`
+ (`{ kind: 'resource', resource: WORKERS_API_RESOURCE, key: 'url' }`) and `:78-84` reading the
+ allocated port back through `ctx.port(...)`.
+- Generated browser code:
+ `plugins/streams/src/adapter/resources/consumer/consumer.stub.ts:42` —
+ `url: buildStreamUrl('%%STREAM_PATH%%', options.baseUrl ?? 'http://localhost:4437')`. Because the
+ literal is passed as `baseUrl`, the resolver at
+ `packages/plugin-streams-core/src/application/stream-url-resolver.ts:99-133` — which is built to
+ throw a diagnostic error rather than guess — never fires.
+- Sibling literals in shipped (non-test) code:
+ `plugins/sagas/src/cli/adapters/runtime-api-client.ts:27`,
+ `plugins/workers/src/cli/adapters/runtime-api-client.ts:27`,
+ `plugins/{sagas,workers,triggers,auth}/streams/factory.ts` (`baseUrl ?? 'http://localhost:4437'`),
+ `plugins/{sagas,workers,streams}/src/e2e/probes/probe-context.ts`,
+ `plugins/workers/test-api.ts:13`, `plugins/triggers/tests/e2e/webhooks_helpers.ts:3`.
+- Owner for the prerequisite work: **#979** (unmilestoned, p2) plus **#980**.
+
+## Current surface
+
+Each contribution allocates through `ctx.port(name, DEFAULT)` inside `contribute()` and then
+publishes a URL that assumes the fallback was taken. Two workspaces that both install plugins would
+also collide on the fixed ports, which is #979's framing. The blast radius is currently limited only
+because `composeAppHost` never runs in production
+(`packages/aspire/src/application/compose-apphost.ts:47`) and the declarations are unread — an
+accidental safety net, not a design.
+
+## Target contract
+
+1. **Nothing publishes a port it did not allocate.** Every env value and health URL a contribution
+ declares is derived from the resource endpoint or from the same `ctx.port(...)` call used at
+ registration — `workers-contribution.ts` is the reference shape.
+2. **Streams publishes an `EnvSource`, not a string.** `DURABLE_STREAMS_URL` becomes a
+ `{ kind: 'resource' }` source like every other resource URL.
+3. **The generated consumer stub resolves, never guesses.** The emitted browser module calls the
+ stream URL resolver and surfaces its diagnostic error; the literal default is removed so a
+ missing endpoint fails loudly in the browser path exactly as it does on the server path.
+4. **Fixed-port literals in shipped CLI clients and probes are resolved or explicitly named as
+ dev-only defaults** with a single documented constant, not scattered magic numbers.
+
+## Acceptance
+
+- [ ] No plugin contribution publishes an env URL or health URL containing a literal port.
+- [ ] `DURABLE_STREAMS_URL` is declared as a resource-derived `EnvSource`.
+- [ ] The generated consumer stub resolves the streams URL and propagates the resolver's diagnostic
+ error instead of defaulting to `localhost:4437`.
+- [ ] Fixed-port literals remaining in shipped CLI clients and probes resolve from one documented
+ constant or from service discovery.
+- [ ] A negative test fails if any plugin contribution reintroduces a literal port in a declared env
+ or health value.
+- [ ] A negative test proves the generated browser consumer errors diagnostically when no stream
+ endpoint is configured.
+- [ ] A scaffolded project on randomized ports resolves every plugin endpoint with no manual
+ configuration.
+
+## Boundaries
+
+- **#979 owns the prerequisites and must land first**: the `scaffold.runtime` gates must resolve each
+ plugin resource endpoint from Aspire instead of live-probing `127.0.0.1:8091–8094` (including the
+ `--allow-net` grant passed to the generated project), and ~20 tutorial/explanation passages under
+ `docs/site/**` must stop `curl`-ing those ports. **Do not re-file that work here**, and do not drop
+ `Port` from scaffolder-written plugin entries in this issue — that is #979's "Then" step. **#980**
+ owns the same defect for `netscript service add`.
+- **T4-01** owns the sagas publisher's `127.0.0.1:8092` fallback (a runtime hot path, p0); this issue
+ owns the declared contribution values and the generated consumer stub.
+- **T4-02** owns the child health contract; it consumes correct URLs from this issue.
+- **T4-05** owns the concurrency env-name mismatch in the same files; separate PRs.
+- The dead `declareEnv`/`declareHealthChecks` seam question (whether `composeAppHost` becomes the
+ production path or is deleted) is decided in **T4-02**, not here.
+
+## Docs/consumer proof
+
+Docs stop teaching fixed plugin ports (coordinated with #979's docs leg) and teach reading the
+endpoint the dashboard reports. Consumer proof: two scaffolded projects run simultaneously with
+plugins installed and neither collides nor requires a manual port edit — the concrete symptom #979
+records.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. All literals and their
+line numbers re-verified against worktree baseline `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-07-verify-background-service-references.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-07-verify-background-service-references.md
new file mode 100644
index 0000000000..b183ee768e
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-07-verify-background-service-references.md
@@ -0,0 +1,111 @@
+# verify(aspire): re-test the wave-6 claim that BackgroundProcessors.*.ServiceReferences is parsed but never injected — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-07 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:test` `area:aspire` `area:cli` `priority:p1` `status:triage` ·
+**Depends on:** none (verify-first; converts to a fix or to a regression test depending on the
+repro)
+
+## Summary
+
+A wave-6 run reported that `BackgroundProcessors..ServiceReferences` is parsed from
+appsettings but never injected into the background resource, so a background child cannot discover
+the services it declares a dependency on. The board sweep found **no owner issue** for this claim.
+Re-reading the generator at baseline `fac9e339042c`, the injection **does exist** — the emitted
+AppHost writes `services____http__0` for every declared reference, and the registration order
+places background processors after services and plugins. This issue therefore opens as a
+verification, not a fix: reproduce or refute the claim on the current published canary before any
+implementation is scheduled.
+
+## Evidence
+
+- Corpus: `SYNTHESIS.md` §3.1 — the `ServiceReferences`-parsed-but-never-injected row is adjudicated
+ a framework-generation defect *class* but flagged "no board owner found by the board sweep → new
+ issue draft, **flagged verify-on-current-canary first**"; `research/preplan-package.md`
+ §Verify-before-filing (the run's standing rule that unverified wave observations are repro'd, not
+ filed as defects); `research/repo-audit/runtime-plugins.md` §1.5 (the discovery-key asymmetry that
+ is the most plausible surviving mechanism).
+- **Counter-evidence at baseline (this is the load-bearing finding):**
+ - `packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:178-197`
+ — for every entry in `entry.ServiceReferences`, the generator emits
+ `const Endpoint = await _services.get('')?.getEndpoint('http');` followed by
+ `await .withEnvironment('services____http__0', Endpoint);`. `:199-217` does the
+ same for `PluginReferences` against `_plugins`.
+ - `packages/cli/src/kernel/assets/generated/aspire/helpers/generate-index-1.ts.template:56,59,62,65`
+ — registration order is `registerServices` → `registerPlugins` → `wireServiceReferences` →
+ `registerBackgroundProcessors`, so the `services`/`plugins` maps are populated before background
+ registration reads them.
+ - `packages/cli/src/kernel/adapters/plugin/appsettings-entry-builders.ts:77-79` — the background
+ entry only carries `ServiceReferences` when `options.serviceReferences` is non-empty.
+ - Parsing exists as claimed:
+ `packages/cli/src/kernel/adapters/config/deploy-config-background.ts:117`
+ (`serviceReferences: raw?.ServiceReferences ?? []`),
+ `packages/cli/src/kernel/adapters/config/deploy-config-types.ts:30,87`.
+- **Most plausible surviving mechanism if the symptom reproduces:** the emitted key uses the raw
+ resource name (`services__sagas-api__http__0`) while the browser-side builder normalizes every
+ non-alphanumeric character to `_` (`packages/aspire/src/application/build-vite-env-var-name.ts:50-66`,
+ `workers-api` → `workers_api`). Consumers read via
+ `packages/sdk/src/discovery/service-url.ts:55-61`, which also uses the raw name. If Aspire itself
+ exports the normalized form for a hyphenated resource, the injected key and the read key differ
+ and the consumer silently falls through to a fixed-port default.
+- Related consumer of that fallthrough: `plugins/sagas/src/runtime/saga-publisher.ts:295-307`
+ (owned by T4-01).
+
+## Current surface
+
+At baseline the generator, the appsettings writer, the parser and the registration order all appear
+correct. The wave-6 observation was made against a published canary on a generated project, not
+against this source, so either (a) the observation predates a fix, (b) the symptom was a
+name-normalization mismatch misattributed to missing injection, or (c) a path exists in which
+`serviceReferences` is never written onto the background entry (for example a plugin install that
+does not pass them). Nothing on the board distinguishes these.
+
+## Target contract
+
+The repository must be able to answer "does a background child receive its declared service
+endpoints?" mechanically rather than by re-reading generator source. Concretely: a scaffolded
+project with a background processor declaring `ServiceReferences` starts with the corresponding
+`services__*` env vars present in the child's environment, the consumer-side resolver finds them
+under the exact key it reads, and a regression test pins both halves — the emitted key and the read
+key — so the two can never drift apart silently again.
+
+## Acceptance
+
+- [ ] Reproduce or refute the claim on the current published canary, attaching the project's
+ `appsettings.json` and the generated `register-background.mts`.
+- [ ] Record the exact env keys present in a running background child's environment for a declared
+ service reference.
+- [ ] Record what Aspire exports for a hyphenated resource name (raw vs underscore-normalized).
+- [ ] If refuted, land a regression test pinning the emitted key and the consumer-read key, and
+ close the row as not-reproducible with the evidence attached.
+- [ ] If reproduced, name the failing mechanism at file:line before any fix is proposed.
+- [ ] A negative test proves a background child whose declared service reference is unresolvable
+ fails or degrades visibly rather than starting with a missing env var.
+- [ ] The result is recorded on the roadmap's verify-first ledger either way.
+
+## Boundaries
+
+- **T4-01** owns the sagas publisher fallback and the server/browser discovery-key normalization
+ decision; if this verification confirms the normalization mismatch, the fix belongs there, and
+ this issue closes with the evidence rather than duplicating it.
+- **#1325** owns the triggers background runtime crash-loop; **T4-02** owns child health. A missing
+ service reference is a distinct symptom from a dead child.
+- **#979 / #980** own host-port pinning. Not this issue.
+- **#1343** owns the installed-consumer smoke; use it as the repro vehicle, do not re-file it.
+- **#511 / #529** (process-manager deploy-target wiring) touch adjacent config keys for bare-metal
+ targets; out of scope.
+- This issue must not be converted into an implementation issue until its first acceptance box is
+ satisfied. A wave-era observation is not a defect until it is reproduced on shipped artifacts.
+
+## Docs/consumer proof
+
+Whichever way the verification lands, `docs/site/orchestration-runtime/**` gains one accurate
+statement of how a background processor discovers a service it references, with the env key spelled
+exactly as emitted. Consumer proof: an unfamiliar agent can confirm the wiring from the generated
+`register-background.mts` plus one dashboard env inspection, without reading `packages/cli` source.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. The counter-evidence above
+was produced by reading the generator at worktree baseline `fac9e339042c` during this drafting pass;
+it contradicts the wave-6 R2 claim as stated and is the reason this draft is verify-first rather
+than a defect.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-08-e2e-child-and-status-truth-gates.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-08-e2e-child-and-status-truth-gates.md
new file mode 100644
index 0000000000..d58342ad23
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T4-08-e2e-child-and-status-truth-gates.md
@@ -0,0 +1,123 @@
+# test(e2e): the merge-readiness gate probes API health only — background children and streams go unverified, and a saga stuck compensating is reported COMPENSATED — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T4-08 · **Proposed milestone:** 0.0.8 (new — "Runtime truth + service slice") ·
+**Labels:** `type:test` `area:tooling` `area:plugins` `area:telemetry` `priority:p1`
+`status:triage` `gate:e2e` · **Depends on:** T4-02 (the child liveness contract this gate asserts),
+T4-06 (endpoint resolution), #979 prerequisite (1)
+
+## Summary
+
+`scaffold.runtime` — the command whose verdict decides merge readiness — probes exactly six API
+endpoints and never looks at a background child or at streams. That blind spot is the direct reason
+#1325 (a trigger processor that crash-loops on every start) could ship with a fully green health
+surface. The same "green wrapper over a false state" pattern exists inside the saga engine: there is
+no `'compensated'` status, `'compensating'` is counted terminal, and telemetry maps `'compensating'`
+to the `COMPENSATED` outcome — so an instance whose compensation merely *started* is
+indistinguishable from one that finished and from one that failed. This issue makes the gate see the
+children and makes the terminal status tell the truth.
+
+## Evidence
+
+- Corpus: `research/repo-audit/runtime-plugins.md` §5.2 ("This is precisely why #1325 shipped"),
+ §6.5, §8 ledger row 1; `research/repo-audit/observability-aspire.md` GAP-3;
+ `SYNTHESIS.md` §2 "Harness/evaluation failure", §6 (T4 pack, "E2E child/span gates" — no existing
+ owner).
+- `.llm/tools/e2e/scaffold-e2e-test.ts:1238-1277` — `#exerciseApis()` probes, in order: workers
+ `/health`, sagas `/health/live`, sagas `/health/ready`, triggers `/health`, auth `/health/live`,
+ auth `/health/ready`, auth session. **No background resource appears.** Called once from `:701`.
+- Streams appears exactly once in that file — `:843`, as a plugin to add
+ (`{ id: 'plugin-add-streams', kind: 'stream', name: 'streams' }`). It is never health-probed.
+- Children that exist and are unprobed: `workers-combined`
+ (`plugins/workers/src/aspire/workers-contribution.ts:55-61`), `trigger-processor`
+ (`plugins/triggers/src/aspire/triggers-contribution.ts`, registered before `:131`), and — the one
+ exception — the sagas runner, which the generated AppHost does probe via a name special case
+ (`packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:79-86`).
+- Status-truth defect:
+ - `packages/plugin-sagas-core/src/domain/constants.ts:18-25` —
+ `SAGA_INSTANCE_STATUSES = ['pending','running','completed','failed','compensating','cancelled']`;
+ there is **no** `'compensated'`.
+ - `packages/plugin-sagas-core/src/runtime/saga-engine.ts:476-478` — any `compensate` cascade writes
+ `'compensating'`.
+ - `:489-491` — `isTerminalStatus()` counts `'compensating'` as terminal.
+ - `:494-497` — `telemetryOutcomeFromStatus()` maps `'compensating'` → `SagaTelemetryOutcomes.COMPENSATED`.
+ - Consumed at `:283` and `:298`.
+- Owner issues in the failure chain: #1325 (the defect this blindness let ship), #1326/#1329
+ (streams), #979 (gate port-resolution prerequisites), #1343 (installed-consumer smoke).
+
+## Current surface
+
+The merge-readiness verdict answers "did four API processes answer HTTP?" and is read as "the
+generated runtime works". Background children, which are where the durable work actually happens,
+contribute nothing to it; streams contributes nothing at all. On the saga side, a persisted status
+of `'compensating'` is simultaneously (a) treated as final, (b) reported to telemetry as a completed
+compensation, and (c) the only state available for an in-flight or failed compensation — three
+different truths behind one string, none of them checked by a gate.
+
+## Target contract
+
+1. **Every first-party background child is probed by the gate.** The `scaffold.runtime` suite
+ installs each KV-backed first-party background runtime and proves each reaches a real ready state
+ as defined by T4-02's child contract — not merely that its process was spawned.
+2. **Streams is health-probed.** The streams resource participates in the gate with the same
+ readiness bar as the other plugins.
+3. **The gate is endpoint-resolving.** Probes resolve each resource endpoint from Aspire rather than
+ from fixed `127.0.0.1:` addresses (#979 prerequisite), so the gate keeps working after
+ T4-06.
+4. **Saga terminal status is honest.** `'compensated'` is added to the status union; `'compensating'`
+ becomes non-terminal; a failed compensation is distinguishable from a completed one; and
+ telemetry reports `COMPENSATED` only for a compensation that finished. Persisted instances in the
+ old encoding are migrated or read-compatible, with the strategy stated.
+5. **The gate fails when the seam is removed.** Each new assertion has a paired negative test, per
+ the run's rule that a green wrapper is not proof.
+
+## Acceptance
+
+- [ ] The `scaffold.runtime` suite probes every first-party background child for real readiness.
+- [ ] The streams resource is health-probed by the suite.
+- [ ] Gate probes resolve endpoints from Aspire rather than from hardcoded `127.0.0.1:`
+ addresses.
+- [ ] `'compensated'` exists in the saga instance status union and `'compensating'` is no longer
+ terminal.
+- [ ] Telemetry reports `COMPENSATED` only for a compensation that completed.
+- [ ] A failed compensation is distinguishable from a completed one in persisted state and in
+ telemetry.
+- [ ] Persisted instances written under the old status encoding are migrated or read-compatibly
+ handled.
+- [ ] A negative test proves a deliberately crash-looping background child turns the suite red.
+- [ ] A negative test proves removing the streams probe turns the suite red.
+- [ ] A negative test proves an instance stuck in compensation is not reported as compensated.
+- [ ] `gate:` `deno task e2e:cli run scaffold.runtime --cleanup` passes with the new assertions on a
+ clean scaffold.
+
+## Boundaries
+
+- **T4-04 owns the saga span assertions** (`saga.handle` → `saga.cascade.compensate` parent edge and
+ the shared correlation id) in `validate-flow-b-traces.ts`. Do not duplicate them here; this issue
+ covers child/streams readiness and the persisted-status truth.
+- **#1325 owns the triggers KV-adapter fix.** This issue builds the detector that would have caught
+ it; do not fix or close #1325 from here, and do not treat a green new gate as evidence #1325 is
+ fixed.
+- **T4-02 defines the child state vocabulary**; this issue asserts it. If T4-02 slips, the gate
+ asserts process-plus-route readiness as an interim bar and says so.
+- **#979 / #980** own removing pinned host ports and the docs `curl` passages; this issue only
+ consumes #979's prerequisite (1).
+- **#1343** owns the installed-consumer smoke against a published canary; **#1163** owns the
+ milestone-run verification; **#542 / #910 / #426** are other epics' E2E gates. None are re-filed.
+- **#1280** (blocked) covers backing-service health; this gate does not attempt to probe Postgres,
+ Redis or Deno KV.
+- Deleting `VALIDATE_TRACES_SCRIPT` and recovering its lost OTLP-endpoint assertion is adjacent
+ tooling debt, not in scope.
+
+## Docs/consumer proof
+
+`AGENTS.md` and the CLI E2E documentation describe the suite's coverage accurately once children and
+streams are included, so "the `scaffold.runtime` verdict" stops overstating what it proves. Saga
+status vocabulary is documented in `docs/site/durable-workflows/sagas.md` with the distinction
+between requested, in-flight, completed and failed compensation. Consumer proof: an operator reading
+the saga instance list can tell a finished compensation from a stuck one without opening traces.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Probe list, streams
+single-occurrence count and the `saga-engine.ts` status/telemetry line numbers re-verified against
+worktree baseline `fac9e339042c`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T7-01-wave7-measured-adoption-smoke.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T7-01-wave7-measured-adoption-smoke.md
new file mode 100644
index 0000000000..e85414d15a
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/T7-01-wave7-measured-adoption-smoke.md
@@ -0,0 +1,66 @@
+# verify(0.0.8): Wave-7 measured adoption smoke — arms A/B/C prove the generated path changes agent behavior — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** T7-01 · **Proposed milestone:** 0.0.8 (exit gate) · **Labels:** `type:test`
+`area:agentic` `area:cli` `priority:p1` `status:triage` · **Depends on:** T2-01, T2-02, T4-01,
+TA-01 (the surfaces under measurement), #1197 (measurement method), #1090 (observational boxes)
+
+## Summary
+
+The remediation program's thesis — generation makes the idiomatic path the easiest path — is
+falsifiable exactly once the 0.0.7 generators and 0.0.8 runtime-truth surfaces exist in a
+published canary. This issue owns the one measured unfamiliar-agent smoke that tests it, as the
+exit gate of 0.0.8. It consumes, and must not duplicate, the existing measurement chain
+(#1102/#1201 capability, #1197 re-measurement, #1090 observational boxes).
+
+## Evidence
+
+- Six consecutive measured runs with zero MCP/doctor/otel adoption (#1197 body; corpus
+ `research/wave-5-6-plans.md` #17).
+- Wave-6 R3's only-GO run is confounded by the supervisor-enforced init gate, model, and canary
+ (`research/wave-6-runs.md` #7) — the strongest untested causal lever in the corpus.
+- Full design: `fable-5-remediation-plan/WAVE7-AND-AGENT-ADOPTION.md` (this run).
+
+## Current surface
+
+Wave-6 harness (brief v3 + PLAN-WAVE6): capability-map rows, executed-command census,
+contamination rules. No arm structure; no generated-slice measurement rows (the verbs do not
+exist yet); wrapper-exit gates only.
+
+## Target contract
+
+Three arms on identical brief/budget: A = post-0.0.8 canary, no init enforcement; B = post-0.0.8,
+R3-style enforced init gate; C = pre-remediation 0.0.6 canary (control). Two runs per arm,
+different frontier models, blind gap-audit scoring on the Wave-6 rubric. New measured rows:
+generated-slice verb usage (used / rejected-with-reason / silent = harness failure), consumer
+no-`any` gate verdict, runtime-truth probes (receipt handling, child liveness, stream restart)
+read from persisted state + one correlated trace.
+
+## Acceptance
+
+- [ ] Arm design (A/B/C, 2 runs each) executed on published canaries with pinned versions recorded
+- [ ] Generated-slice usage measured per run: used or explicitly rejected; zero silent rows
+- [ ] Consumer-side type-escape gate executed against every product (not framework exports)
+- [ ] Runtime-truth rows proved from persisted state + correlated OTEL, not wrapper exits
+- [ ] Arm A vs C delta reported on frontend-composition and runtime-truth capability rows
+- [ ] Negative path: a run that skips a generated verb without recording rejection is scored as a
+ harness failure, not silently excused
+- [ ] Verdict + confirmed residual gaps filed as issues (not folded into harness thickening)
+- [ ] [post-merge] The verdict comment on this issue names the canary versions and links raw
+ measurement artifacts
+
+## Boundaries
+
+#1102/#1201 own MCP retrieval/corpus capability; #1197 owns the re-measurement method and
+extraction script; #1090 owns the wave-five observational boxes (this issue may satisfy them and
+says so per box, but does not absorb them). Harness texts live in `.llm/harness/` + the owner's
+wave folders, not on the board. This issue does not script product choices.
+
+## Docs/consumer proof
+
+The published verdict is the program's go/no-go for advancing the train past remediation
+(`MILESTONE-TRAIN.md` §5). A GO here is the evidence base for any future "0.1.0" claim.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Supervisor-authored
+(Fable 5 · high); design in `WAVE7-AND-AGENT-ADOPTION.md`.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-01-scaffold-protects-api-by-default.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-01-scaffold-protects-api-by-default.md
new file mode 100644
index 0000000000..ca179ee987
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-01-scaffold-protects-api-by-default.md
@@ -0,0 +1,100 @@
+# fix(scaffold): the generated service ships `/api` unprotected and a framework test codifies public API routes as correct — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-01 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:fix` `area:cli` `area:service` `area:auth` `priority:p0` `status:triage` · **Depends on:** TA-02 (a plugin-composable authenticator the generated service can use), T1-05 (SDK client-side credential seam)
+
+## Summary
+
+`netscript service add` emits a `defineService(...)` call with no `auth` key, so every scaffolded
+service serves `/api/rpc/*` and `/api/*` to any unauthenticated caller. The framework does not
+treat this as a defect: `packages/service/tests/auth/define-service-auth_test.ts` contains
+`Deno.test('defineService without auth leaves api routes public')`, which asserts `200` on
+`/api/openapi.json` and therefore locks the insecure default in place as a regression test. A
+product-grade meta-framework must not generate an open API surface by default, and must not carry a
+test that fails when the default is fixed.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §0 (in-repo reproduction of "left RPC unauthenticated"),
+ §3.2, gap **G3**.
+- `packages/cli/src/kernel/assets/service/main.ts.template:14-23` — `defineService(router, { name,
+ version, port, db, openapi, debug })`; no `auth`, no commented stub, no TODO.
+- `packages/service/tests/auth/define-service-auth_test.ts:11-23` — the test that codifies the
+ public default; the adjacent test at `:25` proves `auth` works when supplied.
+- `packages/service/src/presets/define-service.ts:268-273` — `if (options.auth) { builder.withAuthn(...) }`;
+ auth is opt-in and silently absent otherwise.
+- `packages/service/src/auth/auth-middleware.ts:19,22` — `DEFAULT_PROTECTED_PREFIXES = ['/api']`,
+ `DEFAULT_ANONYMOUS_PREFIXES = ['/health']`; the guard already covers exactly the right surface
+ when it is enabled.
+- `grep -rl auth packages/cli/src/kernel/assets/` returns no asset under `assets/service/` or
+ `assets/app/routes/` — no signin route, no `_middleware.ts`, no session helper is generated
+ (`auth.md` §3.2).
+
+## Current surface
+
+A generated workspace produces: a service whose entire `/api` tree is anonymous; an app client
+(`packages/cli/src/kernel/assets/app/lib/example-service.ts.template:16-20`) that cannot send a
+credential even if the service were guarded (`auth.md` §2, gap G1); and a `packages/service` test
+suite that asserts the anonymous behaviour is correct. The server-side guard
+(`createAuthnMiddleware` / `createAuthzMiddleware` / `createScopeAuthorizer`, all fail-closed —
+`auth.md` §1.1) is complete and unused on the generated path.
+
+## Target contract
+
+1. `defineService` treats a missing `auth` option as an **error at build time**, not as "public".
+ Public exposure requires an explicit, greppable opt-out — `auth: 'public'` (or
+ `auth: { public: true, reason: string }`) — recorded in the generated file so an auditor can find
+ every unguarded service with one grep.
+2. `netscript service add` emits the opt-in form by default: an authenticator wired from
+ `@netscript/plugin-auth`'s remote session-verifying `AuthenticatorPort` (TA-02) plus a
+ `createScopeAuthorizer` rule set covering the generated router's routes, with `/health` left
+ anonymous by the existing defaults.
+3. `packages/service/tests/auth/define-service-auth_test.ts:11-23` is **replaced**, not deleted: the
+ new test asserts that an unconfigured `defineService` refuses to start (or starts with `/api`
+ returning 401), and a separate test asserts that the explicit opt-out restores the anonymous
+ surface.
+4. The generated app's client attaches the credential through the T1-05 seam; the scaffolded call
+ path is authenticated end to end without a hand-written `fetch`.
+5. Migration: the change is breaking for existing generated projects. Ship a codemod note plus a
+ startup error message that names the exact opt-out to add.
+
+## Acceptance
+
+- [ ] `defineService` without an `auth` key fails to start with an actionable error naming the opt-out.
+- [ ] An explicit public opt-out is required, is present in generated source, and is greppable.
+- [ ] `netscript service add` emits a guarded service that boots and answers `/health` anonymously.
+- [ ] The generated app calls its own guarded service successfully with no hand-written `fetch`.
+- [ ] Negative test: an unauthenticated `GET /api/openapi.json` on a generated service returns 401.
+- [ ] Negative test: an unauthenticated `POST /api/rpc/*` procedure call on a generated service returns 401.
+- [ ] Negative test: a request with a valid credential but an insufficient scope returns 403.
+- [ ] `defineService without auth leaves api routes public` is replaced by a test asserting the new default.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` green with the guarded default.
+- [ ] The breaking change is recorded in the release notes with the exact opt-out snippet.
+
+## Boundaries
+
+- **Do not** implement the SDK credential seam here — **T1-05** owns `CreateServiceClientOptions` /
+ `ServiceClientContext` headers and per-call credential override (gap G1). This issue consumes it.
+- **Do not** implement `PluginServiceConfig.auth` or the remote authenticator here — **TA-02** owns
+ both.
+- **Do not** design organization-aware policy contracts — **#884** owns them.
+- **Do not** build the auth conformance/mocking test kit — **#885** owns it; this issue adds only
+ the gates listed above.
+- **Do not** add auth UI, signin pages, or a session widget — **#942** owns the auth v1 frontend.
+- **Do not** change the plugin frontend procedure gateway — **#934** owns it.
+- **Do not** re-file scaffold-conformance inventory work — **#1335** is the umbrella; **#1333** owns
+ frontend scaffold modernization.
+
+## Docs/consumer proof
+
+`docs/site/tutorials/workspace/05-route-authz.md:248-258` currently demonstrates an authenticated
+call only with `curl -H 'authorization: Bearer read'` because no typed-client example can be
+written (`auth.md` §2). Adoption is proven when that tutorial shows the generated client making an
+authenticated call, and when `docs/site/reference/sdk/index.md` — which today contains zero
+credential guidance — documents the opt-out and the guarded default. A generated project checked out
+fresh must show the `auth` option in `services//src/main.ts` without the user editing anything.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gaps G3/G1; all cited line numbers re-verified against worktree
+`fac9e339042c` on 2026-08-08.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-02-plugin-services-unguardable.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-02-plugin-services-unguardable.md
new file mode 100644
index 0000000000..3c8fdbedaa
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-02-plugin-services-unguardable.md
@@ -0,0 +1,110 @@
+# fix(plugin): `createPluginService` has no auth seam, so every first-party plugin API is unguardable by construction — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-02 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:fix` `area:plugins` `area:auth` `area:service` `priority:p0` `status:triage` · **Depends on:** none (TA-04 improves the handler-side ergonomics but does not block)
+
+## Summary
+
+`PluginServiceConfig` exposes 16 knobs — cors, logger, openapi, docs, database, middleware, context,
+rpc, health, raw routes, startup/shutdown hooks — and **no auth field**. `createPluginService` never
+calls `withAuthn` or `withAuthz`. Consequently the five first-party plugin services (auth, workers,
+sagas, streams, triggers) and every plugin produced by `netscript plugin new` publish
+`/api/rpc/v1//*` and `/api/v1//*` with no possible guard short of forking the service
+entrypoint. The auth plugin is the sharpest case: the service that owns identity is itself
+unauthenticated (see TA-03a).
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §3.1, gaps **G2** and **G8**; SYNTHESIS §2
+ ("Plugin-composition failure").
+- `packages/plugin/src/service/presentation/create-plugin-service.ts:63-104` — the full
+ `PluginServiceConfig` interface; no `auth`, no `authn`, no `authz`, no `principal`.
+- `packages/plugin/src/service/presentation/create-plugin-service.ts:137-194` — the fixed chain:
+ `createService → withCors → withLogger → withOpenAPI → withDocs → withDatabase → use(middleware)
+ → withContext → withRPC → withHealth → withServiceInfo → route → onStartup → onShutdown`. No auth
+ stage exists.
+- `grep -rn 'withAuthn\|withAuthz' packages plugins` outside `packages/service/` returns **zero**
+ matches — no plugin, and no scaffolded service, calls the guard.
+- Callers proven unguarded today: `plugins/auth/services/src/main.ts:70-84`,
+ `plugins/workers/services/src/main.ts`, `plugins/sagas/services/src/main.ts`,
+ `plugins/streams/services/src/main.ts`, `plugins/triggers/services/src/main.ts`, plus
+ `packages/cli/src/public/features/plugins/new/new-plugin-use-case.ts` (the generator for
+ third-party plugins).
+- The guard that is missing already exists and is correct: `packages/service/src/auth/auth-middleware.ts:19,22`
+ (`DEFAULT_PROTECTED_PREFIXES = ['/api']`, `DEFAULT_ANONYMOUS_PREFIXES = ['/health']`),
+ fail-closed authz (`packages/service/src/auth/scope-authorizer.ts:41`).
+- No remote session-verifying authenticator exists: the only `AuthenticatorPort` implementations are
+ `static-credential`, `trusted-header`, `kv-oauth`, `workos`, `better-auth` (`auth.md` §4.5), so a
+ service can only validate the auth plugin's sessions by embedding the backend, its KV/DB handles
+ and its provider secrets in-process.
+
+## Current surface
+
+A plugin author has exactly two options: publish an open API, or abandon `createPluginService` and
+hand-assemble a `ServiceBuilder` — which discards the "mandated builder order" the factory exists to
+enforce (`create-plugin-service.ts:109-112`). `createTrustedHeaderAuthenticator` reads
+`x-authenticated-user`/`-scopes`/`-roles` (`packages/service/src/auth/trusted-header-authenticator.ts:20-54`)
+but nothing in the repo emits those headers — the receiver exists, the sender does not
+(`auth.md` §4.4, gap G14).
+
+## Target contract
+
+1. `PluginServiceConfig` gains `auth?: PluginServiceAuthConfig` carrying `{ authn, authz? }` with the
+ same shape `defineService` already accepts (`packages/service/src/presets/define-service.ts:268-273`),
+ applied by `createPluginService` **before** `withRPC` so route registration stays inside the guard —
+ the ordering `packages/service/src/builder/service-builder-impl.ts:435-436` already guarantees
+ (`installAuth()` at `:442` runs before `installDeferredRoutes()` at `:462`).
+2. Deny-by-default posture: omitting `auth` is an explicit decision, not a default. The factory
+ requires either `auth` or a recorded `auth: 'public'` opt-out, mirroring TA-01 so the framework
+ has one rule for both generated and plugin services.
+3. `@netscript/plugin-auth` exports `createAuthServiceAuthenticator({ serviceName, ... })` — an
+ `AuthenticatorPort` that verifies a request by calling the auth service's `GET /session` over the
+ typed client (T1-05 seam), so a guarded service needs no backend, no KV handle and no provider
+ secret. This is gap G8 and it is what makes the plugin composable at all.
+4. The five first-party plugin services adopt the seam; each declares its own scope rules, and
+ `/health` stays anonymous.
+5. `netscript plugin new` generates the guarded form.
+
+## Acceptance
+
+- [ ] `PluginServiceConfig.auth` exists and `createPluginService` applies it before `withRPC`.
+- [ ] Omitting `auth` requires an explicit recorded public opt-out.
+- [ ] `@netscript/plugin-auth` exports a remote session-verifying `AuthenticatorPort`.
+- [ ] The remote authenticator needs no backend instance, KV handle, or provider secret.
+- [ ] All five first-party plugin services declare an auth configuration or a recorded opt-out.
+- [ ] `netscript plugin new` scaffolds a guarded plugin service.
+- [ ] Negative test: an unauthenticated `POST /api/rpc/v1//*` on a guarded plugin service returns 401.
+- [ ] Negative test: an unauthenticated `GET /api/v1//*` on a guarded plugin service returns 401.
+- [ ] Negative test: a valid principal with an insufficient scope returns 403 on the same routes.
+- [ ] Negative test: the guard cannot be bypassed by reordering config fields (order is factory-owned).
+- [ ] A guard test fails if a future `createPluginService` change registers RPC routes before authn.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` green with guarded plugin services.
+
+## Boundaries
+
+- **Do not** build the deny-by-default *frontend procedure gateway* — **#934** owns the generated
+ per-procedure route table at `/api/plugins//`, its CSRF/origin checks and its threat
+ model. This issue guards the plugin's own service surface; #934 guards the browser-facing
+ projection of it. Align the deny-by-default vocabulary with #934, do not implement it here.
+- **Do not** define organization/tenant-aware authorization contracts — **#884** owns them; the
+ authorizer added here stays scope/role-based and must remain adaptable to #884's model rather than
+ competing with it.
+- **Do not** build the auth conformance/mocking test kit — **#885** owns it.
+- **Do not** fix the auth service's own signout authentication defect — **TA-03a** owns it (this
+ issue supplies the seam it consumes).
+- **Do not** change plugin discovery's hardcoded factory table — **#1093** owns it.
+- **Do not** touch the CLI's hardcoded `localhost:4437` auth session URL — **#1243** owns it.
+
+## Docs/consumer proof
+
+`docs/site/explanation/plugin-system.md` and `docs/site/identity-access/auth.md` must show a plugin
+service declaring `auth` and a non-auth plugin verifying sessions through
+`createAuthServiceAuthenticator` without embedding a backend. Adoption is proven when a third-party
+plugin fixture (not a first-party one) is guarded end to end in a test, and when
+`docs/site/identity-access/how-to/add-authentication.md` can state, with a code reference, how to
+protect a plugin API — a sentence it cannot write today.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gaps G2/G8/G14; all cited line numbers re-verified against worktree
+`fac9e339042c` on 2026-08-08.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03a-signout-accepts-arbitrary-session-id.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03a-signout-accepts-arbitrary-session-id.md
new file mode 100644
index 0000000000..1e5fd96024
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03a-signout-accepts-arbitrary-session-id.md
@@ -0,0 +1,91 @@
+# fix(auth): `POST /api/v1/auth/signout` revokes any session id an unauthenticated caller supplies — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-03a · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:fix` `area:auth` `area:plugins` `priority:p0` `status:triage` · **Depends on:** TA-02 (the auth service must be guardable before signout can require a principal)
+
+## Summary
+
+The auth plugin's `signout` handler takes `sessionId` from the **request body**, resolves the
+backend, and calls `revokeSession(sessionId)` with no authentication and no ownership check. Any
+caller who learns or guesses a session id can terminate that session; the auth service accepts the
+request because `createPluginService` cannot guard it at all (TA-02). NetScript's own CLI exercises
+this path with a raw `fetch` carrying no credential, which is how the hole stayed invisible.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §3.1, gap **G6**.
+- `plugins/auth/services/src/routers/v1-handlers.ts:189-234` — `export async function signout(input:
+ SignoutInput, context: AuthServiceContext)`; `const sessionId = input.sessionId ?? await
+ backend.interactive?.getSessionId(...)`, then `await backend.sessions.revokeSession(sessionId)`.
+ There is no principal check anywhere in the function.
+- `packages/plugin-auth-core/src/contracts/v1/auth.contract.ts:56-61` — `SignoutInput = Readonly<{
+ sessionId?: string; everywhere?: boolean; redirectTo?: string }>`; the contract itself invites a
+ caller-supplied session id.
+- `plugins/auth/services/src/main.ts:70-84` — the service is built by `createPluginService` with no
+ auth configuration, so no middleware ever populates a principal (TA-02 evidence).
+- `packages/cli/src/public/features/plugins/auth/auth-session-client.ts:7-23` — first-party caller
+ using raw `fetch` with no credential (`auth.md` §0).
+- The `everywhere` flag in the same input widens the blast radius: a global logout is reachable on
+ the same unauthenticated route.
+
+## Current surface
+
+`signout` has two modes. With `input.sessionId` present it revokes that id directly. With it absent
+it falls back to `backend.interactive?.getSessionId(request)`, i.e. the cookie — the only
+identity-bearing path, and it is the *optional* one. The handler emits a `session.revoked` audit
+event and telemetry for the revocation it just performed, so the audit trail records a successful
+revocation with no authenticated actor.
+
+## Target contract
+
+1. `signout` requires an authenticated principal. With TA-02's seam applied to
+ `plugins/auth/services/src/main.ts`, `/api/v1/auth/signout` and `/api/rpc/v1/auth/signout` are
+ inside `DEFAULT_PROTECTED_PREFIXES`.
+2. Session ownership is enforced in the handler, not only at the middleware: the resolved session's
+ subject must equal the principal's subject, otherwise the call fails with the contract's
+ `UNAUTHORIZED` error and **no** revocation and **no** success audit record.
+3. `everywhere: true` revokes only sessions belonging to the authenticated subject.
+4. Caller-supplied `sessionId` is either removed from `SignoutInput` (preferred — the session comes
+ from the credential) or retained solely as a same-subject selector for multi-session sign-out.
+ Whichever is chosen, a foreign id can never be revoked. Removing the field is a contract-breaking
+ change and must be released as such.
+5. The failure path is indistinguishable to the caller between "session does not exist" and "session
+ belongs to someone else" (no enumeration oracle).
+
+## Acceptance
+
+- [ ] `signout` rejects requests that carry no authenticated principal.
+- [ ] `signout` rejects a `sessionId` whose subject differs from the principal's subject.
+- [ ] `everywhere: true` revokes only sessions owned by the authenticated subject.
+- [ ] A rejected signout emits no `session.revoked` audit event and no success telemetry.
+- [ ] Negative test: unauthenticated `POST /api/v1/auth/signout` with a valid foreign `sessionId` returns 401 and the session stays active.
+- [ ] Negative test: authenticated `POST /api/v1/auth/signout` with a foreign `sessionId` returns 401/403 and the session stays active.
+- [ ] Negative test: `POST /api/rpc/v1/auth/signout` is guarded identically to the REST route.
+- [ ] Negative test: unknown and foreign session ids produce the same response shape and status.
+- [ ] The CLI's session commands send a credential and no longer rely on the unauthenticated path.
+- [ ] Any `SignoutInput` contract change is recorded as breaking with a migration note.
+
+## Boundaries
+
+- **Do not** add the `PluginServiceConfig.auth` seam here — **TA-02** owns it; this issue consumes it
+ and is blocked until it lands.
+- **Do not** fix the `Set-Cookie` discard on signin/callback — **TA-03b** owns it.
+- **Do not** change the CORS default — **TA-03c** owns it.
+- **Do not** rework the CLI's hardcoded `localhost:4437` session-stream URL — **#1243** owns it;
+ this issue only changes whether the CLI sends a credential.
+- **Do not** build the auth conformance/mocking test kit — **#885** owns it.
+- **Do not** add org-scoped revocation semantics — **#884** owns organization-aware policy.
+
+## Docs/consumer proof
+
+`docs/site/identity-access/how-to/add-authentication.md:243` advertises `POST /api/v1/auth/signout`
+as "Revoke the current session and clear the session cookie" — "the current session" is exactly the
+guarantee the code does not provide. Adoption is proven when that row is accurate, when the docs show
+the authenticated call shape, and when `docs/site/identity-access/auth.md` documents the ownership
+rule and the deliberate non-enumeration of foreign session ids.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gap G6; all cited line numbers re-verified against worktree
+`fac9e339042c` on 2026-08-08. Split from the TA-03 cluster so each session-lifecycle defect carries
+its own acceptance and negative tests.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03b-orpc-signin-callback-discard-set-cookie.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03b-orpc-signin-callback-discard-set-cookie.md
new file mode 100644
index 0000000000..6f224a458c
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03b-orpc-signin-callback-discard-set-cookie.md
@@ -0,0 +1,103 @@
+# fix(auth): oRPC `signin`/`callback` discard the backend `Set-Cookie`, so the interactive browser flow can never establish a session — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-03b · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:fix` `area:auth` `area:contracts` `area:plugins` `priority:p0` `status:triage` · **Depends on:** none (TA-03c is required for the browser end of the same flow to work)
+
+## Summary
+
+`kv-oauth` returns real 302 responses whose `Set-Cookie` headers carry first the OAuth transaction id
+and then the session id. Both auth handlers throw those responses away and keep only the `Location`
+value. The contract makes this unfixable at the handler level: none of the five auth routes declares
+`outputStructure: 'detailed'`, so no procedure in `authContractV1` can emit a response header at all.
+The result is that the only interactive backend NetScript ships cannot complete a browser sign-in
+through its own published API — the transaction cookie never reaches the browser, so the callback
+fails with `oauth_cookie_missing`, and the session cookie never reaches the browser, so `GET /session`
+and `GET /me` cannot see it afterwards.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §4.1, gap **G5**.
+- `plugins/auth/services/src/routers/v1-handlers.ts:95-107` — `signin` calls `interactive.signIn(...)`
+ and keeps only `responseLocation(response)`; the `Response` object is discarded.
+- `plugins/auth/services/src/routers/v1-handlers.ts:162-169` — `callback` builds
+ `{ completed, sessionId, redirectTo, subject }` from `result` and discards `result.response`.
+- `plugins/auth/services/src/routers/v1-helpers.ts:82-84` — `responseLocation(response)` returns
+ `response.headers.get('location')`; there is no `Set-Cookie` reader anywhere in the helpers.
+- `packages/plugin-auth-core/src/contracts/v1/auth.contract.ts:437-461` — all five routes are
+ `baseContract.route({ method, path }).input(...).output(...)`; **no** `outputStructure: 'detailed'`
+ on any of them.
+- `packages/auth-kv-oauth/src/flow.ts:143` (transaction cookie on the signin redirect) and `:220-224`
+ (session cookie on the callback redirect) — the backend does emit both.
+- `packages/auth-kv-oauth/src/flow.ts:155-159` — `handleCallback` throws `oauth_cookie_missing`
+ unless the txn cookie or an explicit `txn` parameter is present; `v1-handlers.ts:157-160` forwards
+ only `providerId`, `code` and `state`, never `txn`.
+- The inbound half already works: `v1-helpers.ts:40-60` (`toRequest`) forwards inbound headers into
+ the backend `Request`, and `toAuthnRequest` (`:64-78`) reads the session cookie. The defect is
+ strictly the response half.
+- The server-side write-back path exists and is unused by this surface:
+ `AuthnResult.setCookies` / `responseHeaders` applied by `applyAuthnResponse`
+ (`packages/service/src/auth/auth-middleware.ts:166-177`).
+
+## Current surface
+
+`signin` returns `{ started, providerId, redirectUrl, state }`; the caller is expected to follow
+`redirectUrl` in a browser that never received the transaction cookie. `callback` returns
+`{ completed, sessionId, redirectTo, subject }` — the session id is handed to the caller in the JSON
+body, which is the only way it can be observed, and which is itself a credential leak into logs and
+client state. Docs assert the opposite behaviour
+(`docs/site/identity-access/how-to/add-authentication.md:243,273`).
+
+## Target contract
+
+1. `signin`, `callback` and `signout` declare `outputStructure: 'detailed'` in
+ `auth.contract.ts` so their handlers can return headers alongside the body.
+2. The handlers propagate the backend response's `Set-Cookie` headers verbatim — including the
+ `__Host-` prefix, `Path=/`, `Secure`, `HttpOnly` and `SameSite` attributes the backend already
+ asserts (`packages/auth-kv-oauth/src/cookies.ts:105-121`) — without re-encoding or re-signing them.
+3. `callback` forwards the `txn` parameter when present so the documented explicit-transaction path
+ works for non-cookie callers.
+4. The session id stops being returned in the `callback` response body once the cookie path works,
+ or is retained only behind an explicit non-browser flag; either way the change is recorded as
+ contract-breaking.
+5. `signout` returns the session-clearing `Set-Cookie` the docs already promise.
+
+## Acceptance
+
+- [ ] `signin`, `callback` and `signout` declare `outputStructure: 'detailed'`.
+- [ ] The transaction `Set-Cookie` from `signIn` reaches the HTTP response unmodified.
+- [ ] The session `Set-Cookie` from `handleCallback` reaches the HTTP response unmodified.
+- [ ] `signout` emits a session-clearing `Set-Cookie`.
+- [ ] `callback` forwards an explicit `txn` parameter to the backend when supplied.
+- [ ] Cookie attributes (`__Host-`, `Path=/`, `Secure`, `HttpOnly`, `SameSite`) survive the round trip.
+- [ ] Negative test: a `signin` → provider → `callback` round trip without the txn cookie still fails, and fails with `oauth_cookie_missing` rather than silently succeeding.
+- [ ] Negative test: `GET /session` after a completed callback resolves the session **from the cookie only**, with no session id in the request body or query.
+- [ ] Negative test: a cookie rewritten to drop `Secure` or `HttpOnly` is rejected by the backend's cookie-policy assertion.
+- [ ] Tests cover both the REST (`/api/v1/auth/*`) and RPC (`/api/rpc/v1/auth/*`) projections of each route.
+- [ ] Any contract change is released as breaking with a migration note.
+- [ ] `docs/site/identity-access/how-to/add-authentication.md:243,273` are true against the shipped code.
+
+## Boundaries
+
+- **Do not** fix signout authentication/ownership — **TA-03a** owns it.
+- **Do not** change the CORS default or decide the browser topology — **TA-03c** owns both; this
+ issue makes the server emit correct cookies, TA-03c makes a browser able to keep them.
+- **Do not** add the SDK client-side credential seam — **T1-05** owns it.
+- **Do not** build signin/callback UI — **#942** owns the auth v1 frontend.
+- **Do not** re-open multi-backend routing — the single-active-backend boundary is recorded as
+ accepted debt (`.llm/harness/debt/arch-debt.md:1313`, `auth-single-active-backend-boundary`) and
+ **#874** owns the routing gap.
+- **Do not** widen the contract for organization/tenant selectors — **#884** owns them.
+
+## Docs/consumer proof
+
+`docs/site/identity-access/how-to/add-authentication.md:243` ("Revoke the current session and clear
+the session cookie") and `:273` ("After completing the browser sign-in, the session cookie is set")
+are currently unverifiable claims. Adoption is proven when the documented `curl -c cookies.txt` /
+`curl -b cookies.txt` sequence in that file executes end to end against a generated project, and when
+the tutorial `docs/site/tutorials/workspace/02-auth.md` no longer needs to pass a session id by hand.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gap G5; all cited line numbers re-verified against worktree
+`fac9e339042c` on 2026-08-08. Split from the TA-03 cluster so each session-lifecycle defect carries
+its own acceptance and negative tests.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03c-cors-wildcard-blocks-credentialed-calls.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03c-cors-wildcard-blocks-credentialed-calls.md
new file mode 100644
index 0000000000..6e3e1b3fa0
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-03c-cors-wildcard-blocks-credentialed-calls.md
@@ -0,0 +1,93 @@
+# fix(service): the default CORS policy is `origin: '*'`, which makes credentialed browser calls impossible on every generated and plugin service — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-03c · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:fix` `area:service` `area:plugins` `area:auth` `priority:p1` `status:triage` · **Depends on:** none (blocks the browser half of TA-03b; pairs with T1-05)
+
+## Summary
+
+`ServiceBuilder.withCors` defaults to `{ origin: '*' }`, and `createPluginService` passes an
+undefined `config.cors` straight into it, so every first-party plugin service and every generated
+service answers with `Access-Control-Allow-Origin: *`. That header is spec-incompatible with
+`credentials: 'include'`: a browser will refuse to send or store credentials against it. Combined
+with cross-origin service discovery and the `__Host-` cookie prefix, browser cookie auth is
+impossible today **even if** the SDK gained a credentials option — which is why this must be decided
+before, not after, the client seam lands.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §3.4 and §4.2, gap **G7**.
+- `packages/service/src/builder/service-builder-impl.ts:95-98` — `withCors(options?: CorsOptions)`
+ calls `this.app.use('*', cors(options ?? { origin: '*' }))`.
+- `packages/plugin/src/service/presentation/create-plugin-service.ts:139-141` — `if
+ (config.enableCors !== false) { builder = builder.withCors(config.cors) }`; `config.cors` is
+ `undefined` unless a plugin supplies it.
+- `plugins/auth/services/src/main.ts:70-84` — the auth service supplies no `cors`, so the identity
+ service itself answers `*`.
+- `packages/sdk/src/discovery/service-url.ts:97-128` — `getServiceUrl` resolves the **browser** URL
+ from Aspire-injected `import.meta.env`, so the app on its own port calls `http://localhost:8094`
+ cross-origin.
+- `packages/auth-kv-oauth/src/backend.ts:125` — the session cookie name defaults to
+ `__Host-ns_session`; `packages/auth-kv-oauth/src/cookies.ts:105-121` asserts `__Host-` requires
+ `Path=/`, no `Domain`, and `Secure`. A `__Host-` cookie set on `:8094` is never sent to `:8000`.
+- There is no same-origin proxy, BFF route, or gateway anywhere in the scaffold (`auth.md` §4.2).
+
+## Current surface
+
+Three independent facts compose into "no browser session is possible": a wildcard CORS origin, a
+cross-origin service URL handed to the browser, and an origin-locked cookie prefix. Each is
+defensible alone; together they mean the only shipped interactive backend cannot authenticate a
+browser through the generated app, and no SDK option can rescue it.
+
+## Target contract
+
+1. **Decide and record the topology.** Two supported shapes, one default:
+ - *same-origin BFF* — the Fresh app proxies `/api/*` to the service, cookies stay first-party,
+ `__Host-` remains valid; or
+ - *bearer* — the browser holds no cookie, the app forwards a token through the T1-05 client seam
+ and the service uses a bearer authenticator.
+ The decision is written into `docs/architecture/doctrine/` (or an `arch-debt.md` entry if
+ deferred) and the scaffold generates the chosen shape.
+2. `withCors` no longer defaults to `origin: '*'`. The default is an explicit allowlist derived from
+ the generated app's origin(s); a wildcard requires an explicit opt-in and is rejected when
+ `credentials` is enabled.
+3. `createPluginService` passes the workspace allowlist rather than `undefined`.
+4. `getServiceUrl` gains (or documents) a same-origin mode so the browser path and the
+ server-to-server path can differ without the consumer hand-rolling URLs.
+
+## Acceptance
+
+- [ ] The browser auth topology is decided and recorded in a doctrine or `arch-debt.md` entry.
+- [ ] `withCors` no longer defaults to `origin: '*'`.
+- [ ] A generated workspace produces an explicit CORS allowlist covering its own app origin.
+- [ ] Plugin services receive the workspace allowlist instead of `undefined`.
+- [ ] The scaffold generates the chosen topology (same-origin route or bearer forwarding).
+- [ ] Negative test: a wildcard origin combined with credentialed CORS is rejected at build time.
+- [ ] Negative test: a request from an origin outside the allowlist is refused.
+- [ ] Negative test: a `__Host-` session cookie issued by the chosen topology is accepted on a subsequent request from the generated app.
+- [ ] Negative test: the pre-fix arrangement (wildcard + cross-origin + `__Host-`) is proven to drop the cookie, so the regression cannot return silently.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup` green with the new default.
+- [ ] The CORS default change is recorded as breaking with a migration note.
+
+## Boundaries
+
+- **Do not** add the SDK `credentials`/`headers` option — **T1-05** owns the client seam. This issue
+ exists so that option can work when it lands.
+- **Do not** make the auth handlers emit cookies — **TA-03b** owns the response half.
+- **Do not** build the plugin frontend procedure gateway or its CSRF/origin checks — **#934** owns
+ them; the allowlist here is the service-level policy, not the gateway.
+- **Do not** change Aspire host-port pinning — **#979** (+ **#980**) own it; this issue must not
+ hardcode ports and should read origins from the workspace configuration.
+- **Do not** add auth UI — **#942** owns it.
+
+## Docs/consumer proof
+
+`docs/site/identity-access/how-to/add-authentication.md` teaches a `curl`-based cookie round trip
+because no browser round trip works. Adoption is proven when a generated app — not `curl` — completes
+a sign-in and a subsequent authenticated read, and when the docs state the supported topology
+explicitly instead of leaving the reader to discover the wildcard/`__Host-` conflict at runtime.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gap G7; all cited line numbers re-verified against worktree
+`fac9e339042c` on 2026-08-08. Split from the TA-03 cluster so each session-lifecycle defect carries
+its own acceptance and negative tests.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-04-typed-principal-and-policy-metadata.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-04-typed-principal-and-policy-metadata.md
new file mode 100644
index 0000000000..d9742e4720
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-04-typed-principal-and-policy-metadata.md
@@ -0,0 +1,109 @@
+# feat(service): handlers receive `principal` as an untyped bag entry and no procedure can declare a policy — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-04 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:feat` `area:service` `area:plugins` `area:contracts` `area:auth` `priority:p1` `status:triage` · **Depends on:** none (consumed by T1-05, TA-02, #934; prerequisite for #884)
+
+## Summary
+
+`buildRpcContext` writes the resolved `Principal` into a `Record`, so every handler
+that wants an identity must hand-declare an optional field and trust it. `@netscript/plugin` has no
+principal concept at all — zero occurrences repo-wide — so plugin handlers cannot type one even
+badly. Separately, authorization is path-prefix-only: `AuthzRequest` carries `{ principal, method,
+path }`, rules match with `startsWith`, and oRPC's `.meta()` is used **nowhere** in the codebase, so
+a procedure cannot declare the policy it requires. Policy therefore lives in a second place that can
+drift from the contract, OpenAPI emits no `security` metadata, and no generated surface (SDK, MCP,
+agent tooling) can tell a protected procedure from a public one.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §4.3 and §5, gaps **G9** and **G10**; `repo-audit/services-sdk.md`
+ gap **S14**.
+- `packages/service/src/types.ts:270-272` — `export type ContextFactory = (context: Context) =>
+ Record`.
+- `packages/service/src/builder/service-builder-impl.ts:276-279` — `const principal =
+ c.get('principal'); if (principal) { ctx.principal = principal; }` — merged into the untyped bag.
+- `packages/service/src/auth/types.ts:29-46` — `Principal { subject, scopes, roles, scheme, claims }`;
+ its own doc comment delegates organization and tenant identity to the untyped `claims` bag
+ (`:38-45`).
+- `grep -rn principal packages/plugin/src` → **zero matches**.
+- `grep -rn '\.meta(' packages plugins --include=*.ts` → no oRPC `.meta()` call anywhere; the only
+ matches are Zod schema metadata in `packages/aspire/config.ts`.
+- `packages/service/src/auth/types.ts` `AuthzRequest = { principal, method, path }` and
+ `packages/service/src/auth/scope-authorizer.ts:22-29` — rules match `request.path.startsWith(...)`.
+- Consumer proof that the gap is real at the seam: `research/repo-audit/services-sdk.md` S14 records
+ no compile-time link between injected context (`db`/`principal`/`traceHeaders`) and the router's
+ `$context()`, and no exported handler-context type.
+
+## Current surface
+
+A guarded service resolves a correct, hashed-logged `Principal` in middleware
+(`packages/service/src/auth/auth-middleware.ts:166-177,231-237`) and then loses its type on the way
+into the handler. Authorization rules are authored against derived RPC path strings — a rule *can*
+match `/api/rpc/v1/auth/signout` because `RPCLink` appends procedure segments, but that string is a
+consequence of the transport, not a declaration by the procedure. Renaming a router breaks a policy
+silently.
+
+## Target contract
+
+1. **Typed principal.** `@netscript/service` exports a handler-context type carrying an optional
+ `principal: Principal`, and `ContextFactory` is parameterized so `withContext` composes typed
+ fields instead of widening to `Record`. `@netscript/plugin` re-exports the
+ principal type so plugin handlers type it without importing from `@netscript/service` internals.
+2. **Procedure policy metadata.** A first-class `$meta`/`.meta()` policy annotation on contract
+ procedures — minimally `{ public: true }` or `{ scopes: readonly string[]; roles?: readonly
+ string[] }` — carried on the contract, not on a parallel rule table.
+3. **An authorizer adapter that reads it.** `createContractAuthorizer(contract)` derives decisions
+ from the declared metadata and composes with the existing `createScopeAuthorizer` (which stays
+ the path-prefix adapter, not the ceiling). Fail-closed remains the default: a procedure with no
+ declared policy and no matching rule is denied.
+4. **Generated-surface propagation.** OpenAPI emits `security` for procedures declaring a policy;
+ the generated SDK/MCP/agent surfaces expose the same fact so an agent can tell a protected
+ procedure from a public one.
+5. **Forward compatibility with #884.** The principal and policy shapes are designed so #884's
+ organization/membership/assurance model extends them rather than replacing them; this issue does
+ **not** add tenant fields.
+
+## Acceptance
+
+- [ ] A handler reads `context.principal` with the correct type and no cast.
+- [ ] `@netscript/plugin` exposes the principal type to plugin handlers.
+- [ ] `ContextFactory` composes typed fields instead of widening to `Record`.
+- [ ] Contract procedures can declare a policy (`public` or required scopes/roles).
+- [ ] An authorizer derives decisions from declared procedure metadata.
+- [ ] A procedure with no declared policy and no matching rule is denied.
+- [ ] OpenAPI emits `security` metadata for procedures that declare a policy.
+- [ ] Negative test: a handler that reads `context.principal` on an unguarded service fails to type-check or receives `undefined`, never a silently-trusted value.
+- [ ] Negative test: renaming a router breaks a contract-declared policy at compile time rather than silently unguarding the route.
+- [ ] Negative test: a procedure declaring scopes rejects a principal lacking them with 403.
+- [ ] Tests cover both the REST and RPC projections of a policy-annotated procedure.
+- [ ] `quality:scan` and `arch:check` green; no new `as unknown as` or `any` on the added surface.
+- [ ] The principal/policy shapes are reviewed against #884's contract sketch and the review is recorded.
+
+## Boundaries
+
+- **Do not** define organization-aware identity or policy contracts — **#884** owns canonical
+ organization, membership, connection, role/group, assurance and policy-decision types, and the
+ authorization request over subject × organization × resource × action × context. This issue
+ defines only the non-tenant principal and the procedure-level policy annotation, and must be
+ designed so #884 extends it. Cite #884 in the implementation PR and record the compatibility review.
+- **Do not** build the browser-facing deny-by-default gateway — **#934** owns it; it consumes this
+ metadata.
+- **Do not** implement the SDK client seam — **T1-05** owns it; it consumes the same metadata to
+ decide whether a call needs a credential.
+- **Do not** add `PluginServiceConfig.auth` — **TA-02** owns it.
+- **Do not** widen the type-soundness program — **#1278** is the epic-of-record for unsound types
+ (with **#1276** proposed for fold); this issue only avoids adding new unsound surface.
+- **Do not** build the conformance/mocking test kit — **#885** owns it.
+
+## Docs/consumer proof
+
+`docs/site/tutorials/workspace/05-route-authz.md` teaches authorization entirely through path
+prefixes. Adoption is proven when that tutorial declares a policy on the procedure and the route
+guard follows from it, when `docs/site/reference/` documents the principal type a handler receives,
+and when a generated MCP/agent tool listing distinguishes protected from public procedures without
+the author writing a second policy table.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gaps G9/G10 and `research/repo-audit/services-sdk.md` S14; all cited
+line numbers re-verified against worktree `fac9e339042c` on 2026-08-08.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-05-e2e-auth-gates-prove-nothing.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-05-e2e-auth-gates-prove-nothing.md
new file mode 100644
index 0000000000..2eab61324b
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/milestones/0.0.8-runtime-truth-service-slice/TA-05-e2e-auth-gates-prove-nothing.md
@@ -0,0 +1,111 @@
+# test(e2e): the scaffold runtime auth gate proves only that an unauthenticated request succeeds — DRAFT (no GitHub mutation; owner ratification pending)
+
+**Draft-ID:** TA-05 · **Proposed milestone:** 0.0.8 (post-shift "Runtime truth + service slice", SYNTHESIS §5.3) · **Labels:** `type:test` `area:cli` `area:auth` `area:tooling` `priority:p1` `status:triage` · **Depends on:** none for the harness capability; TA-01, TA-02 and TA-03a for the assertions themselves
+
+## Summary
+
+`scaffold.runtime`'s only auth behaviour gate is `behavior.auth-session`, an unauthenticated `GET
+/api/v1/auth/session` that passes when the response is 2xx. It is a liveness probe wearing an auth
+gate's name: it would pass identically if the auth service had no authorization at all, which is
+exactly today's state. The harness cannot express anything better — `HttpGateDefinition` carries only
+`{ kind, method, url }`, and `HttpGate` passes on `result.ok` and retries until the deadline
+otherwise. So there is no way to write "expect 401" or "send this credential", and consequently no
+gate anywhere proves an authenticated call succeeds or an unauthenticated call is refused.
+
+## Evidence
+
+- Corpus: `research/repo-audit/auth.md` §6 (last paragraph) and §7, gap **G12**.
+- `packages/cli/e2e/src/application/gates/scaffold/runtime-gates.ts:404-418` — the three auth gates:
+ `behavior.auth-live` (`/health/live`), `behavior.auth-ready` (`/health/ready`),
+ `behavior.auth-session` (`/api/v1/auth/session`). All are plain `httpGate` calls.
+- `packages/cli/e2e/src/application/gates/scaffold/gate-factory.ts:77-92` — `httpGate(id, title,
+ url, method = 'GET')`; no expected-status, header, body, or credential parameter.
+- `packages/cli/e2e/src/domain/gate-definition.ts:81-86` — `HttpGateDefinition { kind, method, url }`.
+- `packages/cli/e2e/src/application/gates/http-gate.ts` — "Gate that succeeds when an HTTP endpoint
+ returns a 2xx response"; the loop returns `passed` only on `result.ok` and otherwise retries until
+ `httpTimeoutMs` elapses.
+- `packages/service/tests/auth/define-service-auth_test.ts` is the only place a 401/403/200 triple is
+ asserted, and it targets `/api/openapi.json` on a hand-built service — never `/api/rpc/*`, and
+ never a generated project (`auth.md` §6).
+- `packages/cli/e2e/src/domain/cli-surface.ts:138-140` — the three gate ids, for reference when
+ adding new ones.
+
+## Current surface
+
+The generated-path auth evidence chain is: three 2xx probes against an unguarded service. A
+regression that removed the auth service's authorization entirely would leave every gate green. This
+is the same "green wrapper over absent runtime truth" failure mode SYNTHESIS §1.4 identifies across
+the runtime plugins.
+
+## Target contract
+
+1. `HttpGateDefinition` gains optional `expectStatus?: number | readonly number[]` and
+ `headers?: Record` (or a credential-factory callback resolved from `RunContext`),
+ and `HttpGate` asserts the expected status instead of `result.ok`. Retry semantics stay: a
+ connection failure retries, a wrong-but-served status fails fast rather than burning the deadline.
+2. New `scaffold.runtime` gates, ids following the existing `behavior.*` convention:
+ - `behavior.auth-session-unauthenticated` — expects **401** on `GET /api/v1/auth/session`.
+ - `behavior.auth-session-authenticated` — expects **200** with a valid credential.
+ - `behavior.auth-rpc-unauthenticated` — expects **401** on `POST /api/rpc/v1/auth/*`.
+ - `behavior.service-api-unauthenticated` — expects **401** on the generated user service's `/api`.
+ - `behavior.service-api-authenticated` — expects **200** on the same route with a credential.
+ - `behavior.auth-signout-foreign-session` — expects a refusal for a foreign session id (TA-03a).
+3. The credential the gates use comes from the generated project's own configuration, not from a
+ fixture the harness invents — the gate must fail if the generated project cannot produce one.
+4. The pre-fix behaviour is recorded: each new negative gate must be demonstrated red on
+ `fac9e339042c` before the corresponding fix lands.
+
+## Acceptance
+
+- [ ] `HttpGateDefinition` supports an expected status and request headers.
+- [ ] `HttpGate` asserts the expected status rather than any 2xx.
+- [ ] A wrong-but-served status fails the gate without consuming the full retry deadline.
+- [ ] `behavior.auth-session-unauthenticated` expects 401 and is green after TA-02.
+- [ ] `behavior.auth-session-authenticated` expects 200 with a credential.
+- [ ] `behavior.auth-rpc-unauthenticated` covers the `/api/rpc/*` projection.
+- [ ] `behavior.service-api-unauthenticated` and `behavior.service-api-authenticated` cover the generated user service.
+- [ ] `behavior.auth-signout-foreign-session` proves a foreign session id is refused.
+- [ ] The credential is produced by the generated project, not by a harness-only fixture.
+- [ ] Each new negative gate is demonstrated red against `fac9e339042c` and the evidence is linked.
+- [ ] gate: `deno task e2e:cli run scaffold.runtime --cleanup --format pretty` green with the new gates.
+- [ ] `behavior.auth-session` is either removed or renamed so no gate name implies auth coverage it does not provide.
+
+## Boundaries
+
+- **Do not** build the auth conformance, mocking, and scaffold test kit — **#885** owns the
+ security-focused kit (fake OIDC/JWKS, signed tokens, WorkOS/Better Auth fixtures, SCIM, clock and
+ replay controls, per-backend capability matrix) at milestone 0.0.12. This issue adds only the
+ generated-path behaviour gates that must pass before that kit exists, and its gates should be
+ re-expressible on #885's fixtures later without re-filing.
+- **Do not** implement the auth fixes themselves — **TA-01**, **TA-02**, **TA-03a** own them.
+- **Do not** extend gates to background children or `saga.*` spans — that is the T4 runtime-truth
+ E2E work, tracked separately in this plan.
+- **Do not** change Aspire port resolution in the gates — **#979** owns the hardcoded-port
+ dependency of `runtime-gates.ts`.
+- **Do not** re-file published-canary installed-consumer smoke — **#1343** owns it.
+
+## Docs/consumer proof
+
+The gate list is the consumer-visible claim: `deno task e2e:cli gates scaffold.runtime` currently
+advertises `behavior.auth-session` as auth coverage. Adoption is proven when that listing names
+explicit authenticated and rejected cases, and when a reviewer can point at a red-then-green gate id
+as the evidence for each of TA-01/TA-02/TA-03a rather than at a hand-run `curl`.
+
+## Provenance
+
+Seed run `plan-fable5-remediation-roadmap--seed`, PR #1347, 2026-08-08. Sourced from
+`research/repo-audit/auth.md` gap G12; all cited line numbers re-verified against worktree
+`fac9e339042c` on 2026-08-08.
+
+**Scope correction (recorded, not silently dropped).** The Stage-D brief paired this gate work with
+"auth docs debt: arch-debt anchors cited by `add-authentication.md` do not exist"
+(`research/repo-audit/auth.md` §7.1, gap G13). **That claim is false against this worktree.** The
+audit searched `docs/architecture/`; the registry the checker actually resolves against is
+`.llm/harness/debt/arch-debt.md`, where both ids exist —
+`seamless-auth-roadmap` at line 1240 and `auth-single-active-backend-boundary` at line 1313 — and
+`.llm/tools/docs/check-caveat-refs.ts:185-186` resolves `arch-debt:` against that file, wired as
+`check:caveats` in `docs/site/deno.json:6-7` and run in CI by `.github/workflows/pages.yml:38-40`.
+No dangling-anchor issue is drafted. The surviving docs defect from G13 — cookie claims contradicted
+by code — is carried by **TA-03b**'s acceptance; the zero-SDK-auth-guidance half belongs to the T5
+docs pack. This issue therefore stands alone as a test-coverage issue at `priority:p1` rather than
+the briefed `priority:p2` docs-polish, because a missing negative security gate is not docs polish.
diff --git a/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/research/external/eis-chat.md b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/research/external/eis-chat.md
new file mode 100644
index 0000000000..ec82686471
--- /dev/null
+++ b/.llm/runs/plan-fable5-remediation-roadmap--seed/fable-5-remediation-plan/research/external/eis-chat.md
@@ -0,0 +1,411 @@
+# eis-chat — the product-quality frontend bar (teardown)
+
+**Source of truth.** `rickylabs/eis-chat` cloned read-only (`--depth 1`) to
+`/home/codex/repos/netscript-fable5-remediation-plan/.llm/tmp/eis-chat`, HEAD
+`d38e3986392e70535d698668c6802b8105e5ea76` (2026-07-21, "Merge pull request #154 …
+agent/enterprise-windows-desktop"). All `apps/dashboard/**` paths below are relative to that clone.
+NetScript-side paths are relative to this worktree (`origin/main` @ `fac9e339042c`).
+
+**One-line verdict.** eis-chat is a NetScript app pinned at `0.0.1-beta.9`
+(`apps/dashboard/deno.json:29-35`) that reaches product quality by *inventing, in app space, six
+conventions the scaffold does not generate*: route guards, the degraded-loader error banner,
+server-validated forms, an imperative toast bus, an honest LIVE/DESIGNED data-provenance discipline,
+and a per-surface CSS ownership rule. Every API it uses already exists in `packages/fresh` on main.
+The gap is almost entirely **scaffold/generation + docs/discovery**, not missing framework API.
+
+---
+
+## 1. Stack (verified)
+
+| Concern | Choice | Citation |
+|---|---|---|
+| Runtime/build | Deno 2.9, Vite (`npm:vite --configLoader native`), Fresh 2.3.3 | `apps/dashboard/deno.json:7,38` |
+| UI | Preact, `jsx: precompile` + `jsxPrecompileSkipElements` (12 elements) | `apps/dashboard/deno.json:49-66` |
+| Framework | `@netscript/fresh@0.0.1-beta.9` (+ `/streams`, `/query`), `@netscript/fresh-ui@0.0.1-beta.9`, `@netscript/sdk@0.0.1-beta.9` | `apps/dashboard/deno.json:29-34` |
+| RPC/data | contract-first `createServiceClient` + `createQueryFactories` over `@eis-chat/contracts` | `apps/dashboard/lib/channel-service.ts:1-24` |
+| Styling | Tailwind v4 `@import 'tailwindcss'` + `--ns-*` token layer + per-block CSS files; `cn = twMerge(clsx(...))` | `apps/dashboard/assets/styles.css:1-3`, `apps/dashboard/lib/cn.ts:9-11` |
+| Chat | TanStack AI (`useChat`), durable streams via `@netscript/plugin-streams-core` | `apps/dashboard/islands/ChatPane.tsx:473`, `apps/dashboard/lib/stream-loaders.ts:23-27` |
+| Shell | `deno desktop --backend cef`, Windows singleton sidecar | `apps/dashboard/deno.json:12-13,71-82`, `apps/dashboard/main.ts:23-39` |
+| Auth | **none.** Zero hits for `plugin-auth`/`better-auth`/`getSession`/`requireAuth` in `apps/dashboard/**` | grep, 0 results |
+
+Scale: 21 page routes, 15 API routes, 4 `_middleware.ts`, 28 global islands, 48 app-owned UI
+primitives, 10 app-owned blocks, 44 lib modules + 18 lib test files, ~31.9k LoC of TS/TSX.
+
+---
+
+## 2. Route organization — the folder vocabulary (a)
+
+Fresh route groups `(…)` + underscore-prefixed dirs are used as a **non-routing colocation
+vocabulary**, confirmed against the generated manifest (`(_components)` etc. produce no route
+patterns — `apps/dashboard/.generated/manifest.ts:83-105` lists only real URLs).
+
+```
+routes/
+ _app.tsx html/head/theme-init/skip-link (34 lines)
+ _layout.tsx 3-pane app shell, SSR nav, breadcrumbs (240 lines)
+ (_components)/home-view.tsx route-local presentational view
+ (design)/design/ route GROUP: DS gallery with its own _layout + SidebarShell
+ (_components)/ (_islands)/ (_shared)/
+ api/… 15 handler-only routes
+ project/[project]/
+ _middleware.ts param guard
+ (_components)/project-view.tsx
+ channel/[channel]/
+ _middleware.ts param guard + active-channel cookie
+ index.tsx page BUILDER only — no JSX beyond withLayout
+ (_components)/channel-view.tsx 7 exported presentational sections
+ (_components)/create-session-form.tsx withForm component
+ (_islands)/SessionsGrid.tsx route-local island
+ knowledge/{_middleware.ts,index.tsx,[doc].tsx,(_components)/…}
+ session/[session]/{_middleware.ts,index.tsx,(_components)/…}
+ settings/{index.tsx,(_components)/…}
+```
+
+Counts: 21 route-local `(_components)` files, 4 route-local `(_islands)`, 2 `(_shared)`, versus 28
+*global* `islands/` files. **The rule in practice:** an island is global only when it is mounted by
+the shell or reused across surfaces (`islands/ui/*` = ThemeToggle, NavProgress, ActionToasts,
+NewSessionButton); anything owned by one route lives under that route's `(_islands)`.
+
+The page file is **only a builder**. `routes/project/[project]/channel/[channel]/index.tsx` is 295
+lines with zero presentational JSX except the `withLayout` slot grid (lines 265-287); all markup
+lives in `(_components)/channel-view.tsx`. That separation is the single most transferable
+convention in the repo.
+
+Route references: `apps/dashboard/router.ts` re-exports the generated tree and adds a **flat
+`appRoutes` alias map** (`router.ts:20-59`) because the generated shape is
+`routes.project.$project.channel.$channel.$route` (`.generated/routes.ts`). Every outbound link goes
+through `appRoutes.X.href({ path: {...} })` — no string literals — e.g.
+`channel-view.tsx:92-94,202-204,301`.
+
+---
+
+## 3. Data fetch + cache strategy (a)
+
+**Server tier — `definePage()` layers.** Canonical example
+`routes/project/[project]/channel/[channel]/index.tsx:148-292`:
+
+- `.withResource('data'|'mcp'|'skills', ctx => …)` — three independent resources, one of them an
+ aggregate that runs 4 reads under `Promise.all` (lines 113-118).
+- `.withLayer(name, Component, { loader: ctx => props })` — 7 layers, each loader projecting exactly
+ the props its component declares. Components never fetch.
+- `.withForm(...)` — server-validated mutation (§5).
+- `.withLayout(slots => JSX)` + `.withMeta(...)` + `.build()`, then
+ `export const { handler, default: page } = channelPage;`.
+
+**Loader error contract.** Every read is wrapped: `errorHandler(loader, fallback)()` →
+`hasError()` → collect `ErrorData[]` → `extractDataWithFallback()`
+(`index.tsx:106-134`, same shape at `session/[session]/index.tsx:66-90`). Errors are **de-duplicated
+by message** (line 132) because a total backend outage trips all four reads identically. The
+`errors` array is fed to a dedicated `issues` layer that renders an inline banner
+(`(_components)/channel-view.tsx:24-36`). This is the "degrade but surface" pattern, and it is the
+opposite of try/catch-to-empty.
+
+**Client tier — cache-first islands.** `QueryIsland` + `useIslandQuery({ queryKey, queryFn,
+initialData: , staleTime })`. Seeded from the server loader so first paint has data;
+`staleTime` 5s or 15s (`(_islands)/SessionsGrid.tsx:41-56`, `islands/ChannelTreeIsland.tsx:21-24`,
+`islands/SkillsPanel.tsx:78-83`, `islands/SessionRail.tsx:99-104`, `islands/SessionScratch.tsx:58-61`,
+`islands/KnowledgePanel.tsx:203-206`). Query keys are **contract-anchored**, never string literals:
+`channelQueries.listSessions.clientKey({ channelId })`.
+
+**Cache-key collision discipline.** `lib/channel-service.ts:12-24` documents that the factory-group
+name is the cache-key prefix and *must be unique per router*, citing a real
+`['service','list']` collision fixed in `lib/skills.ts`.
+
+**Streaming.** `lib/stream-loaders.ts:52-108` is a server-side prepare/resume helper: it
+create-if-missing's the durable stream so the island's first live read cannot 404, then materializes
+a resume offset. Best-effort throughout — three separate `catch` sites degrade to `{ messages: [] }`
+rather than 500 the page.
+
+---
+
+## 4. Form handling (a)
+
+`.withForm<'createSession', typeof Schema, Result>('createSession', Component, { schema, method:
+'POST', csrf: true, initial, mutate, redirectTo, spanName })`
+(`routes/project/[project]/channel/[channel]/index.tsx:212-236`). The zod schema carries the *user-
+facing* message (`'Name the session before starting it.'`, line 39). `mutate` calls the typed
+service client and returns a typed output; `redirectTo` builds the destination through
+`appRoutes.session.href(...)`; `spanName: 'channel.session.create'` names the telemetry span.
+
+The component receives `RuntimeFormState` and renders `