diff --git a/.llm/runs/release-0.0.6-features--orchestration/context-pack.md b/.llm/runs/release-0.0.6-features--orchestration/context-pack.md
new file mode 100644
index 0000000000..77cd40bec4
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/context-pack.md
@@ -0,0 +1,76 @@
+# Context pack — 0.0.6 runtime / public-surface lane
+
+Closing summary. The lane is **complete**: both owned issues landed on `main`. Read this first if
+resuming or auditing; everything below is traceable to a named artifact.
+
+| Field | Value |
+| --- | --- |
+| Run id | `release-0.0.6-features--orchestration` |
+| Profile | `.llm/harness/workflow/milestone-run.md` (topical lane) |
+| Supervisor | Claude · Opus 5 · high |
+| Control branch / PR | `chore/release-0.0.6-features-orchestration` / **#1525** (evidence only, closes nothing) |
+| Baseline | `origin/main@01aa12b67` |
+| Status | **Complete** — 2/2 issues merged, 2 follow-ups filed |
+
+## Outcome
+
+| Issue | PR | Merge | Gate record |
+| --- | --- | --- | --- |
+| #1405 durable producer rejection taxonomy | #1528 | `8ff1bcb8f` | `slices/pre-merge-gate-1528.md` |
+| #1398 job executions → durable job stream | #1536 | `d7e2b67b2` | `slices/pre-merge-gate-1536.md` |
+
+Both auto-closed `COMPLETED` via closing keywords; `status:shipped` on each issue and PR.
+
+## What changed, in one paragraph each
+
+**#1405** — two settled write **reason strings** misdescribed the state that produced them: a write
+rejected during the graceful close-drain reported `producer-failed` (the producer was healthy and
+closing), and a non-retryable append on attempt 1 reported `retry-exhausted` (nothing was exhausted).
+Fixed by making the closing intent observable to the rejection selector and by branching `#failActive`
+on `isRetryable`, adding exactly one public member, `transport-refused`. The façade's duplicate
+selector was deleted so the two cannot drift apart again — that drift was the defect's origin. No
+change to which writes are accepted, rejected, or delivered.
+
+**#1398** — job executions were never published to the durable job stream because the workers **API
+service** installed the execution-state mutation hook while the **background** entrypoints that
+generated projects actually run never did. Fixed by installing the hook on the worker and combined
+runtimes and publishing inside `context.with(extractContext({traceparent, tracestate}), …)` so the
+publish span joins the `job.execute` trace — including the pre-span `create()` record, which is the
+trap that would otherwise fail TC-14 silently. The two E2E gates deferred against this issue were
+un-deferred and now pass live.
+
+## Evidence chain
+
+- **#1398 acceptance was made mechanical.** Its observational criterion was bound to two gates the
+ repo had already deferred against this issue. Both now pass **by name** on both CI runtime tiers at
+ the merging head: postgres `94073971396` (`passed=88 failed=0 skipped=0`), sqlite `94073971501`
+ (`passed=83 failed=0 skipped=0`).
+- **PLAN-EVAL** #1398: PASS, MiniMax M3, separate session — `plan-eval.md`. Findings F1/F2 folded in.
+- **IMPL-EVAL** #1405: PASS, DeepSeek V4 Flash 0731 — `slices/evaluate-1405.md`. Superseded as
+ *policy* by D-3 after the fact, not retracted.
+- **IMPL-EVAL** #1398: PASS, Qwen 3.8 Max via the **automatic dispatcher** —
+ `slices/evaluate-1398.md`.
+- Merge order and every time-costing failure: `cut-trace.md`. Lessons and mistakes:
+ `retrospective.md`.
+
+## Open items leaving this lane
+
+| Item | Where |
+| --- | --- |
+| `quality:gate` roots omit published packages — **both merge records here rest on explicit target scans, not the repo gate** | **#1542** (0.0.7) |
+| Undeclared `@netscript/plugin-streams-core` imports — filed **unverified**, `publish:dry-run` evidence is its first acceptance box | **#1543** (0.0.7) |
+| Canary and stable cut | **root's**, not this lane — nothing was published here |
+
+## Drift index
+
+D-1 research sub-agent lane override · D-2 evaluator transport fallback · **D-3** IMPL-EVAL waived
+for the small deterministic class · **D-4** phase evaluation moved to the automatic dispatcher ·
+**D-5** label-driven eval trigger contract. Full text in `drift.md`.
+
+## Artifact hygiene note
+
+The two raw evaluator JSONL streams (2.4 MB combined) were **untracked** and moved to
+`.llm/tmp/` scratch, which `.gitignore` excludes. They were 2.4 MB of a 2.5 MB run dir, against a
+96 K largest-artifact precedent in the 0.0.5 run. Their substance is preserved verbatim in
+`plan-eval.md` and `slices/evaluate-1405.md`, each carrying run id, duration, event count, and
+`is_error`. Nothing was deleted — the files remain on disk locally.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/cut-trace.md b/.llm/runs/release-0.0.6-features--orchestration/cut-trace.md
new file mode 100644
index 0000000000..a8d10ff426
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/cut-trace.md
@@ -0,0 +1,47 @@
+# Cut trace — 0.0.6 runtime / public-surface lane
+
+Merge-derived record. Every row is captured **from `git log origin/main` after the merge**, never
+from recollection or from the dispatch plan. This lane does not cut or publish; root orchestration
+owns the canary and the stable cut.
+
+## Baseline
+
+| Field | Value |
+| --- | --- |
+| Lane opened | 2026-08-12 |
+| `origin/main` at open | `01aa12b67` — `docs(harness): record FILING-LOG -- board migration executed once (#1523)` |
+| Owned issues | #1405, #1398 |
+
+## Merges
+
+| # | UTC | Merge commit on `origin/main` | PR | Issues closed | Pre-merge gate record |
+| --- | --- | --- | --- | --- | --- |
+| 1 | 2026-08-12T08:20:29Z | `8ff1bcb8fc741db17a765a1965861828e0ae6171` (`8ff1bcb8f`) — `fix(streams): distinguish producer refusal reasons (#1528)` | #1528 | **#1405** (auto-closed `COMPLETED` by the body's `Closes #1405`) | `slices/pre-merge-gate-1528.md` — all 7 checks PASS |
+
+| 2 | 2026-08-12T10:27:26Z | `d7e2b67b2be535c9ca13449f97f8f4585344030a` (`d7e2b67b2`) — `fix(workers): publish job executions to the durable stream on the job.execute trace (#1536)` | #1536 | **#1398** (auto-closed `COMPLETED` by the body's `Closes #1398`) | `slices/pre-merge-gate-1536.md` — all 7 checks PASS at head `f7d503fee` |
+
+Both rows captured from `git log origin/main --first-parent -1` **after** each merge, per the
+profile's merge-derived rule. Issue states re-read live: both `CLOSED` / `COMPLETED`, with
+`status:shipped` applied to each issue and PR.
+
+**Lane complete: both owned issues landed on `main`.**
+
+## Re-planning events
+
+| # | Date | Event | Decision |
+| --- | --- | --- | --- |
+| 1 | 2026-08-12 | #1536's head changed mid-flight, `e4319c685` → `f7d503fee` | The phase-eval dispatcher (#1524) merged **after** the branch's last `main` sync, so `openhands-phase-eval.yml` was absent from the PR's merge ref and no label cycling could trigger it. Owner approved syncing the branch, accepting a full CI re-run. All gate evidence was then re-read against the new head; no pre-sync evidence was carried forward. |
+| 2 | 2026-08-12 | Evaluation route changed twice mid-run | D-3 removed formal IMPL-EVAL for the small deterministic class (after #1405 had already merged); D-4/D-5 moved phase evaluation to the automatic label-driven dispatcher. #1398's IMPL-EVAL consequently ran on the automatic route rather than a manual launch. |
+
+## Failure modes that cost real time
+
+| # | Date | Failure | Cost | Mitigation recorded |
+| --- | --- | --- | --- | --- |
+| 1 | 2026-08-12 | **Dispatcher absent from the PR's merge ref.** #1524 merged 38 min after #1536's branch last synced with `main`, so `openhands-phase-eval.yml` did not exist for that PR. Labels were cycled correctly and produced **no run at all**; the only runs were the older `openhands-agent.yml`, all `skipped`. | One wasted label re-entry, plus a full CI re-run (~20 min, both `scaffold-runtime` tiers) after the branch sync. | For `pull_request` events GitHub resolves workflows from the **merge ref**. A newly merged workflow cannot fire on a PR whose head predates it. **Check the workflow exists in the PR head before concluding a trigger failed.** Diagnosed by `git cat-file -e
:` → ABSENT, with the post-sync run succeeding as the control. |
+| 2 | 2026-08-12 | **Draft PRs report every check as `skipping`.** #1528 looked "clean" while nothing substantive had run. | None — caught by pre-merge check 4 before merge. | This is the #778/#775 class, alive and current. Absence of red is not green. The blocking tier only runs after draft→ready. |
+| 3 | 2026-08-12 | **Two live `scaffold.runtime` runs died before reaching the gates under test** — run 1 on a transient `generate plugins: fetch failed`, run 2 on a `triggers-api` health timeout at 120 s. | ~25 min of local runtime, no verdict produced. | Local WSL was not a usable arbiter for this suite. CI ran the same suite with the same change to a clean finish on both tiers, which is the control that established the local failures as environmental. **Do not label a local red a "flake" without that control.** |
+| 4 | 2026-08-12 | **A counting watcher would never have fired.** OpenHands updates its summary comment **in place** (`openhands-agent-summary` marker, `"conclusion"` field), so a watcher keyed on comment count polls to timeout while the verdict sits in an edited comment. | None — caught before arming, on inspecting the posted comment's markers. | Watch the **run status** and the comment's `conclusion` marker, never comment count. |
+| 5 | 2026-08-12 | **Stale check summary read as current.** `gh pr checks` reported `close-gate` red from a job that ran 40 min earlier, before the label and body changes it was complaining about. | None — caught by reading the job log, which carried the mirror's own "skipped because labels do not include status:ready-merge" notice. | Compare a check's run time to the change it is judging. The gate-integrity rule already says merge-history audits must take the latest run per check name; the same applies pre-merge. |
+| 6 | 2026-08-12 | **Automation added a second `status:` label.** `status:augment-review` was applied 1 s after the orchestrator moved off `status:impl-eval`, breaching the exactly-one-status invariant. | None — caught while verifying labels before merge. | Re-verify the `status:` set after any automated phase transition, not only after manual edits. |
+| 7 | 2026-08-12 | **Orchestrator's own slice brief named a broken gate command.** `deno test packages/plugin-streams-core` exits 1 with 19 `NotCapable` errors for want of `--allow-env`. | Minor; the implementer reported the red with its cause rather than hiding or working around it. | Use the package-declared `deno task --cwd test`. Corrected in the #1398 brief rather than repeated. |
+| 8 | 2026-08-12 | **One unnecessary evaluator dispatch.** The lane brief's IMPL-EVAL waiver for the #1405 class was read as a blocked-transport fallback rather than the class default. | One DeepSeek IMPL-EVAL run (~643 s) that the owner did not want. | Recorded as D-3. "A waiver is available" and "the waiver is the default" are different instructions; resolve the ambiguity before spending. |
diff --git a/.llm/runs/release-0.0.6-features--orchestration/drift.md b/.llm/runs/release-0.0.6-features--orchestration/drift.md
new file mode 100644
index 0000000000..6fbe47c6fc
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/drift.md
@@ -0,0 +1,124 @@
+# Drift — 0.0.6 runtime / public-surface lane
+
+Append-only. Severity: `minor` | `significant` | `architectural`.
+
+## D-1 — research/plan sub-agent lane overridden to Opus (minor)
+
+**Date** 2026-08-12. **Canonical route** `deep_analysis` = Claude · Fable 5 · medium. **Used**
+Claude · Opus 5 medium/high. **Reason** the owner brief for this lane explicitly directs
+"Delegate plan/research to Claude Opus medium/high sub-agents when useful". Owner instruction is
+more specific than the default lane binding. Invariants preserved: generator ≠ evaluator, no lane
+self-certifies, no paid escalation. Fable 5 · low remains the `review_codex` reviewer for the
+#1398 slice, so opposite-family review of Codex work is untouched.
+
+## D-2 — evaluator transport falls back to local fresh sessions (minor)
+
+**Date** 2026-08-12. The brief routes PLAN-EVAL/IMPL-EVAL through OpenHands *after #1524
+passes/lands*. Observed: PR #1524 is **OPEN**, `mergedAt: null`, with unticked DoD boxes for the
+bounded live DeepSeek smoke and the repository default variable. Per the brief's own condition, this
+run uses fresh local Claude/OpenCode OpenRouter evaluator sessions through the toolchain. Re-checked
+before each dispatch; state recorded at the point of use.
+
+## D-3 — IMPL-EVAL not required for small deterministic taxonomy fixes (significant)
+
+**Date** 2026-08-12. **Owner ruling.** For the #1405 class — small, deterministic, fully specified
+fixes with exact negative tests — a separate formal IMPL-EVAL is **not** to be dispatched. The
+sufficient evidence set is: focused negative tests, CI, close-gate, and the orchestrator's own
+independent diff review.
+
+**My error.** The lane brief said an IMPL-EVAL owner waiver was "acceptable" for #1405 with exact
+negative tests. I read that as a fallback to use only if the evaluator transport was blocked, and
+recorded that reading in `supervisor.md`. The owner's intent was that the waiver is the **default**
+for this class. One evaluator dispatch (DeepSeek V4 Flash 0731 max, 642,836 ms) was spent that should
+not have been.
+
+**Timing, stated plainly.** The ruling arrived after #1405 had already been evaluated **and merged**
+(`8ff1bcb8f`, 2026-08-12T08:20:29Z). It therefore does not retract that merge or its evidence — the
+IMPL-EVAL returned PASS and its per-fix revert isolation is real evidence that remains in the record.
+The ruling governs this class going forward.
+
+**Not weakened for #1398.** Formal PLAN-EVAL and IMPL-EVAL remain **mandatory** for #1398: it changes
+public runtime behaviour, publishes a new record to a durable stream, and requires live evidence.
+Its PLAN-EVAL has already run (PASS, MiniMax M3) and its IMPL-EVAL is still required before merge.
+
+**Cleanup.** The detached #1405 evaluator worktree `/home/codex/repos/ns006-1405-impleval` was
+verified clean (empty `git status --porcelain`) and removed. The #1398 PLAN-EVAL worktree
+`/home/codex/repos/ns006-1398-planeval` is retained pending that issue's IMPL-EVAL. Pre-existing
+global stashes belonging to other lanes' branches were left untouched.
+
+## D-4 — phase evaluation moves to the automatic status dispatcher (significant)
+
+**Date** 2026-08-12. **Owner ruling.** PR #1524 (automatic phase dispatcher) is about to merge. Once
+it lands, **all future phase evaluations use the automatic status workflow** unless the owner selects
+a documented local route or an explicit skip. Manual IMPL-EVAL launches and manual
+`@openhands-agent` PR comments are not to be used for #1536.
+
+**Standing instruction for #1536:** keep it on its **current head and status** — head `e4319c685`,
+`status:impl-eval`, milestone `0.0.6`. Root will **deliberately re-enter** `status:impl-eval` with
+the Qwen override *after* #1524 lands, which is what triggers the automatic dispatcher. This
+orchestrator must not re-enter the label for the same head, must not trigger OpenHands, and must not
+launch a local evaluator. Its remaining job is to **watch the automatic verdict and then finish the
+merge gate**.
+
+**Timing, recorded factually.** The steer anticipated that a local #1536 evaluator had already been
+launched. It had **not**. For #1398 I wrote the evaluator prompt
+(`slices/impl-eval-1398-prompt.md`, 10:57) and pre-created the detached worktree
+`/home/codex/repos/ns006-1398-impleval` at `e4319c685`, then **stopped and raised the decision**
+rather than dispatching. Verified at the time of this entry: no `openrouter-run`/`claude-openrouter`
+process, and no `impl-eval-1398-raw.md` output file — only the prompt. **No duplicate spend occurred
+and none was in flight.**
+
+The two evaluator sessions this run did spend are unaffected and both completed before this ruling:
+PLAN-EVAL #1398 (MiniMax M3, PASS) and IMPL-EVAL #1405 (DeepSeek V4 Flash 0731, PASS — itself the
+subject of D-3).
+
+**Unused artifacts retained, not removed:** `slices/impl-eval-1398-prompt.md` and the
+`ns006-1398-impleval` worktree are left in place, clean and unused, in case the owner later selects
+the documented local route. They are inert; nothing reads them.
+
+**Interaction with D-3.** D-3 waives formal evaluation for the small deterministic class. D-4 does
+not widen that waiver — #1398 still gets a formal IMPL-EVAL; it now arrives through the automatic
+dispatcher rather than a manual launch.
+
+## D-5 — automation trigger contract for formal evaluation (significant)
+
+**Date** 2026-08-12. **Owner policy.** Formal PLAN/IMPL evaluation is triggered by labels, never by a
+manual OpenHands dispatch:
+
+| Phase | Initial trigger | Rerun |
+| --- | --- | --- |
+| PLAN-EVAL | the `openhands` + `status:plan-eval` label **pair**, exactly once | move away from `status:plan-eval`, then re-add |
+| IMPL-EVAL | automatically on **draft → ready**, unless `impl-eval:skip` | move away from `status:impl-eval`, then re-add |
+
+`eval:model:minimax|deepseek|qwen` is an optional **one-shot** override. A local eval already running
+may finish, but must never be duplicated. Manual `@openhands-agent` dispatch for formal PLAN/IMPL
+eval is prohibited. Merge continues to go through normal harness authority.
+
+**This lane complies with no change required:** no manual OpenHands dispatch was made for #1536 (or
+for anything else this run), and no local evaluator was launched for #1536 — the prompt and worktree
+were prepared and the dispatch deliberately withheld (D-4). The two local evaluator sessions this run
+did spend — PLAN-EVAL #1398 (MiniMax M3) and IMPL-EVAL #1405 (DeepSeek) — both **completed** well
+before this policy and are not duplicated.
+
+### Timing finding: #1536's automatic IMPL-EVAL could not have fired
+
+Measured from the issue timeline rather than assumed:
+
+| Event | UTC |
+| --- | --- |
+| #1536 `ready_for_review` | **2026-08-12T08:53:43Z** |
+| #1536 `status:impl` removed, `status:impl-eval` applied | 2026-08-12T08:53:45Z |
+| #1524 (the dispatcher) merged | **2026-08-12T09:24:15Z** |
+
+Both candidate triggers precede the dispatcher's existence by ~30½ minutes. The draft → ready
+transition therefore had nothing to fire, and the initial automatic IMPL-EVAL for #1536 **did not
+run and will not run on its own**.
+
+Under this policy the rerun path is the only one left: move #1536 away from `status:impl-eval`, then
+re-add it. That matches what root already stated it would do with the Qwen override, so the
+conclusion is unchanged — but it is now a **requirement** rather than a preference, and a watcher
+waiting for a spontaneous verdict would wait forever. Recorded so that is visible rather than
+discovered by timeout.
+
+**Not actioned by this lane.** The label re-entry is root's, per D-4. This orchestrator has not
+touched #1536's head, labels, or body.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/plan-eval-prompt.md b/.llm/runs/release-0.0.6-features--orchestration/plan-eval-prompt.md
new file mode 100644
index 0000000000..e3208cbf46
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/plan-eval-prompt.md
@@ -0,0 +1,94 @@
+use harness
+
+# PLAN-EVAL — NetScript issue #1398
+
+You are a **formal PLAN-EVAL evaluator** in a fresh session. You did not write this plan and you are
+not its supervisor. Your job is to try to **break** it, then return a verdict.
+
+Worktree: `/home/codex/repos/ns006-1398-planeval` (detached at `01aeafbfa`). **Read-only** — do not
+edit, commit, or push anything. You may run read-only commands (`git`, `grep`, `deno doc`,
+`deno check`) to verify claims.
+
+## SKILL
+
+- `netscript-harness` — read `.llm/harness/evaluator/plan-protocol.md`,
+ `.llm/harness/gates/plan-gate.md`, and `.llm/harness/evaluator/verdict-definitions.md`.
+- `netscript-doctrine` — plugin/package layering, Archetype 5 thinness, public-surface rules.
+
+## Read
+
+1. `.llm/runs/release-0.0.6-features--orchestration/plan.md` — the plan under evaluation.
+2. `.llm/runs/release-0.0.6-features--orchestration/slices/research-1398.md` — the research it rests
+ on. **Note its "Unverified" section: that research pass was terminated early on budget.**
+3. The live issue #1398 body and its four acceptance boxes.
+4. Whatever source you need to check the plan's claims. The plan cites specific `path:line`
+ locations — check them.
+
+## What to attack, in priority order
+
+The plan's entire design rests on **one causal chain**. Verify it or break it:
+
+> `job-dispatcher.ts:44` derives `parentContext` from the stored trace headers and passes it to
+> `traceJobExecution` (`:108`), so `job.execute` shares a trace id with the execution record's
+> stored `traceparent`; and `instrumentation.ts:160` starts the stream publish span on the **ambient**
+> OTel context. Therefore publishing execution mutations under a context extracted from the stored
+> `traceparent` makes every published record — including the pre-span `create()` one — carry a header
+> traceparent whose **trace id** equals the `job.execute` trace id, satisfying TC-14
+> (`select-flow-b-stream-change.ts:122-153`).
+
+Specific things worth doubting:
+
+1. **Is the trace id really shared?** `getParentContextFromHeaders` may return a context that is
+ remote/non-recording, or `withSpan` may ignore `parentContext` under some configuration. If
+ `job.execute` starts a **new trace** rather than continuing the dispatch trace, decision D3 is
+ wrong and the plan fails.
+2. **Does TC-14 assert what the plan says it asserts?** Read
+ `select-flow-b-stream-change.ts:122-153` and `consume-flow-b-stream.ts:52-107,203-230` yourself.
+ If the gate compares full `traceparent` rather than trace id, or matches a record the plan does
+ not anticipate, say so.
+3. **Selector ambiguity.** Four records share a `correlationId`. Does the selector take the first,
+ the last, or all? If it takes the first and that is the `create()` record, does the plan's D3
+ actually save it — or does D3 only work if the extracted context is non-remote?
+4. **Is `SCAFFOLD_RUNTIME_DEFERRED_GATES` really the acceptance surface** the plan claims (D5), and
+ does removing those two entries make `scaffold.runtime` run them?
+5. **S0's blocking precondition.** The plan blocks on whether `workers-combined` receives the streams
+ env. Read
+ `packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:180-220`
+ and `stream-url-resolver.ts:154-190` and **answer it now** if you can — it is the cheapest way to
+ de-risk the whole slice, and the research never got to it.
+6. **Idempotency of D1.** Can `startCombinedProcess` end up installing the hook twice, or installing
+ it where the API service already did? What happens then?
+7. **Scope honesty.** Does anything in the plan quietly require the `WorkerExecutionZodSchema` change
+ that D4 forbids? If so the plan is internally inconsistent.
+
+Also apply the plan-gate checklist: contract-first, acceptance mapped to evidence, gates named,
+risks recorded, no criterion that cannot be truthfully ticked.
+
+## Output
+
+Write your verdict to
+`.llm/runs/release-0.0.6-features--orchestration/plan-eval.md` **as text in your final message**
+(do not write the file — the orchestrator commits it). Structure:
+
+```
+**[PHASE: PLAN-EVAL] [VERDICT: PASS|FAIL_PLAN]**
+
+
+
+### Verified
+- —
+
+### Findings
+1. **C1 ** — what is wrong, where, and the concrete fix.
+...
+
+### Answers to open questions
+- S0 (streams env reaches workers-combined):
+
+### Next
+-
+```
+
+Rules: every finding cites `path:line`. Distinguish **blocking** (`FAIL_PLAN`) from advisory. If you
+cannot verify something, say "could not verify" — do not guess, and do not pad the verdict with
+praise. A finding I can check is worth more than a paragraph of assessment.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/plan-eval.md b/.llm/runs/release-0.0.6-features--orchestration/plan-eval.md
new file mode 100644
index 0000000000..fae5dd3d36
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/plan-eval.md
@@ -0,0 +1,58 @@
+# PLAN-EVAL — #1398 (verbatim verdict)
+
+| Field | Value |
+| --- | --- |
+| Phase | PLAN-EVAL |
+| Subject | `plan.md` for issue #1398 |
+| Evaluator | MiniMax M3 · high, `claude-evaluator-minimax-m3` preset over OpenRouter |
+| Session | fresh, separate from the generator; worktree `/home/codex/repos/ns006-1398-planeval` @ `01aeafbfa` |
+| Route reason | local fallback per `drift.md` D-2 (#1524 still open) |
+| Duration | ms |
+| is_error | False |
+| Raw stream | `plan-eval-raw.md` (215 events) |
+
+A leaked one-line preamble ("Now I have enough to write the verdict…") preceded the verdict token in
+the raw stream and is stripped here; nothing else is edited.
+
+---
+
+**[PHASE: PLAN-EVAL] [VERDICT: PASS]**
+
+Plan is sound and the causal chain holds; one unresolved open decision worth flagging before implementation.
+
+### Verified
+
+- **Trace id chain** — `plugins/workers/worker/job-dispatcher.ts:43-44` (`parentContext = tracedContext?.parentContext ?? getParentContextFromHeaders(traceHeaders)`) → `:108` (`traceJobExecution({...,parentContext}, ...)`) → `packages/telemetry/src/instrumentation/worker.ts:323-385` (`withSpan(tracer, ..., { parentContext, ... })`) → `packages/telemetry/src/application/span.ts:38-43` (passes `parentContext` as the third arg to `tracer.startSpan`). The `job.execute` span's trace id is therefore the dispatch traceparent's trace id.
+- **Ambient publish span** — `packages/plugin-streams-core/src/telemetry/instrumentation.ts:160-170` calls `this.#tracer.startSpan(StreamSpanNames.PUBLISH, { kind, attributes })` with no parent context. The `headers.traceparent` is `formatTraceparent(span.spanContext())` (line 172). The publish span's trace id is the active OTel context's trace id at the time of `startSpan`.
+- **TC-14 assertion** — `packages/cli/e2e/src/application/gates/scaffold/select-flow-b-stream-change.ts:131-153` extracts the trace id from the matched record's `headers.traceparent` (regex `^[0-9a-f]{2}-([0-9a-f]{32})-...` at line 142) and asserts equality with `identity.traceId`. The comparison is trace id only, not the full `traceparent`. Plan's claim is correct.
+- **Selector match semantics** — `select-flow-b-stream-change.ts:96-105` returns on the first matching `correlationId`. With D3, every record's trace id equals the dispatch traceparent's trace id, so TC-14 passes regardless of which record returns first.
+- **Stored traceparent on the execution record** — `packages/plugin-workers-core/src/state/execution-state.ts:73-75` (and earlier in `create` at lines 154-175) store `traceparent`/`tracestate` from the queue message. The dispatch traceparent is identical to that stored on `create()` (since `create()` runs before `traceJobExecution` and accepts the queue's trace headers).
+- **S0 — streams env reaches workers-combined** — YES. `packages/cli/src/kernel/adapters/plugin/plugin-reference-reconciler.ts:70-91` adds plugin dependencies (including `streams`) and the canonical plugin name to `backgroundReferences` for `workers-combined`. `packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:200-218` then injects `services__streams__http__0` for each plugin reference. `plugins/streams/src/public/mod.ts:45-49` registers `streams` as a service. `packages/plugin-streams-core/src/application/stream-url-resolver.ts:38` and `:108` read `services__streams__http__0` (server-side). The env path is fully wired.
+- **D4 vs reality** — `traceparent`/`tracestate` are already on `ExecutionRecord` (execution-state.ts:73-75). The schema change D4 forbids is not required by D3, since D3 only reads the fields that already exist. No internal conflict.
+- **D1 idempotency** — `KvExecutionState.setMutationHook` (execution-state.ts:147-149) is destructive (single `onMutation` field). If both `workers-api` and `workers-combined` independently call it, the second call replaces the first. Since both call it with `createStreamMutationHook()` (the same behavior), the only consequence is a wasted hook construction; no double-install hazard.
+- **R2 record volume** — `packages/plugin-streams-core/src/domain/producer-contract-v1.ts:50-53` shows the 256-event / 1 MiB bounded buffer. Plan correctly notes this is volume, not correctness.
+- **`isRecord` check in selector** — `select-flow-b-stream-change.ts:122-128` reads `value.correlationId` first. `toExecutionStreamEntity` (producer.ts:94) sets `correlationId: execution.correlationId`. ✓
+
+### Findings
+
+1. **F1 (advisory) — D3 mechanism is implemented by the slice, not enumerated by the plan.** The plan says "publish under an OTel context extracted from the execution record's stored `traceparent`/`tracestate`" but the existing `createStreamMutationHook` (`packages/plugin-workers-core/src/streams/producer.ts:108-118`) calls `producer.upsert(...)` with no `context.with(...)` wrapping. The `StreamsTracerPort.startSpan` interface (`packages/plugin-streams-core/src/telemetry/instrumentation.ts:92-102`) does not accept a parent context argument, so the only way for the publish span to inherit the stored traceparent's trace id is for the hook to wrap the `producer.upsert` call with `context.with(extractContext({traceparent, tracestate}), () => producer.upsert(...))`. The plan's wording is correct but the implementation step is implicit. **Slice review should confirm the implementation explicitly wraps with `context.with(...)` and adds a unit test that pins the trace id under the active context override (the plan already names this in tests #1 and #3).**
+
+2. **F2 (advisory, scope honesty) — S2 misses a second test in `suite-registry_test.ts`.** The plan updates `suite-registry_test.ts:204-215` (the test that asserts `runtime.gates.some(...)` is false for the two gates) but **misses the second test at lines 209-234** (`runtime suites pin the exact #1398 OTEL deferral without widening it`). That test at line 210-221 asserts `SCAFFOLD_RUNTIME_DEFERRED_GATES` exactly equals the two-gate list. After D5 removes both entries, the constant becomes empty (or zero entries), and the assert fails. The test must be removed or rewritten in the same commit as the deferral-list change. **Block-level for S2's "red-on-regression" claim** — if S2 is committed without updating this test, the planned local unit-test gate (or the Tier-A review) will catch and block it. Either is the right time; the plan should name it.
+
+3. **F3 (advisory) — The `instrumentation.ts:160` cite is correct but load-bearing.** The plan's claim that `tracer.startSpan` is called with no explicit context is critical to D3's logic. Verified directly. ✓
+
+4. **F4 (advisory) — Promise chain does not lose the OTel context.** `executionState.create` (execution-state.ts:152-178) → `#save` (line 304-307) → `setMutationHook` callback is in a single async chain. `await this.#kv.set(...)` yields, but the active context within the resumption is the caller context (no parent span at the time of `create()`). Wrapping the hook with `context.with(extracted, ...)` works correctly across the `await` boundary because async contexts in Deno are preserved by the runtime. ✓
+
+5. **F5 (acknowledged) — R3 undeclared `@netscript/plugin-streams-core` import in `packages/plugin-workers-core/deno.json`.** Confirmed: `deno.json` lines 24-35 do not list it, but `src/streams/producer.ts:1` imports it. If `deno publish` rejects it, the slicing is incomplete. The plan correctly defers this. Acceptable.
+
+6. **F6 (advisory) — Selector ambiguity is correctly resolved by D3.** Four records share `correlationId`. Without D3, the first match (`create()`) fails TC-14 because its publish span has a different trace id. With D3, all four records share the trace id — so any match passes TC-14. The plan's reasoning is correct. ✓
+
+### Answers to open questions
+
+- **S0 (streams env reaches workers-combined)**: YES, answered. Citations: `packages/cli/src/kernel/adapters/plugin/plugin-reference-reconciler.ts:70-91` (background inherits plugin dependencies) → `packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:200-218` (env injection) → `plugins/streams/src/public/mod.ts:45-49` (service declared) → `packages/plugin-streams-core/src/application/stream-url-resolver.ts:38,108` (resolution). The plan's S0 blocker is **de-risked**; the slice can proceed without an "is this wired?" probe.
+
+### Next
+
+- **Slice review (Tier-A)**: when each implementation slice lands, verify (a) the hook wraps `producer.upsert` with `context.with(extractContext({traceparent, tracestate}), ...)` and the unit test pins the span trace id (F1), and (b) S2 updates both `suite-registry_test.ts:204-215` AND `suite-registry_test.ts:209-234` in the same commit (F2).
+- **Implementation lane**: proceed with S1 (D1+D2+D3) and S2 (D5 + the missed test) per the plan. S3 (live E2E) is the gate. The two formerly-deferred gates must pass in a real `scaffold.runtime` run.
+- **R3 undeclared import**: tracked separately, not blocking this slice per the plan.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/plan.md b/.llm/runs/release-0.0.6-features--orchestration/plan.md
new file mode 100644
index 0000000000..2546759c89
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/plan.md
@@ -0,0 +1,193 @@
+# Plan — #1398 publish job executions to the durable job stream
+
+Lane: 0.0.6 runtime / public-surface. Control PR #1525. Baseline `origin/main@01aa12b67`.
+Research: `slices/research-1398.md`. Target branch `fix/1398-publish-job-executions-to-durable-stream`.
+
+This plan goes to **PLAN-EVAL in a separate session** before any implementation, per
+`supervisor.md`.
+
+## Contract
+
+A completed job execution must produce a record on the durable job stream that a consumer,
+subscribed **before** the trigger, observes within a bounded time, carrying an identifier that joins
+it to its `job.execute` span. The E2E gate that encodes this already exists and is currently
+**deferred against this issue**:
+
+- `behavior.otel.stream-consumer` and `behavior.otel.traces`, registered at
+ `packages/cli/e2e/src/application/gates/scaffold/otel-gates.ts:52-84`, excluded from
+ `scaffold.runtime` / `scaffold.runtime.sqlite` via `SCAFFOLD_RUNTIME_DEFERRED_GATES`
+ (`packages/cli/e2e/suites/scaffold/capability-suites.ts:23-34`), with the deferral reason
+ *"workers-combined does not install the stream mutation hook"* asserted by
+ `packages/cli/e2e/tests/presentation/suite-registry_test.ts:204-215`.
+
+**Definition of done for this issue is therefore mechanical, not rhetorical: those two gates come
+out of the deferral list and pass in a real `scaffold.runtime` run.** That is what makes "no
+hand-waved timing" enforceable — the bounded-time criterion is the gate's own live SSE loop
+(`consume-flow-b-stream.ts:203-230`), not a stopwatch in a PR comment.
+
+## Root cause (verified)
+
+The hook is installed only by the workers **API service**
+(`plugins/workers/services/src/main.ts:67`). The **background** entrypoints that generated projects
+actually run never install it: `plugins/workers/bin/runtime.ts:89-107`, `:110-122`, `:125-152`.
+Generated projects run `startCombinedProcess()`
+(`plugins/workers/src/adapter/resources/glue/runtime.stub.ts:21-24`) as the Aspire resource
+`workers-combined` (`plugins/workers/src/aspire/workers-contribution.ts:12,56-61`). Job definitions
+are published from a different site (`plugins/workers/services/src/init.ts:91-111`), which is why
+the stream contained exactly three startup snapshots and nothing else.
+
+This is a **wiring gap**, not a missing feature. The producer, the mapper, the hook, and the
+execution record's trace fields all already exist.
+
+## The decision this plan turns on, and the fact that settles it
+
+The research correctly flagged a sharp edge: every execution-state mutation fires the hook
+(`packages/plugin-workers-core/src/state/execution-state.ts:288,307`), so installing it publishes
+~4 records per execution on the same key — and `executionState.create()`
+(`plugins/workers/worker/job-dispatcher.ts:74-86`) runs **before** the `job.execute` span exists
+(`:91`). Since TC-14 (`select-flow-b-stream-change.ts:122-153`) matches on `correlationId` and then
+asserts the matched record's `headers.traceparent` trace id equals the `job.execute` trace id, a
+naive "install the hook, publish everything" fix can match a record that fails the assertion.
+
+Two facts I verified directly resolve this:
+
+1. **`job.execute` is a child of the stored dispatch traceparent.**
+ `plugins/workers/worker/job-dispatcher.ts:44` computes
+ `parentContext = tracedContext?.parentContext ?? getParentContextFromHeaders(traceHeaders)` and
+ passes it to `traceJobExecution` at `:108`, which forwards it as `parentContext`
+ (`packages/telemetry/src/instrumentation/worker.ts:300`). So the `job.execute` span **shares its
+ trace id** with the `traceparent` already stored on the execution record
+ (`execution-state.ts:74`).
+2. **The publish span's trace id comes from the ambient OTel context.**
+ `packages/plugin-streams-core/src/telemetry/instrumentation.ts:160` calls `startSpan` with no
+ explicit context, and the header is `formatTraceparent(span.spanContext())` (`:172`).
+
+**Therefore:** if the mutation hook publishes under a context extracted from the execution record's
+own stored `traceparent`, then *every* published record — including the pre-span `create()` one —
+carries a header traceparent whose trace id equals the `job.execute` trace id. TC-14 passes no
+matter which of the four records the selector matches first.
+
+That removes the sharp edge without moving `create()` and without restricting publication to
+terminal states.
+
+### Alternatives considered and rejected
+
+| Option | Why rejected |
+| --- | --- |
+| Publish only terminal (`complete`) mutations, relying on ambient context | Passes TC-14 by accident of call placement — the join holds only because `complete()` happens to sit inside the span callback (`:165-170`). Any future refactor that moves the call silently breaks the join with no failing test. It also discards running-state visibility that the stream is for. |
+| Move `executionState.create()` inside the `job.execute` span | Reorders execution-state semantics (the record must exist before the span names its `executionId`, `:88` → `:103`) to serve a telemetry concern. Wrong direction of dependency. |
+| Add `traceparent` to `WorkerExecutionZodSchema` and join on the field | The gate asserts the **header**, not the field (`select-flow-b-stream-change.ts:122-153`). Changing the schema also raises a published-surface version question the research could not settle ("whether the streams schema carries an explicit version marker" — unverified). Not needed for the join. |
+
+## Locked decisions
+
+- **D1** — Install the stream mutation hook on the background runtimes in
+ `plugins/workers/bin/runtime.ts`, mirroring `plugins/workers/services/src/main.ts:65-75`. Cover
+ `startWorkerProcess` and `startCombinedProcess`; `startSchedulerProcess` only if it owns execution
+ state. Installation must be idempotent and must not double-install when a process embeds the API
+ service.
+- **D2** — Publish **all** execution mutations (created / updated / deleted), not just terminal ones.
+- **D3** — The hook publishes under an OTel context extracted from the execution record's stored
+ `traceparent`/`tracestate`, so the publish span joins the `job.execute` trace. This is the
+ join mechanism; it is not optional and it is what the new unit test pins.
+
+ **Amended after PLAN-EVAL (finding F1 — verified).** The mechanism is now explicit rather than
+ implied: `StreamsTracerPort.startSpan`
+ (`packages/plugin-streams-core/src/telemetry/instrumentation.ts:92-102`) takes **no parent-context
+ argument**, so the *only* way for the publish span to inherit the stored trace id is for
+ `createStreamMutationHook` (`packages/plugin-workers-core/src/streams/producer.ts:108-118`) to wrap
+ its `producer.upsert(...)` call in
+ `context.with(extractContext({ traceparent, tracestate }), () => producer.upsert(...))`. Today that
+ call has no wrapping at all. An implementation that installs the hook without this wrapping will
+ appear to work and will fail TC-14 on the `create()` record — the exact trap this plan exists to
+ avoid.
+- **D4** — **No change to `WorkerExecutionZodSchema`** and no new public export. If implementation
+ shows the join cannot be made without a schema field, that is a stop-and-report, not a decision to
+ take in-slice.
+- **D5** — Remove `behavior.otel.stream-consumer` and `behavior.otel.traces` from
+ `SCAFFOLD_RUNTIME_DEFERRED_GATES` and update the assertion in
+ `suite-registry_test.ts:204-215`. The deferral list is the issue's own acceptance surface; leaving
+ it in place while claiming the fix would be a false-done.
+
+## Slices
+
+**S0 — environment precondition: RESOLVED before dispatch, no longer blocking.**
+
+Answered by the orchestrator on 2026-08-12. **`workers-combined` does receive the streams URL.**
+
+`generate-register-background.ts:200-218` emits
+`await .withEnvironment('services__[__http__0', ][Endpoint)` for every entry in the
+background processor's `PluginReferences`, and
+`packages/cli/src/public/features/plugins/install/install-plugin_test.ts:1393-1396` asserts that
+`BackgroundProcessors.workers.PluginReferences` is exactly `['streams', 'workers-api']`. That is the
+env name `stream-url-resolver.ts:154-190` reads. So the wiring exists by design.
+
+**Correction, recorded rather than quietly fixed.** I first read
+`plugins/workers/src/aspire/workers-contribution.ts:55-63` — which calls `addDenoBackground` with no
+reference to `streams` and only `builder.waitFor(combined, api)` — and concluded the env was missing
+and the slice needed an extra Aspire slice. That was **wrong**: `PluginReferences` is not derived
+from the contribution file at all. It is reconciled from the plugin manifest's
+`.withDependencies({ streams: streamsPlugin })` (`plugins/workers/src/public/mod.ts:61`),
+independently of install order, per the test above. Checking the mechanism instead of trusting the
+first plausible file is what kept an unnecessary slice out of this plan.
+
+**Residual, for the implementer to confirm in the live run only:** the generated code guards with
+`if (][Endpoint)`, so the env is silently omitted if the streams resource exposes no `http`
+endpoint at wiring time. That is a runtime observation for S3, not a design unknown.
+
+**S1 — hook installation + trace-context join** (D1, D2, D3) with unit tests.
+
+**S2 — un-defer the two OTEL gates** (D5) and make `suite-registry_test.ts` assert the new state.
+
+**Amended after PLAN-EVAL (finding F2 — verified).** This plan originally named only
+`suite-registry_test.ts:204-215`. There is a **second** test that also pins the deferral and must be
+updated in the *same commit*: `suite-registry_test.ts:209-234`,
+`'runtime suites pin the exact #1398 OTEL deferral without widening it'`, which asserts
+`SCAFFOLD_RUNTIME_DEFERRED_GATES` equals the exact two-entry list (`:210-221`) **and** that neither
+runtime tier executes a deferred gate (`:223-233`). Both tiers reference the constant
+(`capability-suites.ts:211,218`). Emptying the constant without rewriting this test leaves S2 red.
+Confirmed by reading both tests directly.
+
+**S3 — live runtime evidence**: one `deno task e2e:cli run scaffold.runtime --cleanup --format pretty`
+run, with the two previously-deferred gates passing, plus the Aspire trace showing producer →
+durable stream → SSE consumer in one trace.
+
+## Tests
+
+1. **Unit — the join.** Mutation hook publishes under a stored `traceparent`; assert the publish
+ span's trace id equals that traceparent's trace id. Today's suite
+ (`packages/plugin-workers-core/tests/streams/workers-streams_test.ts:9-104`) has **no traceparent
+ assertion** — this is the gap that let the defect ship.
+2. **Unit — installation.** The background runtime path installs a mutation hook; a test that fails
+ if `startCombinedProcess` stops installing it. **There is currently no such test, which is
+ precisely why `workers-combined` shipped without the hook.**
+3. **Unit — pre-span publication.** A mutation emitted *before* any ambient `job.execute` span still
+ produces a publish span on the stored trace id (pins D3 against the `create()` case).
+4. **E2E** — the two un-deferred gates, live.
+
+## Gates
+
+Scoped wrappers over `plugins/workers`, `packages/plugin-workers-core`, `packages/cli`; plus
+`deno task quality:gate` (mandatory — `packages/**`/`plugins/**` slice) and
+`deno task e2e:cli run scaffold.runtime --cleanup --format pretty` for S3. The E2E gate is expensive
+and **serialised across this lane** — it does not run concurrently with any other slice's run.
+
+## Risks
+
+- **R1 (blocking, S0)** — streams env may not reach `workers-combined`. Resolved before code.
+- **R2** — publishing ~4 records per execution against a 256-event / 1 MiB bounded buffer
+ (`producer-contract-v1.ts:47-55`) raises volume on busy workers. Not a correctness risk (drops are
+ metered and settled), but worth a recorded note; #1405's reason taxonomy is what makes any such
+ drop legible, which is why it lands first.
+- **R3** — the two undeclared `@netscript/plugin-streams-core` imports in
+ `packages/plugin-workers-core/deno.json` and `plugins/triggers/deno.json` are real but
+ **out of scope** here. If `publish:dry-run` flags them, they get their own issue rather than
+ being absorbed into this PR.
+
+## Acceptance mapping (#1398)
+
+| Box | Satisfied by | Evidence |
+| --- | --- | --- |
+| Completed execution results in a published record | D1 + D2 | unit test 2 + S3 live run |
+| Record carries an identifier joining it to `job.execute` | D3 | unit tests 1 and 3 + TC-14 in S3 |
+| Live subscription opened before trigger observes it within bounded time | D5 | `behavior.otel.stream-consumer` passing live (`consume-flow-b-stream.ts:203-230`) |
+| A test asserts the join | tests 1–3 + un-deferred E2E | red-on-regression demonstrated |
diff --git a/.llm/runs/release-0.0.6-features--orchestration/retrospective.md b/.llm/runs/release-0.0.6-features--orchestration/retrospective.md
new file mode 100644
index 0000000000..3874acbdad
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/retrospective.md
@@ -0,0 +1,105 @@
+# Retrospective — 0.0.6 runtime / public-surface lane
+
+Factual account of one topical milestone lane, 2026-08-12. Both owned issues landed on `main`:
+**#1405 → `8ff1bcb8f`**, **#1398 → `d7e2b67b2`**.
+
+Per the brief: this records what the run's own evidence supports. Where a lesson is a candidate
+rather than a demonstrated rule, it says so. **No policy is promoted from a single occurrence.**
+
+## What the lane produced
+
+| Issue | PR | Merge | Evidence that closed it |
+| --- | --- | --- | --- |
+| #1405 durable producer rejection taxonomy | #1528 | `8ff1bcb8f` | 4 negative tests, each pinned to its own reason; full CI green; 7-check pre-merge gate |
+| #1398 job executions to the durable stream | #1536 | `d7e2b67b2` | Two formerly-deferred OTEL gates un-deferred and **passing live by name on both CI runtime tiers**; 7-check pre-merge gate |
+
+Two follow-ups filed from inside the run: **#1542** (`quality:gate` root coverage) and **#1543**
+(undeclared `plugin-streams-core` imports).
+
+## The one thing most worth carrying forward
+
+**#1398's definition of done was made mechanical instead of rhetorical, and that decision did the
+work.** The issue's acceptance included "a live subscription observes the execution record within a
+bounded time" — a criterion trivially satisfiable with a confident sentence. The plan instead bound
+it to two gates the repo had **already deferred against this very issue**
+(`SCAFFOLD_RUNTIME_DEFERRED_GATES`, reason: *"workers-combined does not install the stream mutation
+hook"*). Done then meant: remove the deferral, and have the gates pass.
+
+That reframing is what made every later step falsifiable. It is also what made two red local runs
+*informative* rather than negotiable — there was nothing to argue about, the gates either ran or
+they did not.
+
+**[observed, single run]** — one instance. The candidate rule is: *when an issue's acceptance is
+observational, look for an existing deferred/skipped gate that encodes it before writing a new
+criterion.* It worked once here; it is not yet a rule.
+
+## Layered review caught what each layer above it missed
+
+Concretely, on the same defect class — stale pins of the #1398 deferral:
+
+| Layer | Found |
+| --- | --- |
+| Orchestrator plan | `suite-registry_test.ts:204-215` |
+| PLAN-EVAL (MiniMax M3) | a **second** pin at `:209-234` |
+| Implementer (Codex Sol medium) | a **third** in `suite-runner_test.ts`, by running full package tests rather than only the tests it was pointed at |
+
+PLAN-EVAL also caught **F1**: the trace-context join was implicit in the plan and would have failed
+*silently* — `StreamsTracerPort.startSpan` takes no parent-context argument, so without an explicit
+`context.with(...)` wrapper the hook looks correct and fails TC-14 on the pre-span `create()` record.
+The final Qwen IMPL-EVAL then traced that same mechanism end-to-end independently.
+
+**[observed]** Each layer found something the previous one could not. This is the strongest evidence
+in the run for the existing generator ≠ evaluator invariant, and it cost two evaluator sessions.
+
+## Verification discipline that changed an outcome
+
+- **Negative-case proof.** For #1405 the orchestrator reverted both fixes (29/34 → 5 failures) and
+ the IMPL-EVAL reverted each **individually**, showing each test fails for its *own* reason. The
+ aggregate check would have passed a suite where all four tests rode on one mechanism.
+ Acceptance box 4 asks for the per-reason property; only the individual reverts demonstrate it.
+- **Reading logs, not summaries.** Both OTEL gates were confirmed **by name** in job logs, with
+ `skipped=0` treated as load-bearing alongside `failed=0`. `gh pr checks` twice reported something
+ materially misleading (all-`skipping` on a draft; a stale `close-gate` red from a job that predated
+ the changes it complained about).
+- **Head discipline.** When #1536's head changed mid-flight, gate evidence was re-read against the
+ new head. The new job ids (`94073971396` / `94073971501`) differ from the pre-sync pair, which is
+ the concrete proof that reusing the earlier verification would have cited a head no longer on the
+ PR.
+
+## Mistakes made by this orchestrator
+
+Recorded because a retrospective that only lists what went well is not evidence of anything.
+
+1. **Wrong default on the IMPL-EVAL waiver** (D-3). The brief said a waiver was "acceptable" for the
+ #1405 class; that was read as a blocked-transport fallback rather than the class default, costing
+ one unnecessary evaluator run. The ruling arrived after #1405 had already merged, so it changed
+ policy, not that outcome.
+2. **A scope inference from the wrong file.** Read `workers-contribution.ts`, saw no `streams`
+ reference on the background resource, and concluded the env was unwired — which would have added
+ an unnecessary Aspire slice. `PluginReferences` is reconciled from the plugin manifest, not that
+ file. Verifying the *mechanism* rather than the first plausible file is what caught it.
+3. **A broken gate command in my own slice brief** — `deno test ` without `--allow-env`. The
+ implementer reported the red with its cause instead of hiding it; the brief was wrong, not the
+ slice.
+4. **A watcher that could not have fired**, twice: one keyed on comment count against a tool that
+ edits in place, and one waiting for a dispatcher that did not exist in the PR's merge ref.
+
+## Candidate rules — not promoted
+
+Each appeared **once**. Recorded so a future run can confirm or falsify, not encoded as doctrine:
+
+- For `pull_request` events, a workflow must exist in the PR's **merge ref**; a newly merged
+ dispatcher cannot fire on an older head. *Check presence before diagnosing a trigger failure.*
+- A local expensive-suite red is not a flake until a clean control run exists elsewhere.
+- Watch a run's status and its terminal marker, never comment count, for tools that update in place.
+- Re-verify the `status:` label set after automated phase transitions, not only manual edits.
+
+## Open items leaving this lane
+
+- **#1542** — `quality:gate` roots omit published packages. Hit independently by three sessions here;
+ both merge records in this lane explicitly rest on **explicit target scans** rather than the repo
+ gate. The most consequential thing this lane found and did not fix.
+- **#1543** — undeclared `plugin-streams-core` imports, filed as **unverified**: nobody checked
+ whether `deno publish` rejects them, and the issue makes that evidence its first acceptance box
+ rather than asserting a defect.
+- Canary and stable cut are **root's**, not this lane's. Nothing here was published.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/codex-thread-ids.md b/.llm/runs/release-0.0.6-features--orchestration/slices/codex-thread-ids.md
new file mode 100644
index 0000000000..eceedf2d35
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/codex-thread-ids.md
@@ -0,0 +1,16 @@
+# ns006-1398 — Codex implementation thread
+- **Thread / session id:** `019ff4ff-a633-7062-ae9c-21930930b5d6`
+- **Rollout:** `/home/codex/.codex/sessions/2026/08/12/rollout-2026-08-12T10-03-42-019ff4ff-a633-7062-ae9c-21930930b5d6.jsonl`
+- **Worktree:** `/home/codex/repos/ns006-1398`
+- **Branch:** `fix/1398-publish-job-executions-to-durable-stream` @ `01aa12b67` (NO upstream by design).
+- **Push rule:** explicit refspec only — `git push origin HEAD:refs/heads/fix/1398-publish-job-executions-to-durable-stream`.
+- **Requested route:** provider=openai · model=gpt-5.6-sol · effort=medium
+- **Observed route:** provider=openai · model=gpt-5.6-sol · effort=medium
+- **Route verdict:** matched
+- **Runtime:** approval=never · sandbox=dangerFullAccess
+- **Brief (staged):** `/home/codex/ns006-1398-brief.md`
+## Steering (same thread — never a second send-message-v2 at this worktree)
+```bash
+codex exec resume 019ff4ff-a633-7062-ae9c-21930930b5d6 -- ""
+```
+_Written by `.llm/tools/agentic/codex/launch-codex-slice.ts`._
\ No newline at end of file
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/evaluate-1398.md b/.llm/runs/release-0.0.6-features--orchestration/slices/evaluate-1398.md
new file mode 100644
index 0000000000..c68d7eccd6
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/evaluate-1398.md
@@ -0,0 +1,105 @@
+# IMPL-EVAL — #1398 / PR #1536 (verbatim verdict)
+
+| Field | Value |
+| --- | --- |
+| Phase | IMPL-EVAL |
+| Subject | head `f7d503fee` vs trusted base `281ab76887`, PR #1536, closes #1398 |
+| Evaluator | `openrouter/qwen/qwen3.8-max` via OpenHands |
+| Route | **automatic phase dispatcher** (`openhands-phase-eval.yml`), triggered by the `status:impl-eval` label re-entry with the one-shot `eval:model:qwen` override |
+| Session | separate from generator and supervisor; no manual dispatch, no local evaluator |
+| Run | `31584188459`, attempt 1, `conclusion: success` |
+| Verdict | **PASS** (`verdict_source: summary-file`) |
+
+Verbatim below, as posted to PR #1536.
+
+---
+
+
+
+## OpenHands Agent — Completed
+
+OPENHANDS_VERDICT: PASS
+
+Model: `openrouter/qwen/qwen3.8-max`
+Provider: `OPENROUTER`
+Job status: success
+Verdict source: summary-file
+Verdict comment: https://github.com/rickylabs/netscript/pull/1536#issuecomment-5265412502
+Summary source: agent
+Housekeeping: ack=success trace=success commit-artifacts=skipped replies=skipped draft-pr=skipped
+Trace: .llm/tmp/run/openhands/pr-1536/run-31584188459-1 (in the Actions artifact)
+Artifact: openhands-agent-31584188459-1
+
+OPENHANDS_VERDICT: PASS
+
+# IMPL-EVAL — PR #1536 (fix(workers): publish job executions to the durable stream on the job.execute trace)
+
+**Subject:** head `f7d503fee1e312335d432cfee8b9a870ddaccbb4` vs trusted base `281ab76887`, PR #1536 closes #1398.
+**Protocol:** `.llm/harness/evaluator/protocol.md` + `verdict-definitions.md`, read-only.
+
+## Summary
+
+The implementation satisfies the approved plan (S1/S2/S3, D1–D5), and every static gate and
+local runtime guard passes. The decisive live runtime evidence — the two formerly deferred OTEL
+gates `behavior.otel.stream-consumer` and `behavior.otel.traces` — now passes green **in CI at
+this exact head** on both runtime tiers (postgres and sqlite), superseding the locally red S3
+attempt that died on a transient dependency `fetch failed` before those gates could run. The
+only red check (`close-gate`) is a merge-time bookkeeping step, not an implementation defect.
+
+## Changes (verified against plan D1–D5)
+
+- `packages/plugin-workers-core/src/streams/producer.ts` — `createStreamMutationHook` wraps every
+ publish (`upsert`/`delete`) in `withContext(extractContext({traceparent, tracestate}), …)` (D3,
+ including the pre-span `create()` trap); `WorkerExecutionRecord` type acknowledges the
+ already-stored W3C fields (no schema change, D4 honored).
+- `plugins/workers/bin/runtime.ts` — hook installed in `startWorkerProcess` and
+ `startCombinedProcess`; scheduler-only correctly untouched (D1).
+- `plugins/workers/tests/runtime/background-stream-hook_test.ts` — new installation guard (D1).
+- `packages/plugin-workers-core/tests/streams/workers-streams_test.ts` — stored-context and
+ pre-span trace-id join tests (required tests 1 & 3).
+- `packages/cli/e2e/**` — both OTEL gates restored into `RUNTIME_GATES`, deferral constant
+ emptied, both pinned tests rewritten (D5, F2 honored; third stale pin in `suite-runner_test.ts`
+ recorded as drift).
+- `.llm/runs/.../slices/worklog-1398.md` — design checkpoint, per-slice gates, negative-guard
+ evidence, honest S3 red record.
+
+## Validation
+
+- PLAN-EVAL: PASS (MiniMax M3, separate session) with F1/F2 folded into plan; run-dir artifacts
+ confirmed on `chore/release-0.0.6-features-orchestration`.
+- Re-run at head: `deno task --cwd packages/plugin-workers-core test` → 27/27 passed;
+ background-hook install test → 1/1 passed.
+- CI at head: `scaffold-runtime (postgres)` → `Summary: passed=88 failed=0 skipped=0` with both
+ restored OTEL gates executing; `scaffold-runtime-sqlite` → `passed=83 failed=0 skipped=0`;
+ `check-test`, `quality`, `code-quality`, `deps-report`, `surface-diff` all PASS.
+- Diff hygiene: no new `as unknown as` / `deno-lint-ignore` / `@ts-ignore` / `any` (excluding
+ run artifacts); no `deno.lock` churn; no new public exports.
+- Context-join mechanism verified end-to-end: hook → `producer.upsert` → `DurableStreamProducer`
+ `#startPublish` → `instrumentation.startPublish` (`tracer.startSpan` on ambient context), so
+ `withContext` makes the publish span carry the stored trace id; pinned by both new unit tests.
+
+## Responses to review/issue context
+
+- Close-gate failure (`check-close-gate.ts`, run 31584138064): issue #1398's four acceptance
+ boxes and the PR DoD's final three boxes are unticked. Root cause per the gate's own output:
+ the acceptance-evidence mirror only applies when `status:ready-merge` is present ("Mirror
+ skipped because live PR labels do not include status:ready-merge"), and the PR body's
+ `acceptance-evidence` block already maps all four boxes. This is the same mechanism by which
+ merged sibling PR #1528 was closed (pre-merge gate record: boxes "ticked by the evidence mirror
+ on `status:ready-merge`"). Remediable by the orchestrator at merge time.
+- S3 local red (transient `fetch failed` in `runtime.flow-b-fixture`) is superseded by the green
+ CI one-pass runs at the same head; the worklog's honest red record stands as process evidence.
+
+## Remaining risks
+
+- Advisory: until the orchestrator applies `status:ready-merge` and the mirror ticks issue #1398,
+ `close-gate` remains red; the PR body DoD line about the one-pass `scaffold.runtime` should be
+ re-evidenced with the CI run URLs when ticking.
+- Advisory: `quality:gate` configured roots still omit `packages/plugin-workers-core`/`cli/e2e`;
+ covered here by the explicit scans the worklog records (gate-coverage gap, pre-existing).
+- Undeclared `@netscript/plugin-streams-core` imports in two `deno.json` files remain tracked out
+ of scope per plan/PLAN-EVAL F5.
+
+OPENHANDS_VERDICT: PASS
+
+Run: https://github.com/rickylabs/netscript/actions/runs/31584188459
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/evaluate-1405.md b/.llm/runs/release-0.0.6-features--orchestration/slices/evaluate-1405.md
new file mode 100644
index 0000000000..5675d416cd
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/evaluate-1405.md
@@ -0,0 +1,45 @@
+# IMPL-EVAL — #1405 / PR #1528 (verbatim verdict)
+
+| Field | Value |
+| --- | --- |
+| Phase | IMPL-EVAL |
+| Subject | commit `c491c6989`, PR #1528, closes #1405 |
+| Evaluator | DeepSeek V4 Flash 0731 · max, `claude-evaluator-deepseek-v4-flash-0731` preset |
+| Session | fresh, separate from generator and supervisor; worktree `/home/codex/repos/ns006-1405-impleval` @ `c491c6989` |
+| Route reason | small-impl evaluator lane; local fallback per `drift.md` D-2 (#1524 still open) |
+| Duration | 642,836 ms · 5,226 stream events · `is_error: False` |
+| Raw stream | `impl-eval-1405-raw.md` |
+
+Verbatim below; nothing edited.
+
+---
+
+**[PHASE: IMPL-EVAL] [VERDICT: PASS]**
+
+Issue #1405's two reason misdescriptions are fixed correctly, behaviour (accepted/rejected/delivered + their `status` classification) is fully preserved, and all four added tests independently guard their own fix.
+
+### Verified
+- **Package tests 33/33 green** — `deno task --cwd packages/plugin-streams-core test` → `ok | 33 passed | 0 failed` (includes all 4 new tests).
+- **Scoped check** — `run-deno-check.ts --root packages/plugin-streams-core --ext ts,tsx` → 43 files, 1 batch, 0 failures.
+- **`deno task quality:gate`** exits 0 — but its configured scan/doctrine roots **omit** `packages/plugin-streams-core` (deno.json:156 `arch:check` root list). I ran the compensating target gates independently: target quality scan → `findings=[]`, `allowCount=0`; target doctrine audit → `FAIL=0 WARN=1 INFO=1` (supervisor 515 lines vs 500 cap — advisory, pre-existing baseline 497, recorded in drift).
+- **Revert-A isolation** — reverting only the `#closing`/`#writeRejectionReason` change makes only "close drain rejects a concurrent write as producer-stopping" (test 1) fail; tests 2,3,4 pass.
+- **Revert-B isolation** — reverting only the `#failActive` reason selection (+ connect-guard split) makes only "first append refusal …" (test 2) and "non-retryable reconnect failure …" (test 4) fail; test 1 and the retry-exhaustion guard (test 3) pass.
+- **Tree restored** — `git status` porcelain empty after reverts; no diff remaining.
+- **Behaviour preservation** — the only functional deltas in the diff are reason strings (`#failActive` ternary, `#writeRejectionReason` arms) plus a behavior-neutral connect-guard refactor (supervisor.ts:308-315 — old `!isRetryable || attempt===max` split into two identical conditional pairs, same calls/returns).
+
+### Findings
+1. **C2 (advisory) Dead `?? 'producer-failed'` fallback** — `create-durable-stream.ts:132,160`. `writeRejectionReason()` returns `undefined` only when `#accepted === true` (supervisor.ts:483-484); both calls are behind `if (this.closed)` where `closed === !#accepted`. `#accepted` is only ever set `false` (stop/close/fail) and never back to `true`, so the `??` can never fire — not even via a TOCTOU (it cannot flip false→true). Harmless, unreachable defensive code; not blocking. Suggested cleanup: drop the `?? 'producer-failed'` and rely on the switch's `default` returning `producer-failed` when not closing.
+2. **C4 (advisory) Reason-vocabulary doc** — the new `transport-refused` is documented only via JSDoc on the union member (producer-contract-v1.ts:86-87). `README.md` describes outcome `status`es generically (README.md:93) and mentions no reason list, so no consumer doc is stale. Additive union member, check passes 43/43, no exhaustive switch exists on `StreamWriteUnknownReasonV1` anywhere in the repo (grep-confirmed; only producer + tests reference it). No blocking doc gap.
+3. **C4 (advisory, recorded)** — `quality:gate`/`arch:check` do not cover this package; gate-coverage gap already logged in `drift.md`. The `research-1405.md` cited in the brief is absent from the run dir (only `worklog.md`/`drift.md` exist); the worklog carries the equivalent analysis. Both are run-artifact issues external to the committed change, not code defects.
+
+### Acceptance box check (#1405)
+- Box 1 (close-drain reason names closing state, ≠ `producer-failed`): **satisfied** — `#closing` set in `close()` (supervisor.ts:222), default arm returns `producer-stopping` for non-terminal states during drain (supervisor.ts:494); test asserts `producer-stopping` and `assertNotEquals producer-failed`.
+- Box 2 (non-retryable append on attempt 1 names refusal, ≠ `retry-exhausted`): **satisfied** — `#failActive` returns `transport-refused` for `!isRetryable(failure)` (supervisor.ts:430-434); test asserts `transport-refused`, `assertNotEquals retry-exhausted`, `appendCalls === 1`.
+- Box 3 (genuine exhaustion keeps its own reason): **satisfied** — retryable-at-bound still yields `retry-exhausted` (isRetryable true ⇒ not `transport-refused`); test 3 asserts it and `assertNotEquals transport-refused`.
+- Box 4 (tests assert each reason for its own state): **satisfied** — one-at-a-time reverts (above) prove test 1 breaks only under fix-A revert and tests 2/4 only under fix-B revert, i.e. they do not collapse on one mechanism.
+- Box 5 (no change to accepted/rejected/delivered, reasons only): **satisfied** — all deltas are `status`-preserving reason strings; no admission gate, settlement `status`, or retry count changed.
+
+### Next
+- Merge is clear. Optionally (non-blocking): remove the unreachable `?? 'producer-failed'` fallback and record the supervisor 515-line split as debt if reopening it is ever scoped. Owner: R. Chautems / slice owner on #1405 for the fallback cleanup; no blocking action required for merge.
+
+Sources for the behavioural claims above: `durable-stream-producer-supervisor.ts` (lines 64-65, 221-222, 308-315, 356-363, 423-438, 482-496), `durable-stream-producer-support.ts:90-92` (`isRetryable` = retryable|stale-epoch), `create-durable-stream.ts:131-133,159-161`, `producer-contract-v1.ts:83-89`.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/impl-eval-1398-prompt.md b/.llm/runs/release-0.0.6-features--orchestration/slices/impl-eval-1398-prompt.md
new file mode 100644
index 0000000000..37da53bafe
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/impl-eval-1398-prompt.md
@@ -0,0 +1,130 @@
+use harness
+
+# IMPL-EVAL — NetScript issue #1398 (PR #1536)
+
+You are a **formal IMPL-EVAL evaluator** in a fresh session. You did not write this code, did not
+supervise it, and are not its reviewer. Try to **break** the claim that it is correct and complete,
+then return a verdict.
+
+Worktree: `/home/codex/repos/ns006-1398-impleval` (detached). **Read-only** — do not edit, commit,
+or push. You may run read-only commands and tests.
+
+## SKILL
+
+- `netscript-harness` — read `.llm/harness/evaluator/protocol.md` and `verdict-definitions.md`.
+- `netscript-doctrine` — plugins are Archetype 5 / thin; `-core` owns convention-bearing primitives.
+- `netscript-cli` — the `e2e:cli` suite surface.
+
+## The change
+
+Three commits on `fix/1398-publish-job-executions-to-durable-stream`. Job executions were never
+published to the durable job stream because the workers **API service** installs the execution-state
+mutation hook (`plugins/workers/services/src/main.ts:67`) but the **background** entrypoints that
+generated projects actually run never did (`plugins/workers/bin/runtime.ts`). Read the live issue
+#1398 for its four acceptance boxes.
+
+Context you must read first, because the plan was already adversarially reviewed and you should not
+repeat that work:
+
+- `.llm/runs/release-0.0.6-features--orchestration/plan.md` — the approved plan, D1–D5.
+- `.llm/runs/release-0.0.6-features--orchestration/plan-eval.md` — the PLAN-EVAL verdict.
+- `.llm/runs/release-0.0.6-features--orchestration/slices/worklog-1398.md` — the implementer's own
+ evidence, including **two red live runs**.
+
+These files live on the orchestration branch `chore/release-0.0.6-features-orchestration`, not on the
+PR branch. If they are not present in your worktree, read them with
+`git show chore/release-0.0.6-features-orchestration:`.
+
+## What to attack, in priority order
+
+1. **Does the trace join actually hold at runtime, not just in the unit test?** D3 wraps publication
+ in `withContext(extractContext({traceparent, tracestate}), …)`
+ (`packages/plugin-workers-core/src/streams/producer.ts:113-128`). The unit tests assert the
+ publish span's trace id. Check the assumption underneath: does `extractContext` return a usable
+ context for a **remote** traceparent, and does `withContext` keep it active across the `await`
+ boundary inside `producer.upsert`'s async path? If the context is lost after the first `await`,
+ the unit test could pass while real publication carries the wrong trace id. **This is the highest
+ value thing you can check.**
+2. **Is the hook installed exactly where execution state is mutated?** It is installed in
+ `startWorkerProcess` and `startCombinedProcess` but **not** `startSchedulerProcess`. Verify the
+ scheduler genuinely does not mutate execution state. If it does, executions from scheduled jobs
+ are still invisible and the issue is only half fixed.
+3. **Double-install and ordering.** `setMutationHook` is destructive (single field). In a combined
+ process, can the hook be installed and then replaced or cleared by anything later — for example by
+ `registerProjectJobs` or the API service sharing a runtime? Installation happens *before*
+ `registerProjectJobs`; confirm nothing in that path resets it.
+4. **Volume and buffer pressure.** D2 publishes every mutation, ~4 per execution on the same key,
+ against a 256-event / 1 MiB bounded buffer. Look for a realistic path where a busy worker drops
+ execution records, and say whether drops are still metered and settled.
+5. **The un-deferral is complete and honest.** `SCAFFOLD_RUNTIME_DEFERRED_GATES` is now empty and
+ both OTEL gates were added to `RUNTIME_GATES`. Confirm **all** stale pins were updated — the
+ implementer found a third in `suite-runner_test.ts` that the plan and PLAN-EVAL both missed.
+ Search for a fourth. Confirm the empty-array type change
+ (`readonly DeferredGate[]` instead of `as const satisfies`) did not weaken any assertion.
+6. **Test quality.** Revert each fix **individually** and confirm each test fails for **its own**
+ reason: (a) remove the `withContext` wrapper → trace tests fail; (b) remove the
+ `startCombinedProcess` hook install → installation test fails; (c) remove the two gates from
+ `RUNTIME_GATES` → suite-registry tests fail. If a test passes while its own defect is
+ reintroduced, that is blocking. Restore the tree and confirm it is clean.
+7. **Scope.** D4 forbids any `WorkerExecutionZodSchema` change; confirm `streams/schema.ts` is
+ untouched. Confirm no #1405 surface, no dependency/export-map change, no new lint suppression or
+ unsafe cast.
+
+## On the live gate — read carefully
+
+Two local `scaffold.runtime` runs went red **before** reaching `behavior.otel.stream-consumer` and
+`behavior.otel.traces`: run 1 at `runtime.flow-b-fixture` (`generate plugins: fetch failed`), run 2
+at `runtime.wait.triggers-api` (unhealthy after 120 s). Neither gave the restored gates a verdict.
+The orchestrator escalated to CI, where the same suite runs.
+
+**Do not treat CI's result as your own finding, and do not re-run `scaffold.runtime` yourself** — it
+is expensive and serialised across this lane. Your job is the code and the tests. State explicitly in
+your verdict whether, in your judgement, the change could plausibly cause a `triggers-api` health
+timeout; #1398 touches workers and the e2e suite definitions, not triggers, but say what the code
+supports rather than what is convenient.
+
+## Gates to run yourself
+
+```bash
+deno task --cwd packages/plugin-workers-core test
+deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root plugins/workers --ext ts,tsx
+deno task quality:gate
+```
+
+Use the package-declared `deno task --cwd test`; a bare `deno test ` omits `--allow-env`
+and exits 1 on `NotCapable`. Note `quality:gate`'s configured roots do **not** cover
+`packages/plugin-workers-core` or `packages/cli/e2e` — run explicit target scans for those and say so.
+
+## Output
+
+Return your verdict **as text in your final message** — do not write files.
+
+```
+**[PHASE: IMPL-EVAL] [VERDICT: PASS|FAIL_FIX|FAIL_RESCOPE|FAIL_DEBT]**
+
+
+
+### Verified
+- —
+
+### Findings
+1. **C1 ** — what is wrong, where, the fix, blocking or advisory.
+
+### Acceptance box check (#1398)
+- Box 1 …: satisfied / not satisfied / not-yet-live-verified — evidence
+
+### Live-gate judgement
+- Could this change plausibly cause the triggers-api timeout? — reasoning.
+
+### Next
+-
+```
+
+Rules: every finding cites `path:line`. State the verdict token exactly. Distinguish blocking from
+advisory. Write "could not verify" rather than guessing. **No praise, no quality adjectives, no
+overall-assessment paragraph** — findings and evidence only. An empty `Findings` list is fine if the
+code is sound, but only after genuinely attempting 1–7.
+
+Note on acceptance box 3 ("a live subscription … within a bounded time"): if the live gate has not
+produced a verdict, the correct answer is **not-yet-live-verified**, not "satisfied". Do not tick it
+from unit tests.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/impl-eval-1405-prompt.md b/.llm/runs/release-0.0.6-features--orchestration/slices/impl-eval-1405-prompt.md
new file mode 100644
index 0000000000..a0dd70530e
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/impl-eval-1405-prompt.md
@@ -0,0 +1,101 @@
+use harness
+
+# IMPL-EVAL — NetScript issue #1405 (PR #1528)
+
+You are a **formal IMPL-EVAL evaluator** in a fresh session. You did not write this code, you did not
+supervise it, and you are not its reviewer. Your job is to try to **break** the claim that this
+change is correct and complete, then return a verdict.
+
+Worktree: `/home/codex/repos/ns006-1405-impleval` (detached at `c491c6989`). **Read-only** — do not
+edit, commit, or push. You may run read-only commands and tests.
+
+## SKILL
+
+- `netscript-harness` — read `.llm/harness/evaluator/protocol.md` and
+ `.llm/harness/evaluator/verdict-definitions.md`.
+- `netscript-doctrine` — `packages/plugin-streams-core` is framework code; `domain/` contracts are
+ published surface.
+
+## The change under evaluation
+
+Commit `c491c6989`, "fix(streams): distinguish producer refusal reasons". It closes #1405, whose
+complaint is that two settled **reason strings** misdescribe the state that produced them:
+
+1. a write rejected during the graceful close-drain reported `producer-failed` though the producer
+ was healthy and closing;
+2. a non-retryable append failure on attempt 1 reported `retry-exhausted` though nothing was
+ exhausted.
+
+Read the live issue #1405 for its five acceptance boxes. Read
+`.llm/runs/release-0.0.6-features--orchestration/slices/research-1405.md` for the line-cited
+analysis the change was built from, and `slices/1405/worklog.md` for the implementer's own evidence.
+
+## What to attack
+
+1. **Behaviour preservation — the load-bearing claim.** The issue requires *no change to which
+ writes are accepted, rejected, cancelled or delivered — reasons only*. Verify this. In particular
+ `#writeRejectionReason()` gained a `case 'failed'` arm and a `#closing`-aware `default`; prove or
+ disprove that no state now yields a different **classification** (not just a different string)
+ than before. `create-durable-stream.ts` deleted its local `stateRejection()` and now delegates to
+ `supervisor.writeRejectionReason() ?? 'producer-failed'` — is that `??` fallback reachable, and
+ if so with what result?
+2. **Is `producer-stopping` reachable when it should not be?** `#closing` is set in `close()` and
+ never cleared. Find any path where a producer that is *not* closing reports `producer-stopping`,
+ or where a genuinely failed producer is now masked as merely stopping. A failure masked as a
+ graceful close would be a **worse** defect than the one being fixed — this is the highest-value
+ thing you can look for.
+3. **`transport-refused` correctness.** `#failActive` now selects on `isRetryable(failure)`. Check
+ every call site (the connect guard was split into two). Is there a failure kind where the new
+ branch mislabels exhaustion as refusal, or vice versa? Check what `isRetryable` actually
+ considers retryable, including `stale-epoch`.
+4. **Public surface.** `transport-refused` is a new member of the exported
+ `StreamWriteUnknownReasonV1`. Is anything in the repo switching exhaustively on that union that
+ now fails to compile or silently falls through? Is the addition documented?
+5. **Test quality — do the guards fire?** Four negative tests were added. The orchestrator already
+ demonstrated that reverting both fixes turns the suite red (29 passed / 5 failed). Go further:
+ revert them **one at a time** and confirm each test fails for *its own* reason rather than all
+ four depending on one mechanism. If a test passes while its own defect is reintroduced, that is a
+ blocking finding. Restore the tree afterwards and confirm it is clean.
+6. **Anything the implementer or reviewer missed.** Both are recorded as having found nothing
+ blocking. Assume they are wrong somewhere and look.
+
+## Gates to run yourself
+
+```bash
+deno task --cwd packages/plugin-streams-core test
+deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/plugin-streams-core --ext ts,tsx
+deno task quality:gate
+```
+
+Note: the bare `deno test packages/plugin-streams-core` exits 1 on `NotCapable` permission errors
+because it omits `--allow-env`; that is a defect in the original brief, already recorded, not a
+finding. Use the package-declared task.
+
+## Output
+
+Return your verdict **as text in your final message** — do not write files. Structure:
+
+```
+**[PHASE: IMPL-EVAL] [VERDICT: PASS|FAIL_FIX|FAIL_RESCOPE|FAIL_DEBT]**
+
+
+
+### Verified
+- —
+
+### Findings
+1. **C1 ** — what is wrong, where, the concrete fix, and whether it blocks merge.
+...
+
+### Acceptance box check (#1405)
+- Box 1 …: satisfied / not satisfied — evidence
+
+### Next
+-
+```
+
+Rules: every finding cites `path:line`. State the verdict token exactly. Distinguish blocking from
+advisory. If you cannot verify something, write "could not verify" — do not guess. Do not include
+praise, quality adjectives, or an overall assessment paragraph; findings and evidence only. An empty
+`Findings` list is a fine outcome if the code is sound, but only after you have genuinely tried
+items 1–6.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/implement-1398.md b/.llm/runs/release-0.0.6-features--orchestration/slices/implement-1398.md
new file mode 100644
index 0000000000..b5c99a05be
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/implement-1398.md
@@ -0,0 +1,189 @@
+use harness
+
+# Slice brief — #1398 publish job executions to the durable job stream
+
+You are the implementation agent for this slice. **Codex · GPT-5.6 Sol · medium**
+(`normal_implementation`). The plan has already passed a separate-session PLAN-EVAL; your job is to
+implement it, not to re-decide it.
+
+| Field | Value |
+| --- | --- |
+| Issue | #1398 (`priority:p1`) |
+| Worktree | `/home/codex/repos/ns006-1398` |
+| Branch | `fix/1398-publish-job-executions-to-durable-stream` |
+| Base | `origin/main@01aa12b67` |
+| Run dir | `.llm/runs/release-0.0.6-features--orchestration/` |
+
+**Read these first, in this order:**
+
+1. `.llm/runs/release-0.0.6-features--orchestration/plan.md` — the approved plan. **Authoritative.**
+2. `.llm/runs/release-0.0.6-features--orchestration/plan-eval.md` — the PLAN-EVAL verdict, whose
+ findings F1 and F2 are already folded into the plan.
+3. `.llm/runs/release-0.0.6-features--orchestration/slices/research-1398.md` — line-cited research.
+ **Its "Unverified" section is honest; treat those items as unknown, not as facts.**
+
+## SKILL
+
+- `netscript-doctrine` — plugins are Archetype 5 / thin; convention-bearing primitives belong in
+ `-core`. Read before deciding where code lives.
+- `netscript-harness` — slice/commit trail, drift recording.
+- `netscript-cli` — the `e2e:cli` suite surface and what `scaffold.runtime` actually runs.
+- `aspire` — service graph and trace evidence for the live run.
+- `netscript-tools` — validation wrappers and what counts as gate evidence.
+- `netscript-pr` — draft PR body, closing keyword, phase comments, labels.
+
+## The defect
+
+A triggered job execution completes and emits a `job.execute` span, but **no record for that
+execution is ever published to the durable job stream**. The stream contains only the three startup
+job-definition snapshots. This is a **wiring gap**, not a missing feature — the producer, the mapper,
+the hook, and the execution record's trace fields all already exist.
+
+The workers **API service** installs the stream mutation hook
+(`plugins/workers/services/src/main.ts:67`). The **background** entrypoints that generated projects
+actually run never do (`plugins/workers/bin/runtime.ts:89-107`, `:110-122`, `:125-152`). Generated
+projects run `startCombinedProcess()` as Aspire resource `workers-combined`.
+
+**The repo already admits this.** Two E2E gates are deferred against this issue with that exact
+reason (`packages/cli/e2e/suites/scaffold/capability-suites.ts:24-35`).
+
+## LOCKED decisions — implement, do not re-decide
+
+- **D1** — Install the mutation hook on the background runtimes in `plugins/workers/bin/runtime.ts`,
+ mirroring `plugins/workers/services/src/main.ts:65-75`. Cover `startWorkerProcess` and
+ `startCombinedProcess`; `startSchedulerProcess` only if it owns execution state. Note
+ `KvExecutionState.setMutationHook` is destructive (single `onMutation` field), so a second install
+ replaces the first rather than double-firing — installation must still be deliberate, not
+ incidental.
+- **D2** — Publish **all** execution mutations (created / updated / deleted), not only terminal ones.
+- **D3 — the join, and the trap.** The hook must wrap its publish in the execution's stored trace
+ context:
+ `context.with(extractContext({ traceparent, tracestate }), () => producer.upsert(...))` in
+ `createStreamMutationHook` (`packages/plugin-workers-core/src/streams/producer.ts:108-118`), which
+ today does **no** wrapping.
+ **Why this is not optional:** `StreamsTracerPort.startSpan`
+ (`packages/plugin-streams-core/src/telemetry/instrumentation.ts:92-102`) takes **no**
+ parent-context argument and starts the publish span on the **ambient** context (`:160`), and the
+ header is `formatTraceparent(span.spanContext())` (`:172`). Meanwhile `job.execute` is a child of
+ the stored dispatch traceparent (`plugins/workers/worker/job-dispatcher.ts:43-44` → `:108` →
+ `packages/telemetry/src/application/span.ts:38-43`). So wrapping is what makes the published
+ record's trace id equal the `job.execute` trace id.
+ **The trap:** `executionState.create()` (`job-dispatcher.ts:74-86`) runs *before* the
+ `job.execute` span exists (`:91`). The E2E selector returns the **first** `correlationId` match
+ (`select-flow-b-stream-change.ts:96-105`) and TC-14 then asserts that record's trace id
+ (`:131-153`). Install the hook **without** D3's wrapping and it will look like it works, then fail
+ TC-14 on the `create()` record. Do not skip the wrapping because "the terminal record is inside
+ the span anyway".
+- **D4** — **No change to `WorkerExecutionZodSchema`** and no new public export. The gate asserts the
+ **header** traceparent, not a record field, so no schema field is needed. If you conclude you
+ cannot make the join without a schema change, **stop and report** — do not take that decision
+ in-slice.
+- **D5** — Remove both entries from `SCAFFOLD_RUNTIME_DEFERRED_GATES`
+ (`capability-suites.ts:24-35`) and update **both** tests that pin the deferral, in the same commit:
+ - `packages/cli/e2e/tests/presentation/suite-registry_test.ts:204-205` — flips `false` → `true`.
+ - `packages/cli/e2e/tests/presentation/suite-registry_test.ts:209-234` — *"runtime suites pin the
+ exact #1398 OTEL deferral without widening it"*, which asserts the constant equals the exact
+ two-entry list (`:210-221`) and that neither runtime tier executes a deferred gate (`:223-233`).
+ Both tiers reference the constant (`capability-suites.ts:211,218`).
+
+ Missing the second test is the single most likely way to leave this slice red. It was caught by
+ PLAN-EVAL, not by the plan's first draft.
+
+## Slices
+
+- **S1** — D1 + D2 + D3 with unit tests.
+- **S2** — D5, both tests, same commit.
+- **S3** — live runtime evidence (below).
+
+## Required tests
+
+1. **The join.** The mutation hook publishes under a stored `traceparent`; assert the publish span's
+ trace id equals that traceparent's trace id. Today's suite
+ (`packages/plugin-workers-core/tests/streams/workers-streams_test.ts:9-104`) has **no traceparent
+ assertion** — that gap is why this shipped.
+2. **Installation.** The background runtime path installs a mutation hook; the test must fail if
+ `startCombinedProcess` stops installing it. **No such test exists today, which is exactly why
+ `workers-combined` shipped without the hook.**
+3. **Pre-span publication.** A mutation emitted *before* any ambient `job.execute` span still
+ produces a publish span on the stored trace id. This pins D3 against the `create()` case — the
+ trap above. Without this test, D3 can silently regress.
+
+Each test must fail if its own defect is reintroduced. Assert the positive value **and** that it is
+not the pre-fix value where that distinction is meaningful.
+
+## Gates — deliverables, not hopes
+
+```bash
+deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root plugins/workers --ext ts,tsx
+deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/plugin-workers-core --ext ts,tsx
+deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root plugins/workers --ext ts,tsx
+deno run --allow-read --allow-run .llm/tools/run-deno-fmt.ts --root plugins/workers --ext ts,tsx
+deno task quality:gate
+deno task --cwd packages/plugin-workers-core test
+```
+
+**Use the package-declared `deno task --cwd test`, not a bare `deno test `** — the bare
+form omits `--allow-env` and exits 1 on `NotCapable` permission errors. That was a defect in the
+previous slice brief; it is corrected here rather than repeated.
+
+`deno task quality:gate` is mandatory for a `packages/**`/`plugins/**` slice. Check whether the
+configured quality roots actually cover the packages you touched; if they do not, run an explicit
+scan against your target paths and say so. A new `deno-lint-ignore`, `as unknown as`, `any`, or
+`@ts-ignore` added to green a gate is a review-blocking finding — stop and report instead.
+
+**S3 — live evidence, one pass:**
+
+```bash
+deno task e2e:cli run scaffold.runtime --cleanup --format pretty
+```
+
+Do **not** split this into individual `gates` invocations. The two formerly-deferred gates
+(`behavior.otel.stream-consumer`, `behavior.otel.traces`) must **pass for real** in that run — that
+is what makes this issue's acceptance mechanical instead of asserted. Report the raw exit code and
+any failing suite/test names.
+
+This gate is **expensive and serialised across this lane** — three concurrent `scaffold.runtime`
+runs once produced two failures that were contention, not defects. Confirm no other run of it is
+active before you start, and do not run it more than necessary.
+
+**Runtime-evidence order:** use plugin doctor and Aspire logs/OTEL traces **before** hand probes.
+Prefer the trace as evidence over a hand-rolled curl. **No hand-waved timing** — the bounded-time
+criterion is the gate's own live SSE loop (`consume-flow-b-stream.ts:203-230`), not a stopwatch in a
+comment.
+
+**Known residual to observe (not to fix):** the generated Aspire wiring guards with
+`if (][Endpoint)` (`generate-register-background.ts:200-218`), so `services__streams__http__0` is
+silently omitted if the streams resource exposes no `http` endpoint at wiring time. The env path is
+otherwise confirmed wired (`install-plugin_test.ts:1393-1396`). If the producer throws
+`Missing plugin reference "streams"` at `create-durable-stream.ts:262-272`, that is this residual —
+report it, do not paper over it.
+
+## Out of scope — do not absorb
+
+- Any `packages/plugin-streams-core` reason-string change — that is #1405, landing separately.
+- The undeclared `@netscript/plugin-streams-core` imports in `packages/plugin-workers-core/deno.json`
+ and `plugins/triggers/deno.json`. Real, but they get their own issue. If `publish:dry-run` flags
+ them, **report; do not fix here.**
+- Any `WorkerExecutionZodSchema` change (D4).
+
+## Commit trail
+
+1. Open a **draft PR against `main`** in the same session as your first commit. Title:
+ `fix(workers): publish job executions to the durable stream on the job.execute trace`.
+ Body per `netscript-pr`: `Closes #1398` in `## Scope`, run-dir path, slice checklist, Definition
+ of Done, and a fenced `acceptance-evidence` block mapping each of #1398's four acceptance boxes.
+ Labels `type:fix`, `area:plugins`, `area:telemetry`, `status:impl`, milestone `0.0.6`.
+2. Commit per slice, push by **explicit refspec**
+ (`git push origin HEAD:refs/heads/fix/1398-publish-job-executions-to-durable-stream`), and post a
+ `[PHASE: IMPL]` comment with commit hash and **pasted real gate output**.
+3. Keep your slice worklog current in the same commit.
+
+## Reporting contract
+
+Report: what you changed and where; the exact test names and what each would catch; verbatim gate
+output including the E2E exit code; and **anything you could not do, could not verify, or that
+surprised you**. If a gate goes red, report the red with its output — do not go idle on it and do
+not work around it silently. The previous slice in this lane reported a red caused by a mistake in
+its own brief, and that was the correct behaviour.
+
+You do **not** merge and you do **not** flip the PR to ready. The orchestrator holds merge authority.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/implement-1405.md b/.llm/runs/release-0.0.6-features--orchestration/slices/implement-1405.md
new file mode 100644
index 0000000000..cc5e2cface
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/implement-1405.md
@@ -0,0 +1,150 @@
+use harness
+
+# Slice brief — #1405 durable producer rejection taxonomy
+
+You are the implementation agent for one small, fully specified slice. **Codex · GPT-5.6 Sol ·
+low** (`light_implementation`). Do exactly this slice: do not refactor neighbours, do not rename
+anything not named here, do not touch #1398's surface.
+
+| Field | Value |
+| --- | --- |
+| Issue | #1405 |
+| Worktree | `/home/codex/repos/ns006-1405` |
+| Branch | `fix/1405-durable-producer-rejection-taxonomy` |
+| Base | `origin/main@01aa12b67` |
+| Orchestrator run dir | `.llm/runs/release-0.0.6-features--orchestration/` |
+| Research (read it first) | `.llm/runs/release-0.0.6-features--orchestration/slices/research-1405.md` |
+
+## SKILL
+
+- `netscript-doctrine` — `plugin-streams-core` is a framework package; its `domain/` contracts are
+ published surface. Read before editing `producer-contract-v1.ts`.
+- `netscript-harness` — slice/commit trail discipline, drift recording.
+- `netscript-tools` — the scoped validation wrappers and what counts as gate evidence.
+- `netscript-pr` — draft PR body, closing keyword, phase comments, labels.
+
+## The defect, exactly
+
+Two settled **reason strings** misdescribe the state that produced them. Nothing else is wrong: no
+write is silently lost, no false `delivered` is possible, and every drop path is already metered.
+
+**D1 — close-drain window reports `producer-failed`.**
+`packages/plugin-streams-core/src/application/durable-stream-producer-supervisor.ts:215` sets
+`#accepted = false`, then `:216` calls `#closeGracefully()`, which `await`s `flush()` and the
+connect promise before reaching `this.#transition('stopping', 0)` at `:225`. Throughout that drain
+`#accepted` is false while `#state.state` is still `ready`, so both rejection selectors fall to
+their `default` arm and answer `producer-failed`:
+
+- supervisor `#writeRejectionReason()` — `:467-478`
+- façade `stateRejection()` — `create-durable-stream.ts:251-262`, reached from `upsert` (`:131-133`)
+ and the second write entry point (`:159-160`) because `get closed()` is `!this.#accepted`
+ (supervisor `:109-111`)
+
+`#fail()` was never called and `#state.error` is unset. The producer is healthy and closing.
+
+**D2 — first-attempt refusal reports `retry-exhausted`.** `#failActive()` (`:412-423`) settles every
+non-`aborted` failure as `retry-exhausted` (`:418-420`), across three call sites:
+
+| Call site | Guard | True cause |
+| --- | --- | --- |
+| `:302` | `!isRetryable(connected.failure) \|\| attempt === maxAttempts` | **conflated** |
+| `:346` | `else if (!isRetryable(result.failure))` | **refusal** — reachable on attempt 1 |
+| `:350` | `if (attempt === this.#reconnectPolicy.maxAttempts)` | genuine exhaustion |
+
+## LOCKED decisions — implement these, do not re-decide
+
+1. **D1 reuses the existing `producer-stopping` reason. Do NOT add a `producer-closing` member.**
+ `StreamWriteRejectionReasonV1` (`producer-contract-v1.ts:70-78`) already contains
+ `producer-stopping`, and it already means "shutting down, not accepting". The bug is that the
+ selectors cannot observe the closing intent before the `stopping` transition — fix the
+ *observability*, not the vocabulary. Make the closing intent visible from the moment `close()`
+ is entered (e.g. a private closing flag set alongside `#accepted = false` at `:215`, consulted by
+ `#writeRejectionReason()`), and make the façade's `stateRejection()` agree — the façade must not
+ 独立ly re-derive `producer-failed` from a stale state snapshot. Preferred shape: have the façade
+ ask the supervisor for the reason rather than duplicating the state switch, so the two can never
+ drift apart again. `#stopImmediately()` (`:185-186`) has no such window and must not change.
+
+2. **D2 adds exactly one new member to `StreamWriteUnknownReasonV1`** (`:84-87`), named
+ **`transport-refused`**. This is the only public-surface addition in the slice. Semantics: the
+ transport returned a **non-retryable** failure — the server positively refused — regardless of
+ attempt number. Then:
+ - `#failActive()` selects: `failure.kind === 'aborted'` → `transport-aborted` (unchanged);
+ else `!isRetryable(failure)` → `transport-refused`; else → `retry-exhausted`.
+ - Call site `:302` must stop conflating its two guards, so a non-retryable connect failure on the
+ final attempt is still reported as a refusal, not as exhaustion.
+ - `retry-exhausted` must remain reachable and must still be what `:350` produces.
+
+3. **No behaviour change beyond reason strings.** Which writes are accepted, rejected, cancelled, or
+ delivered is identical before and after. `#fail()` is still called from exactly the paths it is
+ called from today. If you find yourself changing an acceptance decision, stop and report it.
+
+4. **Update the doc comments and the exported surface consistently.** `transport-refused` needs its
+ JSDoc line in the union, and `packages/plugin-streams-core/mod.ts:44-45` already re-exports both
+ unions — verify the export still resolves; do not add new exports.
+
+## Required tests — these are the deliverable, not an afterthought
+
+Acceptance box 4 says a future refactor that collapses these reasons must **fail**. A test that only
+asserts the new string passes trivially if someone later reintroduces the old one on the other path,
+so each test must assert the correct reason **and** explicitly assert it is not the old wrong one.
+
+1. A write issued after `close()` is entered but **before** the drain completes settles
+ `rejected` / `producer-stopping`, asserted **not** `producer-failed`. You must actually hold the
+ drain open (a transport whose `close`/`flush` does not resolve until the test releases it) —
+ a test that races is not evidence.
+2. Append transport returns a **non-retryable** failure on **attempt 1** with `maxAttempts > 1`:
+ settles `delivery-unknown` / `transport-refused`, asserted **not** `retry-exhausted`, and assert
+ the transport was called exactly **once** (proving no retries were exhausted).
+3. Append transport returns **retryable** failures until `maxAttempts`: still settles
+ `delivery-unknown` / `retry-exhausted`.
+4. A non-retryable **connect** failure settles `transport-refused` (covers the `:302` conflation).
+5. Existing behaviour guard stays green:
+ `packages/plugin-streams-core/tests/telemetry/durable-stream-producer-telemetry_test.ts:172-219`
+ already distinguishes rejected / dropped / delivery-unknown. Do not edit it to fit your change;
+ if it goes red, your change altered classification and that is a stop-and-report.
+
+## Gates — turn these green; they are part of the deliverable, not a checklist to hope about
+
+This slice touches `packages/**`, so the scoped wrappers alone are **not** a verdict.
+
+```bash
+deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/plugin-streams-core --ext ts,tsx
+deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root packages/plugin-streams-core --ext ts,tsx
+deno run --allow-read --allow-run .llm/tools/run-deno-fmt.ts --root packages/plugin-streams-core --ext ts,tsx
+deno task quality:gate
+deno task doc:lint --root packages/plugin-streams-core --pretty
+deno test packages/plugin-streams-core
+```
+
+`deno task quality:gate` (= `quality:scan` + `arch:check`) is **mandatory** for a `packages/**`
+slice. A new `// deno-lint-ignore`, `as unknown as`, `any`, or `@ts-ignore` introduced to green a
+wrapper is a review-blocking finding, not a pass — if you think you need one, stop and report.
+
+Do **not** run `deno task e2e:cli` — this slice does not touch scaffold output, and that gate is
+expensive and serialised across the lane.
+
+**Known hazard, pre-empted:** `deno fmt` rewraps long lines and can silently undo a scripted string
+edit. After the format wrapper runs, re-grep for every string you introduced (`transport-refused`,
+`producer-stopping`) and confirm it is still present in the file you put it in.
+
+## Commit trail
+
+1. Open a **draft PR against `main`** in the same session as your first commit. Title:
+ `fix(streams): name the closing and refusal states in producer write reasons`.
+ Body per `netscript-pr` — it MUST carry `Closes #1405` in `## Scope`, the run dir path, the slice
+ checklist, a Definition of Done, and a fenced `acceptance-evidence` block mapping each of the
+ five acceptance boxes of #1405 to its evidence. Labels: `type:fix`, `area:plugins`,
+ `status:impl`, milestone `0.0.6`.
+2. Commit by slice, push, and post an `[PHASE: IMPL]` comment with the commit hash and **pasted real
+ gate output** (exit codes, test counts). No green box without evidence.
+3. Keep `worklog.md` in your slice dir current as part of the same commit.
+
+## Reporting contract
+
+When done, report: the reason strings as implemented, the exact test names and what each would
+catch, verbatim gate output, and **anything you could not do or that surprised you**. If you hit a
+red gate, do not go idle — report the red with its output. A red gate reported is useful; a red gate
+sat on is the failure mode this brief exists to prevent.
+
+You do **not** merge, and you do **not** flip the PR to ready. The orchestrator holds merge
+authority and runs the pre-merge gate.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1525.md b/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1525.md
new file mode 100644
index 0000000000..0ad99d4bcf
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1525.md
@@ -0,0 +1,33 @@
+# Pre-merge gate — PR #1525 (control run, closes nothing)
+
+Run per `.llm/harness/workflow/milestone-run.md`, at head `bdc62b0c1`, 2026-08-12. This PR carries
+**orchestration evidence only**; it never became an implementation umbrella.
+
+| # | Check | Result | Evidence |
+| --- | --- | --- | --- |
+| 1 | `close-gate` green | **PASS** | `close-gate` → `pass`. A result exists. |
+| 2 | Zero unticked boxes on issues the PR closes | **PASS, vacuously — and deliberately so** | The body contains **0** closing keywords, verified by grep. An evidence-only control PR must close nothing; the two owned issues were closed by their own implementation PRs (#1528, #1536). |
+| 3 | No new `deno-lint-ignore` / `as unknown as` / `@ts-ignore`, excluding `.llm/runs/**` | **PASS** | Diff outside `.llm/runs/**` is empty; scanned and clean. |
+| 4 | Named expensive gates `SUCCESS` | **PASS** | `scaffold-runtime (aspire + docker + postgres)` · `scaffold-runtime-sqlite` · `scaffold-static` · `quality` · `check-test` · `surface-diff` · `deps-report` · `close-gate` — all `pass`. |
+| 5 | The single decisive claim, re-verified | **PASS** | Claim: *the lane's recorded evidence matches what actually happened*. Both merges in `cut-trace.md` were captured from `git log origin/main --first-parent` **after** each merge, and both issue states re-read live (`CLOSED`/`COMPLETED`). |
+| 6 | Changed-file audit for `packages/**`/`plugins/**` | **PASS — audited, not assumed** | This is exactly the lane where check 6 earns its place (#1079: a docs slice landed framework source). Explicit grep for `^(packages\|plugins)/` over the full diff → **no matches**. All 20 changed paths are under `.llm/runs/`. |
+| 7 | PR body checklist matches what shipped | **PASS** | All `## Slices` and `## Definition of Done` boxes ticked, each asserted true at edit time by a script that refuses to leave an unticked box under either heading. |
+
+`agentic:review-threads` → recorded below.
+
+## Artifact completeness
+
+Mandatory set present: `supervisor.md`, `plan.md`, `worklog.md`, `context-pack.md`, `drift.md`, plus
+the profile's signature `cut-trace.md` and this lane's `retrospective.md`. Both formal verdicts are
+captured as artifacts (`plan-eval.md`, `slices/evaluate-1405.md`, `slices/evaluate-1398.md`), not
+left only as PR comments.
+
+Run dir is **208 K**. The two raw evaluator JSONL streams (2.4 MB combined) were untracked to
+gitignored `.llm/tmp/` scratch — they were 2.4 MB of a 2.5 MB run dir against a 96 K
+largest-artifact precedent in the 0.0.5 run, which tracks no raw streams at all. Nothing was deleted;
+the files remain on disk and their substance is verbatim in the distilled verdicts with run id,
+duration, event count, and `is_error`.
+
+## Verdict
+
+**Cleared to merge.** Evidence-only, closes nothing, no framework source, all seven checks pass.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1528.md b/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1528.md
new file mode 100644
index 0000000000..c0d5647b04
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1528.md
@@ -0,0 +1,37 @@
+# Pre-merge gate — PR #1528 (closes #1405)
+
+Run per `.llm/harness/workflow/milestone-run.md` § The pre-merge gate, by the orchestrator holding
+merge authority. Evaluated at head `c491c6989`, 2026-08-12.
+
+| # | Check | Result | Evidence |
+| --- | --- | --- | --- |
+| 1 | `close-gate` result is green | **PASS** | `close-gate` → `SUCCESS`. A result exists — not absent, not skipped. |
+| 2 | Zero unticked `- [ ]` on every issue the PR closes | **PASS** | #1405 body fetched live: all **5** acceptance boxes `- [x]`, ticked by the evidence mirror on `status:ready-merge` from the PR's fenced `acceptance-evidence` block, not by hand. |
+| 3 | No new `deno-lint-ignore` / `as unknown as` / `@ts-ignore` in the diff, excluding `.llm/runs/**` | **PASS** | `git diff origin/main...FETCH_HEAD -- . ':(exclude).llm/runs/**'` grepped for all four patterns on added lines → no matches. The diff **was** scanned; this is a verdict, not an absence. |
+| 4 | Named expensive gates report `SUCCESS`, not `SKIPPED`/`CANCELLED` | **PASS** | Named individually rather than counted: `scaffold-runtime (aspire + docker + postgres)` SUCCESS · `scaffold-runtime-sqlite (aspire + sqlite + garnet)` SUCCESS · `scaffold-static (deno-only)` SUCCESS · `code-quality` SUCCESS · `quality` SUCCESS · `check-test` SUCCESS · `surface-diff` SUCCESS · `deps-report` SUCCESS · `close-gate` SUCCESS. |
+| 5 | The single decisive claim per issue, re-verified independently | **PASS** | Claim: *the reasons are now accurate and each is independently pinned*. Verified twice from different directions — orchestrator reverted **both** fixes → 5 failures; IMPL-EVAL reverted **each** fix alone → close-drain test fails only under fix-A revert, refusal tests only under fix-B revert. Aggregate redness would not have proven this. |
+| 6 | Changed-file audit for `packages/**`/`plugins/**` on docs-lane PRs | **N/A, and audited anyway** | Not a docs-lane PR. Files: 4 × `packages/plugin-streams-core/**` + 4 × this run's own `slices/1405/**` artifacts. No unrelated package or plugin touched; no #1398 surface. |
+| 7 | The PR body's own checklist matches what shipped | **PASS** | All 5 DoD boxes ticked and each is true of the diff: reasons-and-observability only; four negative tests present; evidence recorded including the red the brief's own bad command produced. |
+
+**Additional gate run (repo requirement, not in the seven):** `agentic:review-threads` →
+`PASS threads=0 unanswered=0`, exit 0. No silent review finding is being merged past.
+
+## Did-not-run discipline
+
+Every row above states a positive result from a named source. The checks that legitimately did not
+run are recorded rather than ignored: `agent` (×2), `code-quality-repo`, and `Minimax M3 docs
+accuracy` reported `SKIPPED`. None is in the named-gate set for a `packages/**` code change —
+`code-quality-repo` is the repo-wide audit and the Minimax job is the docs-accuracy lane — and
+`code-quality` itself ran and passed.
+
+**Recorded caveat carried from IMPL-EVAL:** `deno task quality:gate`'s configured roots omit
+`packages/plugin-streams-core`, so the package-quality verdict rests on the **explicit target scan**
+(`findings=[]`, `allowCount=0`) rather than on the repo gate. The CI `code-quality` job passing is
+therefore not by itself proof of this package's quality; the target scan is. This is a repo
+gate-coverage gap, not a defect in this change, and it is stated here so the merge record is not
+read as more than it is.
+
+## Verdict
+
+**Cleared to merge.** All seven checks pass with evidence; IMPL-EVAL PASS from a separate session;
+no unanswered review threads.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1536.md b/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1536.md
new file mode 100644
index 0000000000..2837173972
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/pre-merge-gate-1536.md
@@ -0,0 +1,50 @@
+# Pre-merge gate — PR #1536 (closes #1398)
+
+Run per `.llm/harness/workflow/milestone-run.md` § The pre-merge gate, by the orchestrator holding
+merge authority. Evaluated at head **`f7d503fee`**, 2026-08-12.
+
+**Head discipline.** This PR's head changed mid-flight (`e4319c685` → `f7d503fee`) when the branch
+was synced with `main` so the phase-eval dispatcher would exist in the merge ref. **No evidence from
+before that sync is cited below.** Every gate result and job id here was re-read against
+`f7d503fee`; the pre-sync job ids (`94062070840`, `94062070984`) describe a head that is no longer on
+this PR and appear nowhere in the PR body or this record.
+
+| # | Check | Result | Evidence |
+| --- | --- | --- | --- |
+| 1 | `close-gate` result is green | **PASS** | `close-gate` → `pass` on the re-run after `status:ready-merge`. The earlier red was a genuine red against the then-current body, not a flake — it listed all four unticked issue boxes and all three unticked DoD boxes by line. |
+| 2 | Zero unticked `- [ ]` on every issue the PR closes | **PASS** | #1398 fetched live: all **4** acceptance boxes `- [x]`, ticked by the evidence mirror from the PR's `box-index` entries, not by hand. |
+| 3 | No new `deno-lint-ignore` / `as unknown as` / `@ts-ignore` in the diff, excluding `.llm/runs/**` | **PASS** | `git diff origin/main...FETCH_HEAD -- . ':(exclude).llm/runs/**'` grepped for all four patterns on added lines → no matches. Diff was scanned; this is a verdict, not an absence. |
+| 4 | Named expensive gates report `SUCCESS`, not `SKIPPED`/`CANCELLED` | **PASS** | Named individually, not counted: `scaffold-runtime (aspire + docker + postgres)` · `scaffold-runtime-sqlite (aspire + sqlite + garnet)` · `scaffold-static (deno-only)` · `code-quality` · `quality` · `check-test` · `surface-diff` · `deps-report` · `close-gate` — all `pass` at `f7d503fee`. |
+| 5 | The single decisive claim per issue, re-verified independently | **PASS** | Claim: *job executions now reach the durable stream and join the `job.execute` trace*. Both formerly-deferred gates re-read **by name** from this head's logs: postgres job `94073971396` (`passed=88 failed=0 skipped=0`) and sqlite job `94073971501` (`passed=83 failed=0 skipped=0`). The Qwen IMPL-EVAL independently traced the join mechanism end-to-end (hook → `producer.upsert` → `#startPublish` → `instrumentation.startPublish` on ambient context). |
+| 6 | Changed-file audit for `packages/**`/`plugins/**` on docs-lane PRs | **N/A, audited anyway** | Not a docs-lane PR. 7 non-run-artifact files, all in `packages/cli/e2e`, `packages/plugin-workers-core`, `plugins/workers`. No `streams/schema.ts` change (D4 honoured), no #1405 surface, no dependency or export-map change. |
+| 7 | The PR body's own checklist matches what shipped | **PASS** | All 7 DoD boxes ticked and each true of this head. The two previously-false claims were **replaced, not ticked over**: the E2E box no longer says "blocked before those gates by `runtime.flow-b-fixture` fetch failure", and the acceptance evidence no longer says "Not yet live-verified". |
+
+**Additional gate:** `agentic:review-threads` → `PASS threads=0 unanswered=0`, exit 0.
+
+## IMPL-EVAL
+
+`OPENHANDS_VERDICT: PASS` — automatic phase dispatcher, `openrouter/qwen/qwen3.8-max`, run
+`31584188459`, evaluated at head `f7d503fee` against trusted base `281ab76887`. Triggered by the
+label pair alone per D-5; **no manual OpenHands dispatch and no local evaluator** was used for this
+PR.
+
+Its three advisories are all pre-existing and tracked, none blocking:
+
+- `close-gate` red pending `status:ready-merge` — now resolved, above.
+- `quality:gate` roots omit `packages/plugin-workers-core` / `packages/cli/e2e` — filed as **#1542**.
+- Undeclared `@netscript/plugin-streams-core` imports — filed as **#1543**.
+
+## Did-not-run discipline
+
+`agent` ×2 and `code-quality-repo` report `skipping`. None is in the named-gate set for this change
+(`code-quality-repo` is the repo-wide audit), and `code-quality` itself ran and passed.
+
+**Caveat carried into the merge record:** as with #1528, `quality:gate`'s configured roots do not
+cover the packages this change touches, so the package-quality verdict rests on the **explicit target
+scans** the slice worklog records (`findings=[]`, `allowCount=0`) rather than on the repo gate. CI
+`code-quality` passing is not by itself proof for these packages. That gap is #1542.
+
+## Verdict
+
+**Cleared to merge.** Seven checks pass with named evidence at `f7d503fee`; IMPL-EVAL PASS from a
+separate automatic session; no unanswered review threads.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/research-1398.md b/.llm/runs/release-0.0.6-features--orchestration/slices/research-1398.md
new file mode 100644
index 0000000000..10fa0cfb9c
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/research-1398.md
@@ -0,0 +1,175 @@
+# Research — #1398 job executions never reach the durable job stream
+
+Delegated read-only sub-agent (Claude · Opus 5, `drift.md` D-1). Baseline `origin/main@01aa12b67`.
+**Terminated early on budget** after a narrow discovery pass; the unverified list below is part of
+the finding, not a gap to paper over. Every claim carries a `path:line` citation from a file the
+sub-agent actually read.
+
+## Root cause — already recorded in-repo, and it is a wiring gap, not a missing feature
+
+The stream mutation hook is installed by the workers **API service** and by nothing else:
+
+- `plugins/workers/services/src/main.ts:67` —
+ `runtime.executionState.setMutationHook(createStreamMutationHook())`, inside a `queueMicrotask`
+ after `serve()`.
+- The **background** entrypoints never call `setMutationHook`:
+ `plugins/workers/bin/runtime.ts:89-107` (`startWorkerProcess`), `:110-122`
+ (`startSchedulerProcess`), `:125-152` (`startCombinedProcess`). Each builds its own runtime via
+ `createWorkersServiceRuntime()` (`plugins/workers/services/src/service-runtime.ts:80-89`, with
+ `KvExecutionState` at `:84`).
+- Generated projects run `startCombinedProcess()`
+ (`plugins/workers/src/adapter/resources/glue/runtime.stub.ts:21-24`) as the Aspire resource
+ `workers-combined` (`plugins/workers/src/aspire/workers-contribution.ts:12,56-61`).
+
+So job **executions** run in a process whose execution-state mutations are never forwarded to the
+producer. Job **definitions** are published from a different place —
+`plugins/workers/services/src/init.ts:91-111` calls `emitJobToStream(...)` per job at startup, which
+is exactly the three startup snapshots the issue observed. Chain:
+`plugins/workers/streams/producer.ts:61-63` → `packages/plugin-workers-core/src/streams/producer.ts:122-127`
+→ `producer.upsert('job', job)`. Stream path `/workers/executions`, producerId `workers-service`
+(`packages/plugin-workers-core/src/streams/producer.ts:5-6,66-73`).
+
+**The repo already says this.** The two Flow-B OTEL gates are deferred against #1398 with the reason
+*"workers-combined does not install the stream mutation hook"* —
+`packages/cli/e2e/suites/scaffold/capability-suites.ts:23-34`, asserted by
+`packages/cli/e2e/tests/presentation/suite-registry_test.ts:204-215`. `behavior.otel.stream-consumer`
+and `behavior.otel.traces` are registered (`otel-gates.ts:52-84`) but excluded from
+`scaffold.runtime` / `scaffold.runtime.sqlite` via `SCAFFOLD_RUNTIME_DEFERRED_GATES`.
+
+## Execution path and the identifiers available at completion
+
+Webhook → triggers service → `TriggerProcessor.process` → `dispatchAction`
+(`packages/plugin-triggers-core/src/runtime/trigger-processor.ts:172`, wired at
+`plugins/triggers/src/runtime/trigger-runtime-processor.ts:80-82`) → `enqueueWorkerJob` (`:318-367`)
+enqueues a `JobMessage` on queue `jobs` with `correlationId = event.id` (`:333`) and
+`traceparent`/`tracestate` from `traceJobDispatch` (`:348-354`).
+
+Worker consumption — `plugins/workers/worker/job-dispatcher.ts:35-175`:
+
+| Step | Line | Note |
+| --- | --- | --- |
+| `executionState.create({… correlationId, traceparent, tracestate})` | `:74-83` | **before** the `job.execute` span exists |
+| `traceJobExecution` creates the `job.execute` span | `:91` | |
+| `executionState.start` | `:131` | inside the span context |
+| `executionState.complete(executionId, {status, exitCode, result, error})` | `:165-170` | inside the span context |
+
+`traceJobExecution` → `withSpan` (`packages/telemetry/src/instrumentation/worker.ts:295-323`;
+`packages/telemetry/src/application/span.ts:32-62`) uses `context.with(trace.setSpan(...))`, so any
+span started **during** the callback inherits the `job.execute` trace id.
+
+The stored record already carries the join identifiers:
+`packages/plugin-workers-core/src/state/execution-state.ts:35-78` (`traceparent` `:74`, `tracestate`
+`:76`), with mutations emitted at `:288` (delete) and `:307` (`#save`, created/updated).
+
+## The producer is already reachable — nothing needs threading
+
+`getWorkersStreamProducer()` is a module-level singleton
+(`plugins/workers/streams/producer.ts:28-44`), so the worker process can publish without new
+plumbing. Public API: `createDurableStream`
+(`packages/plugin-streams-core/src/application/create-durable-stream.ts:28-49` options, `:124-146`
+`upsert`, `:148-170` `delete`, `:171-186` `flush/stop/close`, `:295-320` singleton-per-streamPath
+with a fingerprint-mismatch throw). `upsert` returns `StreamWriteReceiptV1`
+(`producer-contract-v1.ts:97-105`); bounded buffer 256 events / 1 MiB (`:47-55`).
+
+**Environment precondition:** `getStreamsUrl()` requires `DURABLE_STREAMS_URL` or
+`services__streams__http__0` (`packages/plugin-streams-core/src/application/stream-url-resolver.ts:154-190`);
+missing → throw at `create-durable-stream.ts:262-272`. Whether `workers-combined` actually receives
+that env is **unverified** — see below.
+
+## Record shape and how `traceparent` is actually carried
+
+`traceparent` is **not** on the stream record. It is a **header**, generated by the producer's own
+`stream.publish` span: `create-durable-stream.ts:222-242` →
+`packages/plugin-streams-core/src/telemetry/instrumentation.ts:159-177`
+(`headers.traceparent = formatTraceparent(span.spanContext())`). `correlationId` defaults to the
+entity key when no `StreamWriteContextV1` is passed (`create-durable-stream.ts:229-230`). The
+envelope contract requires `operation`, `correlationId`, `traceparent`
+(`packages/plugin-streams-core/src/domain/sse-contract-v1.ts:34-56`).
+
+The `execution` entity schema **omits** `traceparent`/`tracestate`
+(`packages/plugin-workers-core/src/streams/schema.ts`, `WorkerExecutionZodSchema`), and the mapper
+drops them (`packages/plugin-workers-core/src/streams/producer.ts:88-106`).
+
+**This matters more than it looks.** Because the header trace id comes from the publish span, the
+join to `job.execute` is satisfied by *where the publish happens in the trace context*, not by
+copying a field. That is what the E2E gate asserts.
+
+## The gate that defines "done"
+
+`packages/cli/e2e/src/application/gates/scaffold/consume-flow-b-stream.ts:52-107` reads the
+`job.execute` correlation + trace id from the dashboard, then selects; offset/live-SSE loop at
+`:203-230`. The selector `select-flow-b-stream-change.ts:68-115` matches on `value.correlationId`
+else `headers.correlationId`, and `:122-153` (**TC-14**) requires the matched record's
+`headers.traceparent` trace id to equal the `job.execute` trace id.
+
+Consumer surface: `bindStreamEventSourceV1`, `createStreamSseReplayStateV1`,
+`parseStreamSseEventV1` (`packages/plugin-streams-core/src/sse/mod.ts:30-34`); offsets are opaque
+(`sse-contract-v1.ts:25,62-72`).
+
+## Doctrine and dependency edges
+
+- `plugins/workers/deno.json` **already declares** `@netscript/plugin-streams-core`, and the manifest
+ declares `.withDependencies({ streams: streamsPlugin })` (`plugins/workers/src/public/mod.ts:61`).
+ **A workers-side fix needs no new dependency edge.**
+- Two undeclared-import findings, incidental to this issue but real:
+ `packages/plugin-workers-core/deno.json` does not list `@netscript/plugin-streams-core` while
+ `src/streams/producer.ts:1` and `src/streams/schema.ts:1-2` import it; same for
+ `plugins/triggers/deno.json` vs `plugins/triggers/streams/{producer,factory,schema}.ts`. Both
+ resolve through the workspace today. Whether `deno publish` rejects them is **unverified**.
+- Plugins are Archetype 5 / thin; convention-bearing primitives belong in `-core`
+ (`docs/architecture/doctrine/11-plugin-thinness-and-base-seams.md:16-36`).
+- No `arch-debt.md` entry mentions #1398.
+
+## Existing tests
+
+`packages/plugin-workers-core/tests/streams/workers-streams_test.ts:9-104` covers the mapper and hook
+as units against a mock producer, with **no traceparent assertion**. There is no test that would fail
+if the hook were never installed on the background runtime — which is precisely why this shipped.
+
+## The two decisions the plan must make (these are findings, not proposals)
+
+1. **Volume and key collision.** Every mutation fires the hook (`execution-state.ts:288,307`), so
+ installing it on the worker publishes roughly four records per execution
+ (pending/queued/running/terminal) — each an `upsert` on the **same key**.
+2. **Trace-context placement — the sharp edge.** TC-14 requires the matched record's publish span to
+ be a child of `job.execute`. `complete()` (`job-dispatcher.ts:165`) **is** inside that active
+ context, so its publish inherits the right trace id. But `create()` (`:74`) runs **before**
+ `traceJobExecution` (`:91`), so a record published from it carries a **different** trace id. Since
+ the selector matches on `correlationId` and both records share it, a naive "install the hook and
+ publish everything" fix can produce a record set where the *first* match fails TC-14. Publishing
+ more records is not automatically closer to passing.
+
+`correlationId` reaches the consumer only because `toExecutionStreamEntity` sets `value.correlationId`;
+`createStreamMutationHook` passes no `StreamWriteContextV1` (`producer.ts:112-118`).
+
+## Unverified — do not treat as facts
+
+- Whether `workers-combined` actually receives `services__streams__http__0` at runtime
+ (`packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:180-220`
+ was located but **not read**). If it does not, the fix is larger than a hook installation.
+- Whether `plugins/workers/bin/{combined,worker,scheduler}.ts` exist as thin wrappers — only
+ `bin/runtime.ts` was read.
+- Whether `deno publish` fails on the undeclared `plugin-streams-core` imports above.
+- The triggers-side execution publication (`plugins/triggers/streams/server.ts`) and whether its hook
+ is installed.
+- `packages/plugin-workers-core/src/domain/job-spec.ts:113-142` ("event published when a job
+ execution completes") — not read.
+- Workers SSE `subscribe.ts` (KV-watch, distinct from the durable stream) — not read.
+- `validate-flow-b-traces.ts` contents — not read.
+- Whether the streams schema carries an explicit version marker (bears on whether adding
+ `traceparent` to the entity implies a version bump).
+
+## Handoff — files to open in order
+
+1. `plugins/workers/bin/runtime.ts:89-152` — the missing `setMutationHook` call site.
+2. `plugins/workers/services/src/main.ts:65-75` — the correct wiring to mirror.
+3. `plugins/workers/worker/job-dispatcher.ts:74-170` — identifiers and the `job.execute` context.
+4. `packages/plugin-workers-core/src/streams/producer.ts:88-119` — mapper + hook.
+5. `packages/plugin-workers-core/src/streams/schema.ts` — `WorkerExecutionZodSchema` field set.
+6. `packages/plugin-streams-core/src/application/create-durable-stream.ts:124-146,222-242` — publish
+ and header semantics.
+7. `packages/cli/e2e/src/application/gates/scaffold/select-flow-b-stream-change.ts:122-153` — TC-14.
+8. `packages/cli/e2e/suites/scaffold/capability-suites.ts:23-34` +
+ `packages/cli/e2e/tests/presentation/suite-registry_test.ts:204-225` — the deferral to remove.
+9. `packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-background.ts:180-220`
+ — confirm the streams env reaches `workers-combined` (**first unverified item**).
diff --git a/.llm/runs/release-0.0.6-features--orchestration/slices/research-1405.md b/.llm/runs/release-0.0.6-features--orchestration/slices/research-1405.md
new file mode 100644
index 0000000000..5322e7a1a6
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/slices/research-1405.md
@@ -0,0 +1,98 @@
+# Research — #1405 durable producer rejection taxonomy
+
+Author: orchestrator session (Claude · Opus 5 · high). Baseline `origin/main@01aa12b67`.
+Read-only inspection; no code changed in this phase.
+
+## Confirmed defect 1 — close-drain window reports `producer-failed`
+
+`close()` sets `#accepted = false` at
+`packages/plugin-streams-core/src/application/durable-stream-producer-supervisor.ts:215` and *then*
+delegates to `#closeGracefully()` (`:216`), which first `await`s `flush()` and the connect promise
+(`:221-224`) before reaching `this.#transition('stopping', 0)` at `:225`.
+
+For the whole duration of that drain, `#accepted` is `false` while `#state.state` is still `ready`
+(or `connecting`/`backoff`). Both rejection selectors fall through to their default arm:
+
+- supervisor `#writeRejectionReason()` — `:467-478`: `#accepted` false, state not
+ `stopping`/`stopped` → `default: return 'producer-failed'`.
+- façade `stateRejection()` — `create-durable-stream.ts:251-262`: `closed` is true (supervisor
+ `get closed()` is `!this.#accepted`, `:109-111`), state not `stopping`/`stopped` →
+ `return 'producer-failed'` at `:260`.
+
+Both `upsert` (`create-durable-stream.ts:131-133`) and the second write entry point (`:159-160`) go
+through `stateRejection`. So a write arriving during an orderly drain is told the producer failed.
+It did not: `#fail()` was never called and `#state.error` is unset.
+
+`#stopImmediately()` does **not** have this hole — it transitions to `stopping` on the line right
+after clearing `#accepted` (`:185-186`), with no `await` between.
+
+## Confirmed defect 2 — first-attempt refusal reports `retry-exhausted`
+
+`#failActive()` (`:412-423`) settles every non-`aborted` failure as
+`{ status: 'delivery-unknown', reason: 'retry-exhausted' }` (`:418-420`). It has three call sites,
+and only one of them is genuine exhaustion:
+
+| Call site | Guard | True cause |
+| --- | --- | --- |
+| `:302` | `!isRetryable(connected.failure) \|\| attempt === maxAttempts` | **conflated** — either refusal or exhaustion |
+| `:346` | `else if (!isRetryable(result.failure))` | **refusal**, can fire on attempt 1 |
+| `:350` | `if (attempt === this.#reconnectPolicy.maxAttempts)` | genuine exhaustion |
+
+`:346` is reachable on attempt 1 with `maxAttempts` at any value: the append transport returned a
+non-retryable failure, i.e. the server positively refused the write. Nothing was exhausted.
+
+## Public surface impact
+
+The reason unions are declared in
+`packages/plugin-streams-core/src/domain/producer-contract-v1.ts`:
+
+- `StreamWriteRejectionReasonV1` `:70-78` — already contains `producer-stopping`.
+- `StreamWriteUnknownReasonV1` `:84-87` — `retry-exhausted | transport-aborted | producer-stopped`;
+ has **no** member naming a positive refusal.
+
+Both are re-exported from the package root (`packages/plugin-streams-core/mod.ts:44-45`), so they
+are published surface. A repo-wide grep finds **no consumer outside `plugin-streams-core/src` that
+switches on either union**, so widening `StreamWriteUnknownReasonV1` breaks no in-repo exhaustive
+switch. It is still an additive change to a published union and must be treated as such in the PR
+body.
+
+## Consequence for the fix shape
+
+1. **Close-drain.** No new public member is needed: `producer-stopping` already exists and already
+ means "the producer is shutting down and is not accepting". The defect is that the *selector*
+ cannot see the closing state before the `stopping` transition happens. The minimal accurate fix
+ is to make the closing intent observable to both selectors from the moment `close()` is entered,
+ so they return `producer-stopping` instead of falling through to `producer-failed`. Adding a new
+ `producer-closing` member is the alternative; it grows published surface for a distinction
+ (graceful drain vs abrupt stop) that the existing vocabulary already covers, and the issue only
+ requires "a reason naming the closing state, distinct from `producer-failed`".
+2. **Refusal.** A new member of `StreamWriteUnknownReasonV1` **is** required — the issue demands a
+ reason naming the refusal *and* that retry-exhaustion keeps its own reason, and no existing
+ member means "positively refused". `#failActive` must branch on `isRetryable(failure)` rather
+ than only on `failure.kind === 'aborted'`, and `:302` must stop conflating its two guards.
+
+Neither change alters which writes are accepted, rejected, or delivered. `#fail()` is still called
+in every path it is called in today; only the settled `reason` string differs.
+
+## Negative tests the acceptance requires
+
+Acceptance box 4 ("a future refactor that collapses them fails") means each reason needs a test that
+would go red if the reason regressed to the old value:
+
+1. write issued after `close()` is entered but before the drain completes → settles
+ `rejected` / closing reason, **asserted not** `producer-failed`.
+2. append transport returns a non-retryable failure on attempt 1, `maxAttempts > 1` → settles
+ `delivery-unknown` / refusal reason, **asserted not** `retry-exhausted`, and the attempt count
+ observed is 1.
+3. append transport returns retryable failures until `maxAttempts` → still settles
+ `retry-exhausted`.
+4. an existing-behaviour guard that accepted/delivered/rejected classification is unchanged
+ (`tests/telemetry/durable-stream-producer-telemetry_test.ts:172-219` already distinguishes
+ rejected / dropped / delivery-unknown and must stay green).
+
+## Open decision for the implementer brief
+
+Reuse `producer-stopping` for the close-drain window, or add `producer-closing`. The brief will
+**lock reuse of `producer-stopping`** (no published-surface growth where existing vocabulary is
+accurate) and require the refusal member to be the only union addition. Divergence goes in
+`drift.md`, not into the implementer's discretion.
diff --git a/.llm/runs/release-0.0.6-features--orchestration/supervisor.md b/.llm/runs/release-0.0.6-features--orchestration/supervisor.md
new file mode 100644
index 0000000000..8a8fac4c7f
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/supervisor.md
@@ -0,0 +1,102 @@
+# Supervisor — NetScript 0.0.6 runtime / public-surface lane
+
+| Field | Value |
+| ---------------- | ------------------------------------------------------------------------------ |
+| Run id | `release-0.0.6-features--orchestration` |
+| Profile | `.llm/harness/workflow/milestone-run.md` (topical lane, not the whole milestone) |
+| Role skill | `.agents/skills/agent-milestone-orchestrator` |
+| Supervisor model | Claude · Anthropic · Opus 5 · high (`planning_decisions`) |
+| Session surface | Claude Code, native, mobile-visible via `/rc` |
+| Host | WSL2 Linux 6.18.33.2-microsoft-standard-WSL2 |
+| Checkout | `/home/codex/repos/netscript-006-features` |
+| Control branch | `chore/release-0.0.6-features-orchestration` |
+| Baseline | `origin/main@01aa12b67` |
+| Milestone | GitHub milestone 26, `0.0.6` |
+| Opened | 2026-08-12 |
+
+## Scope — exclusive ownership
+
+This is a **topical lane**, not the whole 0.0.6 milestone. Sibling orchestration lanes run in
+parallel checkouts (`netscript-006-docs`, `netscript-006-fixes`, `netscript-006-internals`). This
+lane owns exactly two issues and takes no others:
+
+| Order | Issue | Title | Shape |
+| ----- | ------ | --------------------------------------------------------------------------------- | ---------------------------------- |
+| 1 | #1405 | durable producer settles two inaccurate rejection reasons | small, specified, deterministic |
+| 2 | #1398 | job executions are never published to the durable job stream | substantive public/runtime behavior |
+
+PR #1522 is unrelated and has been removed from the milestone — out of scope here.
+#1398 builds on merged #1395 (versioned SSE/OTEL envelope) and #1402 (durable producer reconnect)
+but is **not** satisfied by them; it stays its own reviewable PR with live E2E evidence.
+
+## Control-branch contract
+
+The control branch stores **orchestration evidence only** — this run dir and nothing else. It never
+becomes an implementation umbrella. Both issues land through normal fresh leaf worktrees/branches
+with draft PRs opened **directly against `main`**.
+
+## Lane bindings
+
+Routes selected from `.llm/harness/workflow/lane-policy.md`. Deviations are recorded in `drift.md`.
+
+| Purpose | Route |
+| -------------------------------------- | ---------------------------------------------------------------------- |
+| Orchestration (`planning_decisions`) | Claude · Opus 5 · high — this session |
+| Research / plan sub-agents | Claude · Opus 5 · medium/high (owner brief override — see D-1) |
+| Implementation #1405 | Codex · GPT-5.6 Sol · **low** (`light_implementation`), mobile-visible |
+| Implementation #1398 | Codex · GPT-5.6 Sol · **medium** (`normal_implementation`), escalate to high only on genuine complexity |
+| Slice review of #1405 (Sol·low pair) | `review_codex_light`: Claude · Opus 5 · high — this session |
+| Slice review of #1398 (Sol·med pair) | `review_codex`: Claude · Fable 5 · low |
+| PLAN-EVAL (#1398 only) | `formal_plan_evaluation` — MiniMax M3 high, fresh session |
+| IMPL-EVAL #1405 (small) | `formal_impl_evaluation` small — DeepSeek V4 Flash 0731 max |
+| IMPL-EVAL #1398 (broad/complex) | `formal_impl_evaluation` complex — Qwen 3.8 Max |
+| CI / minor green-up watcher | Codex · GPT-5.6 Sol · low |
+
+**Evaluator transport.** The owner brief routes evaluation through OpenHands *after #1524
+passes/lands*; otherwise a fresh local Claude/OpenCode OpenRouter session using the toolchain.
+Checked 2026-08-12: **PR #1524 is OPEN and unmerged** (`mergedAt: null`), with its own DoD boxes
+`Bounded live DeepSeek smoke` and `Repository default variable is updated` still unticked. Therefore
+this run uses the **local fresh-session fallback** for every evaluation until #1524 lands. Re-check
+before each eval dispatch and record the observed state.
+
+Generator and evaluator never share a session. No implementation lane self-certifies.
+
+## PLAN-EVAL decision
+
+- **#1405** — `PLAN-EVAL: N/A`. Owner brief authorizes it; the issue carries a complete contract
+ (exact call sites, exact acceptance, reasons-only change with no accepted/rejected/delivered
+ behaviour change). Per lane-policy owner decision 2026-08-08, small/mechanical issues with a
+ complete contract record N/A.
+- **#1398** — PLAN-EVAL **required**, separate session, before implementation. It changes public
+ runtime behaviour and a published record shape.
+- **Wave plan itself** — this lane is two sequential PRs, not a multi-wave board. Per
+ `milestone-run.md` the wave-plan PLAN-EVAL is `[asserted]`, not observed; recorded decision:
+ **no separate PLAN-EVAL for the two-PR sequencing plan**, because the #1398 PLAN-EVAL covers the
+ only decision-heavy content in it. Recorded here rather than assumed.
+
+## IMPL-EVAL decision
+
+- **#1405** — **owner ruling 2026-08-12 (supersedes the reading below):** for this class — small,
+ deterministic, fully specified taxonomy fixes — do **not** dispatch a separate evaluator. Sufficient
+ evidence is focused negative tests + CI + close-gate + the orchestrator's independent diff review.
+ This ruling arrived **after** #1405 had already been evaluated and merged (`8ff1bcb8f`), so it does
+ not change that outcome; it governs this class from here. See `drift.md` D-3.
+- ~~#1405 — owner brief permits an IMPL-EVAL waiver *provided exact negative tests exist*. Default is
+ still to run the small IMPL-EVAL lane; the waiver is only exercised if the evaluator transport is
+ genuinely blocked.~~ — my reading treated the waiver as a fallback rather than the default for the
+ class. That was the wrong default and it cost one unnecessary evaluator dispatch.
+- **#1398** — IMPL-EVAL mandatory, separate session, with live runtime evidence attached.
+
+## Merge authority
+
+This orchestrator holds merge authority for both PRs and merges only through the `milestone-run.md`
+pre-merge gate, recorded per PR in `worklog.md` and appended to `cut-trace.md`. Root orchestration
+owns the canary and the stable cut; this lane does not publish.
+
+## Environment proof (stage A)
+
+- `git rev-parse --abbrev-ref HEAD` → `chore/release-0.0.6-features-orchestration`
+- `git rev-parse --short HEAD` → `01aa12b67`, clean tree
+- `git fetch origin main` → `origin/main@01aa12b67` (identical; lane starts at tip)
+- `deno task agentic:runtime doctor` → `no_change (schema 1.0)`, components 18, **sessions 0**
+ (no pre-existing Codex sessions to collide with)
diff --git a/.llm/runs/release-0.0.6-features--orchestration/worklog.md b/.llm/runs/release-0.0.6-features--orchestration/worklog.md
new file mode 100644
index 0000000000..90dc8c8b02
--- /dev/null
+++ b/.llm/runs/release-0.0.6-features--orchestration/worklog.md
@@ -0,0 +1,550 @@
+# Worklog — 0.0.6 runtime / public-surface lane
+
+## 2026-08-12 — Stage A, bootstrap
+
+**Identity / worktree proof.**
+
+| Check | Command | Result |
+| --- | --- | --- |
+| Branch | `git rev-parse --abbrev-ref HEAD` | `chore/release-0.0.6-features-orchestration` |
+| HEAD | `git rev-parse --short HEAD` | `01aa12b67` |
+| Tree | `git status --porcelain` | clean at open |
+| Remote | `git fetch origin main` | `origin/main@01aa12b67` — lane starts at tip |
+| Runtime | `deno task agentic:runtime doctor` | `no_change (schema 1.0)`; components 18; **sessions 0** |
+
+**Correction to the line above.** `doctor`'s `sessions: 0` counts *desired-state runtime controller*
+sessions, **not** live Codex threads. A later `deno task agentic:codex-status` showed a sibling lane
+working in `/home/codex/repos/ns006-1374-compilegate` plus three idle `agy` sessions elsewhere. No
+collision occurred — this lane uses its own fresh worktrees — but "sessions: 0" must not be read as
+"nothing is running", and is not cited as such anywhere else in this run.
+
+**Issue re-baseline (live bodies fetched, not recalled).**
+
+| Issue | State | Milestone | Labels | Acceptance boxes |
+| --- | --- | --- | --- | --- |
+| #1405 | OPEN | 26 / `0.0.6` | `type:fix`, `area:plugins`, `status:triage`, `priority:p2` | 5, all unticked |
+| #1398 | OPEN | 26 / `0.0.6` | `type:fix`, `area:plugins`, `area:telemetry`, `status:triage`, `priority:p1` | 4, all unticked |
+
+Both still carry `status:triage` and no assignee — neither has been started by another lane.
+
+**Predecessor state.** PR #1395 merged 2026-08-09T01:25:15Z; PR #1402 merged 2026-08-09T05:11:32Z.
+Both are ancestors of the baseline, so #1398 is being planned against the landed envelope and the
+landed reconnect supervisor, not against their PR branches.
+
+**Evaluator-transport precondition.** `gh pr view 1524` → **OPEN**, `mergedAt: null`. Its own DoD
+still has `Bounded live DeepSeek smoke` and `Repository default variable is updated` unticked. The
+brief's OpenHands eval route is conditional on #1524 passing/landing, so this run falls back to
+fresh local sessions. Recorded as `drift.md` D-2; re-checked before each eval dispatch.
+
+**Research.** #1405 researched in-session (small, fully specified) →
+`slices/research-1405.md`, both defects confirmed at exact call sites with a line-cited call-site
+table for `#failActive`. #1398 research delegated to a Claude Opus sub-agent (read-only, `drift.md`
+D-1 records the lane override) — report pending.
+
+## 2026-08-12 — #1398 research returned early (budget), root cause found
+
+The delegated research sub-agent was **stopped on token budget** mid-pass and asked for concise
+findings rather than killed, so its evidence survived. Report: `slices/research-1398.md`.
+
+It found the root cause and, importantly, found that **the repo already records it**: the two Flow-B
+OTEL gates are deferred against #1398 with the reason "workers-combined does not install the stream
+mutation hook" (`packages/cli/e2e/suites/scaffold/capability-suites.ts:23-34`). The workers API
+service installs the hook (`plugins/workers/services/src/main.ts:67`); the background entrypoints
+that generated projects actually run never do (`plugins/workers/bin/runtime.ts:89-152`).
+
+The report's honest **unverified list** is carried into `plan.md` as blocking slice S0 rather than
+being smoothed over — the first item (does `workers-combined` actually receive the streams env)
+decides whether the fix is a hook installation or something larger.
+
+**Orchestrator-verified fact** (not delegated, checked in-session, because the whole plan turns on
+it): `job-dispatcher.ts:44` derives `parentContext` from the stored trace headers and passes it to
+`traceJobExecution` at `:108`, so `job.execute` shares its trace id with the record's stored
+`traceparent`; and `instrumentation.ts:160` starts the publish span on the **ambient** context. That
+pair is what lets every published execution record join the `job.execute` trace, including the
+pre-span `create()` record — which is the sharp edge the research flagged.
+
+## 2026-08-12 — #1405 slice dispatched (Tier-D, mobile-visible)
+
+| Field | Value |
+| --- | --- |
+| Worktree | `/home/codex/repos/ns006-1405` (fresh leaf) |
+| Branch | `fix/1405-durable-producer-rejection-taxonomy` @ `01aa12b67`, **no upstream by design** |
+| Thread id | `019ff4f0-0e1c-7333-8138-bbb107e4f1b3` |
+| Rollout | `/home/codex/.codex/sessions/2026/08/12/rollout-2026-08-12T09-46-40-019ff4f0-0e1c-7333-8138-bbb107e4f1b3.jsonl` |
+| Requested route | openai · gpt-5.6-sol · low |
+| Observed route | openai · gpt-5.6-sol · low — **verdict: matched** |
+| Runtime | approval=never · sandbox=dangerFullAccess |
+| Steering | `codex exec resume 019ff4f0-0e1c-7333-8138-bbb107e4f1b3 -- ""` |
+| Brief | `slices/implement-1405.md` (staged to `/home/codex/ns006-1405-brief.md`) |
+
+Launched through `deno task agentic:launch-codex-slice` after a clean `--dry-run`; the first dry-run
+**failed git-safety** because the leaf branch tracked `origin/main`, which was cleared before launch
+(push is explicit-refspec only). Watching with `agentic:codex-watch --mode turn` on that thread —
+event-driven, not polled.
+
+The brief locks both reason decisions (reuse `producer-stopping`; add exactly one new member
+`transport-refused`), names the gates as deliverables including the mandatory `quality:gate`,
+pre-empts the known `deno fmt` rewrap hazard, and forbids the agent from merging or flipping to
+ready.
+
+## 2026-08-12 — #1405 slice landed on its branch; Tier-A review done
+
+Codex thread `019ff4f0-0e1c-7333-8138-bbb107e4f1b3` completed its turn (detected by
+`agentic:codex-watch --mode turn`, not by polling). Commit `c491c6989`, pushed by explicit refspec,
+**draft PR #1528** open against `main` carrying `Closes #1405`, a fenced `acceptance-evidence` block
+mapping all five boxes, and a DoD whose last two boxes (IMPL-EVAL, orchestrator review) are
+correctly left unticked.
+
+**Independent re-verification (not read off the slice report):**
+
+| Check | Command | Result |
+| --- | --- | --- |
+| Package suite | `deno task --cwd packages/plugin-streams-core test` | **33 passed, 0 failed** |
+| Negative case | both fixes reverted, suite re-run | **29 passed, 5 failed** — the guards fire |
+| Restore | `git checkout` + re-run | 33/33, clean tree |
+
+The negative-case run is the `milestone-run.md` gate-integrity rule applied: a guard enters only
+with its predicate demonstrated. 0.0.4 shipped two guards that could never fire and looked correct;
+this one was checked rather than assumed.
+
+**Diff conforms to the locked decisions.** `transport-refused` is the only public-surface addition;
+close-drain reuses `producer-stopping`; `#failActive` branches on `isRetryable`; the conflated
+connect guard is split. The façade's duplicate `stateRejection()` was **deleted** and both write
+entry points delegate to `supervisor.writeRejectionReason()` — so the two selectors that drifted
+apart to produce this defect can no longer drift again.
+
+**Two advisory findings, neither blocking** (posted to #1528): the reverted-state failure arrives as
+an uncaught-error cascade rather than four individual assertion failures; and `arch:check` carries
+`WARN=1` because the supervisor file is now 515 lines against a 500-line advisory cap (`FAIL=0`).
+
+**A defect in my own brief, surfaced by the implementer.** The brief's gate list named
+`deno test packages/plugin-streams-core`, which exits 1 with 19 `NotCapable` failures for want of
+`--allow-env`. The slice reported the red with its cause instead of hiding it and used the
+package-declared task. The brief was wrong; corrected before the #1398 brief goes out.
+
+**IMPL-EVAL dispatched** — separate session, separate worktree `/home/codex/repos/ns006-1405-impleval`
+at `c491c6989`, DeepSeek V4 Flash 0731 max (small-impl evaluator lane, local fallback transport per
+D-2). Its prompt (`slices/impl-eval-1405-prompt.md`) directs it at the highest-value failure mode —
+whether a genuinely failed producer can now be masked as merely closing, which would be a worse
+defect than the one being fixed — and requires it to revert each fix **individually** to prove each
+test fails for its own reason.
+
+## 2026-08-12 — #1398 S0 resolved (and an orchestrator inference corrected)
+
+The plan's blocking precondition — does `workers-combined` actually receive the streams URL — is
+**answered: yes**, so S0 stops being a blocking unknown and becomes a runtime confirmation in S3.
+
+Evidence: `generate-register-background.ts:200-218` emits `services__][__http__0` for every
+background-processor `PluginReferences` entry, and
+`packages/cli/src/public/features/plugins/install/install-plugin_test.ts:1393-1396` asserts
+`BackgroundProcessors.workers.PluginReferences === ['streams', 'workers-api']`.
+
+**Correction.** My first read was `plugins/workers/src/aspire/workers-contribution.ts:55-63`, where
+`addDenoBackground` declares no `streams` reference and only `builder.waitFor(combined, api)` — from
+which I concluded the env was missing and the plan needed an extra Aspire slice. That inference was
+wrong. `PluginReferences` is not derived from the contribution file; it is reconciled from the plugin
+manifest's `.withDependencies({ streams: streamsPlugin })` (`plugins/workers/src/public/mod.ts:61`).
+Verifying the mechanism rather than stopping at the first plausible file is what kept an unnecessary
+slice out of the plan. Recorded here rather than silently amended, because the wrong version was
+briefly the basis for a scope judgement.
+
+Residual for S3 only: the generated wiring guards with `if (][Endpoint)`, so the env is silently
+omitted if the streams resource exposes no `http` endpoint at wiring time. Runtime observation, not
+a design unknown.
+
+**Evaluator sessions in flight** (both real agentic turns, not nominal): PLAN-EVAL #1398 on MiniMax
+M3 high (208 stream events at 07:59Z) and IMPL-EVAL #1405 on DeepSeek V4 Flash 0731 max (1012
+events). Preset validation ran first — `agentic:provider-canary --all` reported all six presets
+`passed`, with `claude-evaluator-minimax-m3` and `claude-evaluator-deepseek-v4-flash-0731` both
+`liveEligible: true`, `agenticTurn: supported`.
+
+## 2026-08-12 — #1398 PLAN-EVAL PASS, plan amended, slice dispatched
+
+**PLAN-EVAL verdict: PASS** (MiniMax M3 high, fresh session, worktree
+`/home/codex/repos/ns006-1398-planeval` @ `01aeafbfa`, 562,947 ms, `is_error: False`, 215 stream
+events — a real agentic turn, not a nominal one). Verbatim verdict in `plan-eval.md`; a leaked
+one-line preamble before the verdict token was stripped and that edit is disclosed in the file.
+
+It confirmed the causal chain one hop deeper than I had traced it — through `withSpan` into
+`packages/telemetry/src/application/span.ts:38-43`, where `parentContext` is passed as the third
+argument to `tracer.startSpan` — and confirmed TC-14 compares **trace id only**
+(`select-flow-b-stream-change.ts:131-153`) and that the selector returns the **first**
+`correlationId` match (`:96-105`). It also answered S0 `yes` by a **different route** than I did
+(`plugin-reference-reconciler.ts:70-91` rather than `install-plugin_test.ts`), so that precondition
+is now confirmed twice from two directions.
+
+**Two findings folded into the plan after I verified each myself:**
+
+- **F1** — the join mechanism was implicit and would have failed silently.
+ `StreamsTracerPort.startSpan` (`instrumentation.ts:92-102`) takes **no** parent-context argument,
+ so only an explicit `context.with(extractContext({traceparent, tracestate}), …)` wrapper around
+ `producer.upsert` makes the publish span inherit the job trace. `createStreamMutationHook`
+ (`plugin-workers-core/src/streams/producer.ts:108-118`) does no wrapping today. D3 now says so.
+- **F2** — the plan named one of the **two** tests that pin the deferral. The second,
+ `suite-registry_test.ts:209-234`, asserts `SCAFFOLD_RUNTIME_DEFERRED_GATES` equals the exact
+ two-entry list and that neither runtime tier executes a deferred gate. I read both tests directly
+ to confirm. Emptying the constant without rewriting that test leaves S2 red. D5 now names both.
+
+Neither was blocking; both earned the pass.
+
+**Slice dispatched (Tier-D, mobile-visible).**
+
+| Field | Value |
+| --- | --- |
+| Worktree | `/home/codex/repos/ns006-1398` (fresh leaf, upstream cleared before launch) |
+| Branch | `fix/1398-publish-job-executions-to-durable-stream` @ `01aa12b67` |
+| Thread id | `019ff4ff-a633-7062-ae9c-21930930b5d6` |
+| Rollout | `/home/codex/.codex/sessions/2026/08/12/rollout-2026-08-12T10-03-42-019ff4ff-a633-7062-ae9c-21930930b5d6.jsonl` |
+| Requested / observed route | openai · gpt-5.6-sol · **medium** — **verdict: matched** |
+| Steering | `codex exec resume 019ff4ff-a633-7062-ae9c-21930930b5d6 -- ""` |
+| Brief | `slices/implement-1398.md` |
+
+The brief carries F1 and F2 as locked requirements, names the `create()` trap explicitly (install the
+hook without D3's wrapping and it looks correct, then fails TC-14 on the first matched record),
+corrects the `--allow-env` gate-command defect from the previous brief instead of repeating it,
+fences the out-of-scope items (#1405's surface, the undeclared imports, any schema change), and
+requires the expensive `scaffold.runtime` gate to be confirmed un-contended before it runs.
+
+**Note on tooling:** `slices/codex-thread-ids.md` is written per slice-dir and was **overwritten** by
+the second launch, so it now shows only the #1398 thread. The #1405 identity is preserved in this
+worklog above; nothing was lost, but the file is not an accumulating registry.
+
+## 2026-08-12 — #1405 MERGED (`8ff1bcb8f`)
+
+IMPL-EVAL returned **PASS** (DeepSeek V4 Flash 0731 max, fresh session, 642,836 ms, 5,226 events).
+Its per-fix revert isolation is the substantive result: reverting only the `#closing` change fails
+only the close-drain test; reverting only the `#failActive` change fails only the refusal tests.
+My earlier both-at-once revert proved the tests fire but **not** that each guards its own defect —
+that distinction is what acceptance box 4 actually asks for.
+
+**One evaluator suggestion declined, with reasons on the PR.** It proposed dropping the unreachable
+`?? 'producer-failed'` fallback (`create-durable-stream.ts:132,160`). The unreachability is correct,
+but the cleanup does not typecheck: `writeRejectionReason()` returns
+`StreamWriteRejectionReasonV1 | undefined`, so removing the `??` needs a non-null assertion — which
+the slice brief forbids — or a wider refactor. A total, type-safe expression is the better trade.
+Declining with a reason satisfies the review-thread gate; silently ignoring it would not.
+
+**The draft-CI trap fired exactly as the profile predicts.** Every check on #1528 read `skipping`
+while it was a draft. Under check 4 that is *unproven*, not clean — the #778/#775 failure mode where
+"clean" meant "nothing ran". Flipping to ready produced real runs, and the named expensive gates then
+reported genuine `SUCCESS`: `scaffold-runtime (aspire + docker + postgres)`,
+`scaffold-runtime-sqlite`, `scaffold-static`, `code-quality`, `quality`, `check-test`,
+`surface-diff`, `deps-report`, `close-gate`. Terminal after 440 s.
+
+The evidence mirror was **pre-flighted with `--dry-run` before labeling** and reported it would skip
+without `status:ready-merge`; applying the label let it tick all five #1405 acceptance boxes from the
+PR's fenced `acceptance-evidence` block rather than by hand.
+
+**Pre-merge gate:** `slices/pre-merge-gate-1528.md`, all seven checks PASS with named sources, plus
+`agentic:review-threads` → `PASS threads=0 unanswered=0`. Merged squash → `8ff1bcb8f`; #1405
+auto-closed `COMPLETED` by the body's closing keyword; `status:shipped` on both.
+
+**Caveat carried into the merge record rather than dropped:** `quality:gate`'s configured roots omit
+`packages/plugin-streams-core`, so this package's quality verdict rests on the explicit target scan
+(`findings=[]`, `allowCount=0`), not on the repo gate. CI `code-quality` passing is not by itself
+proof for this package. Repo gate-coverage gap, not a defect in the change — candidate for a
+follow-up issue at lane close.
+
+## 2026-08-12 — #1398 slice landed on branch; live gate red **twice**, no verdict yet
+
+Codex thread `019ff4ff-a633-7062-ae9c-21930930b5d6` completed. Three commits, draft PR **#1536**
+against `main`. Diff conforms to every locked decision — D1 (hook on worker + combined, correctly not
+scheduler), D2, **D3** (`withContext(extractContext({traceparent, tracestate}), …)` — the PLAN-EVAL
+F1 trap, implemented), D4 (`streams/schema.ts` untouched, 0 changes), D5 (both named tests).
+
+**A third stale deferral pin, found by neither the plan nor PLAN-EVAL.** The plan named
+`suite-registry_test.ts:204-215`; PLAN-EVAL F2 found `:209-234`; the implementer found a **third** in
+`suite-runner_test.ts` still expecting two deferred skipped steps, and fixed it in the same commit —
+by running the full package tests rather than only the tests it was pointed at. Each layer caught
+what the one above missed. It also hit a genuine type trap: an empty
+`as const satisfies readonly DeferredGate[]` infers its element as `never`, breaking fixture code
+that reads `issue`/`reason`; fixed by declaring against the explicit contract without relaxing an
+assertion.
+
+### The live gate has not produced a verdict — two runs, two different pre-gate failures
+
+| Run | Where it died | Result | Reached the restored gates? |
+| --- | --- | --- | --- |
+| 1 (slice) | `runtime.flow-b-fixture` — `netscript generate plugins failed: fetch failed` | `passed=33 failed=1`, exit 1 | **no** |
+| 2 (orchestrator, branch updated to current `main`) | `runtime.wait.triggers-api` — timed out unhealthy after 120 s | `passed=50 failed=1`, exit 1 | **no** |
+
+Run 2 got substantially further: the run-1 fetch failure did **not** reproduce, and fixture
+generation, Aspire start, database init/migrate/generate/seed, AppHost restart, and health waits for
+postgres, garnet, workers-api, **workers**, sagas-api and sagas all passed. It then timed out on
+`triggers-api`.
+
+**Neither run gives `behavior.otel.stream-consumer` or `behavior.otel.traces` a live verdict, so
+#1398's acceptance criterion 3 remains unproven and the PR cannot merge.** The slice said this
+plainly about run 1 and did not retry to manufacture a green; that was correct.
+
+**What I can and cannot say about run 2.** #1398 touches workers and the CLI e2e suite definitions —
+not triggers. `runtime.wait.workers` passed while `runtime.wait.triggers-api` timed out. The Aspire
+AppHost log shows no triggers error, only an unrelated dev-certificate trust warning, and an issue
+search found no known `triggers-api` health defect. That is **consistent with** an environmental
+failure but does **not** prove one — I have not reproduced the suite on a clean `main` checkout, so I
+cannot state as fact that the change is uninvolved.
+
+**Serialisation was verified before run 2**, per the expensive-gate rule: no
+`/tmp/netscript-e2e-scaffold-runtime.lease`, no competing `e2e:cli` process, zero Docker containers.
+`agentic:leak-check` after run 1 reported no survivors.
+
+### Escalating the verdict to CI rather than burning more local runs
+
+Two local failures at two unrelated points, neither reaching the target gates. CI ran
+`scaffold-runtime (aspire + docker + postgres)` **green** on PR #1528 (main + #1405) an hour earlier,
+so the CI environment starts this suite cleanly. #1398 adds both OTEL gates to `RUNTIME_GATES`, so
+CI's own scaffold-runtime job now exercises them — the same gate, in an environment that is currently
+working.
+
+PR #1536 flipped to ready to trigger the blocking tier, held at `status:impl-eval` (**not**
+`ready-merge`) so it cannot be mistaken for merge-ready. IMPL-EVAL is still required — the D-3 owner
+ruling waives evaluation only for the small deterministic class, explicitly not for this issue.
+
+## 2026-08-12 — #1398 live gates PASS in CI, both tiers
+
+The two gates deferred against #1398 ran **by name** and passed in CI, verified from job logs rather
+than inferred from a suite-level green:
+
+| Tier | Job | Gates observed | Summary |
+| --- | --- | --- | --- |
+| `scaffold-runtime (aspire + docker + postgres)` | `94062070840` | `behavior.otel.stream-consumer`, `behavior.otel.traces` | `passed=88 failed=0 skipped=0` |
+| `scaffold-runtime-sqlite (aspire + sqlite + garnet)` | `94062070984` | both, same | `passed=83 failed=0 skipped=0` |
+
+`skipped=0` matters as much as `failed=0`: a silently skipped gate is the exact false-green the
+deferral list existed to prevent. Confirmed the SQLite tier really carries them —
+`POSTGRES_ONLY_RUNTIME_GATES` (`capability-suites.ts:146-151`) lists only the four DB-specific gates,
+so `RUNTIME_SQLITE_GATES` inherits both OTEL gates. Both tiers is stronger than the plan required.
+
+**This retires the open question about the two red local runs.** I had recorded that the
+`triggers-api` timeout was *consistent with* an environmental failure but not proven to be one, since
+I had not reproduced the suite on a clean `main`. CI now runs the same suite **with this change**
+through `runtime.flow-b-fixture` and past `triggers-api` to a clean finish on both tiers. That is the
+missing control: the failures were local-host environmental, not caused by the change. Upgraded from
+"consistent with" to "established", with the evidence named.
+
+**#1398 acceptance criterion 3 is now live-verified** — a subscription opened before the trigger
+observes the execution record within a bounded time, proven by the gate's own live SSE loop
+(`consume-flow-b-stream.ts:203-230`), not by asserted timing.
+
+**CI state:** every check green except `close-gate`, which is correctly red — DoD box 5 unticked and
+#1398's acceptance boxes unmirrored. Both are now truthfully tickable and will be handled when the
+merge gate is finished.
+
+**Held, deliberately.** Per D-4 the PR stays on head `e4319c685` at `status:impl-eval`. I have made
+no label change, no body edit, no OpenHands trigger, and no local evaluator launch. IMPL-EVAL will
+arrive via the automatic dispatcher after #1524 lands; root re-enters the label to fire it. This
+lane's remaining work is to watch that verdict, then run and record the seven-check pre-merge gate.
+
+## 2026-08-12 — deferred findings filed as their own issues (scope-drift checkpoint)
+
+Two findings were fenced out of this lane's PRs on purpose. Filing them rather than letting them die
+in a run artifact, per the profile's rule that scope drift is an explicit checkpoint and that
+findings raised inside a run get filed from inside the run:
+
+| Issue | Title | Milestone | Origin |
+| --- | --- | --- | --- |
+| **#1542** | `quality:gate` roots omit published packages, so a green gate is not proof they were scanned | 0.0.7 | found **independently three times**: #1405 implementer, #1405 IMPL-EVAL (against `deno.json:156`), #1398 implementer |
+| **#1543** | `plugin-workers-core` and `plugins/triggers` import `plugin-streams-core` without declaring it | 0.0.7 | #1398 research + PLAN-EVAL, recorded as plan risk R3 |
+
+**#1542 is the more serious of the two** and is the reason the #1405 merge record says explicitly
+that this package's quality verdict rests on the explicit target scan and **not** on the repo gate.
+A `quality:gate` that silently skips a package leaves the framework-wave gate law unenforced while
+appearing enforced — the same shape as pre-merge check 4 ("clean" meaning "nothing ran"), one level
+down. Three independent sessions each hit it and each hand-compensated, which is exactly the pattern
+that eventually gets forgotten once.
+
+**#1543 is filed honestly as unverified.** Whether `deno publish` actually rejects the undeclared
+import was never checked; the issue says so and makes `publish:dry-run` evidence the first
+acceptance box rather than asserting a defect. Both the research pass and the PLAN-EVAL recorded it
+as unverified, and it is carried forward that way rather than upgraded by repetition.
+
+Neither issue is a 0.0.6 blocker; both are `0.0.7` with `status:triage`.
+
+## Waiting state — #1524 has not landed
+
+`gh pr view 1524` → `OPEN`, `mergedAt: null`. The automatic phase dispatcher is therefore not live,
+root has not re-entered `status:impl-eval`, and no automatic verdict exists. The merge gate for
+#1536 genuinely cannot proceed, and nothing has been done to it: head `e4319c685`,
+`status:impl-eval`, no label change, no body edit, no evaluator, no OpenHands trigger.
+
+Watcher confirmed **alive** (PID 224681) rather than assumed — a dead watcher and a quiet one look
+identical, and "liveness is not progress" cuts both ways.
+
+The merge-gate finish is staged and **dry-run against a throwaway copy of the PR body**
+(`scratchpad/finish-1536.py`): it ticks the three remaining DoD boxes, replaces the now-false claims
+("blocked before those gates by `runtime.flow-b-fixture` fetch failure", "Not yet live-verified") with
+the CI job evidence, and rewrites the four acceptance-evidence entries to `box-index` form because
+#1398's boxes wrap across lines and the mirror matches on a checkbox's first line. Every edit asserts
+it matched and the script refuses to finish with an unticked DoD box or a surviving stale claim.
+It is **not applied**: DoD box 6 asserts IMPL-EVAL passed, which is not true until the verdict lands.
+
+## 2026-08-12 — #1524 landed; watching for the automatic verdict
+
+`gh pr view 1524` → **MERGED** `7837ef470` at 2026-08-12T09:24:15Z. The automatic phase dispatcher is
+live, so D-4's routing is now in force: phase evaluations go through the automatic status workflow
+unless the owner documents a local route or skip.
+
+Per D-4, **root re-enters `status:impl-eval` with the Qwen override** to fire the dispatcher — that
+is root's action. This lane does not touch the label, the head, or the body, and does not launch a
+local evaluator or an OpenHands comment. #1536 verified unchanged at the moment #1524 landed: head
+`e4319c685`, labels `type:fix, status:impl-eval, area:plugins, area:telemetry`, 5 comments.
+
+Now watching #1536 for the automatic verdict — a new comment carrying an `IMPL-EVAL`/`VERDICT`
+marker, and any label transition — from a recorded baseline of 5 comments and the exact current
+label set, so a change is detected as a change rather than inferred.
+
+On a PASS the merge gate finishes in this order: apply the staged body transform (already dry-run
+against a copy), move to `status:ready-merge` so the mirror populates #1398's four issue boxes from
+the `box-index` entries, confirm `close-gate` flips green, run and record the seven-check pre-merge
+gate plus `review-threads`, merge, then append the row to `cut-trace.md` captured from
+`git log origin/main` **after** the merge.
+
+On findings, none of that runs; the findings go to the slice thread for a fix cycle and the eval loop
+limit of two failures applies before escalation.
+
+## 2026-08-12 — label re-entry executed; dispatcher structurally cannot fire on this head
+
+Executed the owner's instruction exactly: applied the one-shot `eval:model:qwen`, moved away from
+`status:impl-eval`, then re-added it. Timeline confirms a clean sequence with exactly one `status:`
+label at every point and **no head change**:
+
+| UTC | Event |
+| --- | --- |
+| 09:39:17Z | `eval:model:qwen` labeled; `status:impl` labeled; `status:impl-eval` unlabeled (move away) |
+| 09:39:25Z | `status:impl-eval` labeled; `status:impl` unlabeled (**the trigger**) |
+
+`impl-eval:skip` is not present, so nothing suppressed the eval.
+
+**It did not run, and it cannot run on this head.** Three workflow runs fired around the label
+changes — all of them `openhands-agent.yml` ("OpenHands Agent"), all `completed/skipped`, job `agent`
+skipped with zero steps. The new dispatcher, `openhands-phase-eval.yml` ("OpenHands phase
+evaluation"), produced **no run on this branch at all**, while it has runs on other branches the same
+morning.
+
+Root cause, verified rather than inferred:
+
+```
+git cat-file -e e4319c685:.github/workflows/openhands-phase-eval.yml -> ABSENT
+```
+
+For `pull_request` events GitHub resolves workflows from the PR's merge ref, so a workflow absent
+from the PR head does not exist for that PR. The branch's last sync with `main` is `e4319c685` at
+**2026-08-12T08:45:46Z**; #1524 merged at **09:24:15Z** — 38 minutes later. No amount of label cycling
+can trigger a workflow the PR does not contain.
+
+`main` has since moved again: `281ab7688 fix(agentic): use chainable token for eval statuses (#1547)`,
+which addresses the dispatcher's own `PAT_TOKEN` requirement — the "Require chainable trigger token"
+step that hard-fails when the secret is absent. So the current head is missing both the dispatcher
+and its follow-up fix.
+
+**Not actioned unilaterally.** The only path is to sync the branch with current `main`, which
+**changes the head SHA** — directly against the standing instruction to keep #1536 on its current
+head — and invalidates the present green CI, forcing a full re-run including both expensive
+`scaffold-runtime` tiers. That is a material, owner-visible trade, so it is being raised rather than
+taken. The prior green evidence (both OTEL gates passing by name on both tiers) would need to be
+re-established against the new head, which is arguably better evidence anyway since it would match
+what actually merges.
+
+## 2026-08-12 — branch synced (owner-approved head change); dispatcher fired; Qwen evaluator running
+
+Owner selected "sync branch with main, then re-enter", accepting the head change and the full CI
+re-run. Executed:
+
+| Step | Result |
+| --- | --- |
+| `git merge origin/main` into the slice branch | new head **`f7d503fee`** |
+| Dispatcher present in head? | `git cat-file -e HEAD:.github/workflows/openhands-phase-eval.yml` → **PRESENT** (was ABSENT at `e4319c685`) |
+| #1547 token fix included? | yes — `281ab7688 fix(agentic): use chainable token for eval statuses` |
+| #1398 changes survived the merge? | verified: `withContext` ×2, `setMutationHook(createStreamMutationHook())` ×2, deferral list still `= []`, `streams/schema.ts` still 0 changes, 7 non-run-artifact files unchanged in set |
+| Re-entered `status:impl-eval` | move away → re-add, exactly one `status:` at each point |
+
+**The dispatcher then fired and succeeded**: `OpenHands phase evaluation` at 09:42:56Z →
+`completed/success` (the 09:42:49Z run correctly skipped — that was the move-away event). Contrast
+with the previous attempt on `e4319c685`, where this workflow produced **no run at all**. That is the
+control proving the diagnosis: same labels, same sequence, different head — the only variable was
+whether the workflow existed in the merge ref.
+
+**Automatic evaluator is running on the requested override:** `openrouter/qwen/qwen3.8-max`,
+provider `OPENROUTER`, output `pr-comment`, run `31584188459`. No manual OpenHands dispatch was made;
+the trigger was the label pair alone, per D-5.
+
+### Watcher corrected — a counting watcher would never have fired
+
+The dispatcher's comment carries `` and
+``, i.e. OpenHands **updates that comment in place**
+rather than posting a new one on completion. The original watcher keyed on *comment count* and would
+have polled to timeout while the verdict sat in an edited comment — the same silence-looks-like-
+waiting failure that already bit this lane twice today (draft CI `skipping`, and the dispatcher not
+existing on the old head).
+
+That watcher was stopped and replaced with **one** watcher keyed on the run's terminal status and the
+comment's `conclusion` marker. Still exactly one active watch, as instructed — corrected for how the
+tool actually reports, not left running on a false assumption.
+
+CI is also re-running against `f7d503fee`; the previously proven OTEL gate evidence must be
+re-established on this head before merge, and the earlier job ids no longer describe what would land.
+
+## 2026-08-12 — CI evidence re-established on the merging head `f7d503fee`
+
+The pre-sync evidence was **not** carried forward. Both OTEL gates were re-read by name from the new
+head's job logs:
+
+| Tier | Job (head `f7d503fee`) | Gates observed | Summary |
+| --- | --- | --- | --- |
+| `scaffold-runtime (aspire + docker + postgres)` | `94073971396` | `behavior.otel.stream-consumer`, `behavior.otel.traces` | `passed=88 failed=0 skipped=0` |
+| `scaffold-runtime-sqlite (aspire + sqlite + garnet)` | `94073971501` | both, same | `passed=83 failed=0 skipped=0` |
+
+**These are different job ids** from the pre-sync run (`94062070840` / `94062070984`), which is the
+concrete proof that reusing the earlier verification would have cited evidence for a head no longer
+on the PR. The staged body transform now takes head SHA and both job ids as **required arguments**
+and asserts that none of `94062070840`, `94062070984`, or `e4319c685` survives anywhere in the
+resulting body — it fails loudly rather than silently carrying a stale citation.
+
+Full CI on `f7d503fee`: terminal after 300 s, every check green except `close-gate` (expected — DoD
+box 5 and the issue's acceptance boxes are not yet updated). `agent` ×2 and `code-quality-repo`
+report `skipping`; none is in the named-gate set for this change, and `code-quality` itself passed.
+
+The `dispatch` job also shows `pass` — that is the phase-eval dispatcher, present and running on this
+head, which it was not on the previous one.
+
+**Qwen evaluator still `in_progress`** (run `31584188459`, summary comment marker still
+`"conclusion":"running"`). Nothing is applied to the PR body or labels until that verdict lands.
+
+## 2026-08-12 — #1398 MERGED (`d7e2b67b2`) — lane complete
+
+**IMPL-EVAL: PASS** — `OPENHANDS_VERDICT: PASS`, automatic phase dispatcher,
+`openrouter/qwen/qwen3.8-max`, run `31584188459`, evaluated at head `f7d503fee` against trusted base
+`281ab76887`. Triggered by the label pair alone; no manual OpenHands dispatch and no local evaluator,
+per D-5.
+
+It independently traced the join mechanism end-to-end — hook → `producer.upsert` →
+`DurableStreamProducer#startPublish` → `instrumentation.startPublish` (`tracer.startSpan` on ambient
+context) — which is precisely the link no unit test in this slice could settle and which would have
+failed silently if wrong. Its three advisories were all pre-existing and already tracked (#1542,
+#1543) or resolved by the merge sequence itself.
+
+### Finishing the gate exposed two real problems, both caught rather than merged past
+
+1. **A second `status:` label.** Automation applied `status:augment-review` one second after I moved
+ off `status:impl-eval`, so the PR briefly carried two `status:` labels against the
+ exactly-one-status rule. Removed to restore the invariant before merging.
+2. **A stale close-gate result.** `gh pr checks` reported `close-gate` red, but that job ran at
+ 09:42:44Z — before `status:ready-merge` (10:22:50Z) and before the body update. Reading the job
+ log rather than the summary showed the mirror's own notice: *"Mirror skipped because live PR
+ labels do not include status:ready-merge"*. The red was genuine **against the body as it then
+ was**, listing all four unticked issue boxes and all three unticked DoD boxes by line — not a
+ flake to retry past. A re-run with the live labels turned it green and the mirror ticked all four
+ #1398 boxes from the `box-index` entries.
+
+**Stale evidence was actively prevented, not merely avoided.** The head changed mid-flight
+(`e4319c685` → `f7d503fee`), so both OTEL gates were re-read by name from the new head's logs —
+postgres `94073971396` (`passed=88 failed=0 skipped=0`), sqlite `94073971501`
+(`passed=83 failed=0 skipped=0`). These job ids differ from the pre-sync pair, which is the concrete
+proof the earlier citation would have described a head no longer on the PR. The body transform takes
+head and job ids as required arguments and asserts no pre-sync reference survives.
+
+**Pre-merge gate:** `slices/pre-merge-gate-1536.md`, all seven checks PASS with named sources, plus
+`review-threads` → `PASS threads=0 unanswered=0`. Merged squash → `d7e2b67b2`; #1398 auto-closed
+`COMPLETED`; `status:shipped` on both.
+
+**Both owned issues are now on `main`.** #1405 → `8ff1bcb8f`; #1398 → `d7e2b67b2`.
]