mercury-agent 0.23.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/container/Dockerfile +1 -1
  2. package/container/Dockerfile.base +1 -1
  3. package/container/agent-package.json +1 -1
  4. package/docs/configuration.md +60 -0
  5. package/docs/container-lifecycle.md +27 -15
  6. package/docs/extensions.md +36 -0
  7. package/docs/goals/rehearsal-bench/roadmap.md +1 -1
  8. package/docs/goals/release-gate/roadmap.md +8 -8
  9. package/docs/goals/whatsapp-bot-hardening/open-threads.md +18 -0
  10. package/docs/goals/whatsapp-bot-hardening/roadmap.md +1 -1
  11. package/docs/graceful-shutdown.md +21 -5
  12. package/docs/pending-verification.md +195 -4
  13. package/docs/plans/{bugs-0915 → archive/2026-09-15-bugs-0915}/decisions.md +1 -0
  14. package/docs/plans/{bugs-0915 → archive/2026-09-15-bugs-0915}/plan.md +2 -2
  15. package/docs/plans/archive/2026-09-15-bugs-0915/sessions/S1.md +56 -0
  16. package/docs/plans/archive/2026-09-15-bugs-0915/sessions/S2.md +51 -0
  17. package/docs/plans/archive/2026-09-15-bugs-0915/sessions/S3.md +60 -0
  18. package/docs/plans/archive/2026-09-15-bugs-0915/sessions/S4.md +46 -0
  19. package/docs/plans/archive/2026-09-22-p0-bugs-0922/briefs/S1.md +60 -0
  20. package/docs/plans/archive/2026-09-22-p0-bugs-0922/briefs/S2.md +60 -0
  21. package/docs/plans/archive/2026-09-22-p0-bugs-0922/briefs/S3.md +58 -0
  22. package/docs/plans/archive/2026-09-22-p0-bugs-0922/briefs/S4.md +58 -0
  23. package/docs/plans/archive/2026-09-22-p0-bugs-0922/decisions.md +8 -0
  24. package/docs/plans/archive/2026-09-22-p0-bugs-0922/plan.md +102 -0
  25. package/docs/plans/archive/2026-09-22-p0-bugs-0922/sessions/S1.md +29 -0
  26. package/docs/plans/archive/2026-09-22-p0-bugs-0922/sessions/S2.md +29 -0
  27. package/docs/plans/archive/2026-09-22-p0-bugs-0922/sessions/S3.md +30 -0
  28. package/docs/plans/archive/2026-09-22-p0-bugs-0922/sessions/S4.md +32 -0
  29. package/docs/plans/bench-patterns-0915/briefs/S10.md +72 -0
  30. package/docs/plans/bench-patterns-0915/briefs/S11.md +95 -0
  31. package/docs/plans/bench-patterns-0915/briefs/S12.md +87 -0
  32. package/docs/plans/bench-patterns-0915/briefs/S9.md +81 -0
  33. package/docs/plans/bench-patterns-0915/decisions.md +7 -1
  34. package/docs/plans/bench-patterns-0915/plan.md +92 -8
  35. package/docs/plans/bench-patterns-0915/sessions/S10.md +40 -0
  36. package/docs/plans/bench-patterns-0915/sessions/S11.md +53 -0
  37. package/docs/plans/bench-patterns-0915/sessions/S12.md +44 -0
  38. package/docs/plans/bench-patterns-0915/sessions/S4.md +29 -0
  39. package/docs/plans/bench-patterns-0915/sessions/S5.md +30 -0
  40. package/docs/plans/bench-patterns-0915/sessions/S6.md +46 -0
  41. package/docs/plans/bench-patterns-0915/sessions/S7.md +30 -0
  42. package/docs/plans/bench-patterns-0915/sessions/S8.md +30 -0
  43. package/docs/plans/bench-patterns-0915/sessions/S9.md +2 -0
  44. package/docs/plans/flat-0916/briefs/S1.md +55 -0
  45. package/docs/plans/flat-0916/briefs/S2.md +55 -0
  46. package/docs/plans/flat-0916/decisions.md +8 -0
  47. package/docs/plans/flat-0916/plan.md +88 -0
  48. package/docs/plans/p0-plan-0922/briefs/S1.md +60 -0
  49. package/docs/plans/p0-plan-0922/briefs/S2.md +54 -0
  50. package/docs/plans/p0-plan-0922/briefs/S3.md +58 -0
  51. package/docs/plans/p0-plan-0922/briefs/S4.md +54 -0
  52. package/docs/plans/p0-plan-0922/briefs/S5.md +79 -0
  53. package/docs/plans/p0-plan-0922/briefs/S6.md +77 -0
  54. package/docs/plans/p0-plan-0922/briefs/S7.md +79 -0
  55. package/docs/plans/p0-plan-0922/briefs/S8.md +69 -0
  56. package/docs/plans/p0-plan-0922/decisions.md +9 -0
  57. package/docs/plans/p0-plan-0922/plan.md +215 -0
  58. package/docs/plans/p0-plan-0922/sessions/S1.md +22 -0
  59. package/docs/plans/p0-plan-0922/sessions/S2.md +22 -0
  60. package/docs/plans/p0-plan-0922/sessions/S3.md +22 -0
  61. package/docs/plans/p0-plan-0922/sessions/S4.md +22 -0
  62. package/docs/plans/p0-plan-0922/sessions/S5.md +44 -0
  63. package/docs/plans/p0-plan-0922/sessions/S6.md +60 -0
  64. package/docs/plans/p0-plan-0922/sessions/S7.md +69 -0
  65. package/docs/plans/sensitive-bugs-0922/briefs/S1.md +63 -0
  66. package/docs/plans/sensitive-bugs-0922/briefs/S2.md +62 -0
  67. package/docs/plans/sensitive-bugs-0922/briefs/S3.md +61 -0
  68. package/docs/plans/sensitive-bugs-0922/briefs/S4.md +61 -0
  69. package/docs/plans/sensitive-bugs-0922/briefs/S5.md +62 -0
  70. package/docs/plans/sensitive-bugs-0922/decisions.md +9 -0
  71. package/docs/plans/sensitive-bugs-0922/plan.md +147 -0
  72. package/docs/release-gate.md +1 -1
  73. package/docs/smoke-fixtures.md +9 -0
  74. package/examples/extensions/feed-watch/fixtures.ts +210 -0
  75. package/examples/extensions/feed-watch/index.ts +140 -9
  76. package/examples/extensions/office-365-mail/README.md +132 -0
  77. package/examples/extensions/office-365-mail/index.ts +170 -0
  78. package/examples/extensions/office-365-mail/lib/graph.ts +1031 -0
  79. package/examples/extensions/office-365-mail/scripts/login.ts +252 -0
  80. package/examples/extensions/office-365-mail/skill/SKILL.md +94 -0
  81. package/examples/profiles/football-reporter/AGENTS.md +15 -2
  82. package/examples/profiles/football-reporter/config.yaml +5 -1
  83. package/examples/profiles/tel-aviv-flat-search/AGENTS.md +5 -1
  84. package/package.json +10 -6
  85. package/resources/pi-extensions/subagent/index.ts +38 -42
  86. package/resources/pi-extensions/subagent/outcome.ts +28 -0
  87. package/src/adapters/whatsapp-thread.ts +12 -0
  88. package/src/agent/container-entry.ts +40 -7
  89. package/src/agent/container-env.ts +26 -0
  90. package/src/agent/container-error.ts +28 -0
  91. package/src/agent/container-runner.ts +50 -14
  92. package/src/audit/ledger-queries.ts +35 -3
  93. package/src/bridges/slack.ts +4 -1
  94. package/src/bridges/teams.ts +55 -15
  95. package/src/bridges/telegram.ts +3 -1
  96. package/src/cli/mercury.ts +46 -0
  97. package/src/cli/mrctl-http.ts +25 -0
  98. package/src/cli/mrctl.ts +13 -14
  99. package/src/config.ts +70 -34
  100. package/src/core/api.ts +51 -29
  101. package/src/core/boot-reconcile.ts +168 -0
  102. package/src/core/chat-run-journal.ts +383 -0
  103. package/src/core/debounce.ts +97 -1
  104. package/src/core/handler.ts +292 -44
  105. package/src/core/instance-lock.ts +159 -0
  106. package/src/core/operator-alerts.ts +80 -10
  107. package/src/core/pending-ingress.ts +298 -0
  108. package/src/core/routes/dashboard.ts +28 -6
  109. package/src/core/routes/media.ts +22 -0
  110. package/src/core/routes/shadow.ts +28 -3
  111. package/src/core/runtime.ts +139 -4
  112. package/src/core/simulate-ingress.ts +5 -2
  113. package/src/core/space-queue.ts +38 -9
  114. package/src/core/storage-cleanup.ts +16 -8
  115. package/src/core/system-messages.ts +50 -0
  116. package/src/core/task-scheduler.ts +150 -14
  117. package/src/dashboard/index.html +2 -0
  118. package/src/main.ts +121 -2
  119. package/src/ops/shadow-service.ts +113 -1
  120. package/src/ops/shadow-snapshot.ts +126 -0
  121. package/src/preflight/checks/roundtrip.ts +50 -39
  122. package/src/smoke/cli.ts +24 -4
  123. package/src/storage/db.ts +613 -89
  124. package/src/types.ts +20 -3
  125. /package/docs/plans/{bugs-0915 → archive/2026-09-15-bugs-0915}/briefs/S1.md +0 -0
  126. /package/docs/plans/{bugs-0915 → archive/2026-09-15-bugs-0915}/briefs/S2.md +0 -0
  127. /package/docs/plans/{bugs-0915 → archive/2026-09-15-bugs-0915}/briefs/S3.md +0 -0
  128. /package/docs/plans/{bugs-0915 → archive/2026-09-15-bugs-0915}/briefs/S4.md +0 -0
@@ -0,0 +1,60 @@
1
+ ## Launched — 2026-09-24T12:35Z
2
+ Branch: worktree-feature-ledger-misses-interrupted-runs
3
+
4
+ ## Report — 2026-09-24T15:52Z
5
+ Outcome: delivered
6
+ Delivered: —
7
+ Blocked-on: —
8
+ Deploy needs: rebuild + schema migration — container/build.sh (src/types.ts is baked), then restart in a proven gap; first boot rebuilds task_runs (finished_at nullable). Rehearse on a shadow first (release-gate gate 2), paste the §4 evidence block. Rollback: DELETE FROM task_runs WHERE finished_at IS NULL before starting an older build.
9
+ Files changed:
10
+ - docs/ARCHITECTURE.md
11
+ - docs/archive/ops/2026-09-24-ledger-misses-interrupted-runs.md
12
+ - docs/configuration.md
13
+ - docs/graceful-shutdown.md
14
+ - docs/pending-updates/ledger-misses-interrupted-runs.md
15
+ - docs/pending-verification.md
16
+ - scripts/football-audit.ts
17
+ - src/audit/ledger-queries.ts
18
+ - src/core/boot-reconcile.ts
19
+ - src/core/operator-alerts.ts
20
+ - src/core/routes/dashboard.ts
21
+ - src/core/runtime.ts
22
+ - src/core/system-messages.ts
23
+ - src/core/task-scheduler.ts
24
+ - src/dashboard/index.html
25
+ - src/main.ts
26
+ - src/storage/db.ts
27
+ - src/types.ts
28
+ - tests/api.test.ts
29
+ - tests/boot-reconcile.test.ts
30
+ - tests/container-hooks-carry-source.test.ts
31
+ - tests/dashboard-task-last-run.test.ts
32
+ - tests/ledger-queries.test.ts
33
+ - tests/scheduled-post-platform-id.test.ts
34
+ - tests/scheduler-heartbeat.test.ts
35
+ - tests/scheduler-interrupted-shutdown.test.ts
36
+ - tests/scheduler-missed-runs.test.ts
37
+ - tests/scheduler-noop-not-persisted.test.ts
38
+ - tests/task-run-ledger.test.ts
39
+ - tests/task-runs-migration.test.ts
40
+ Siblings must know:
41
+ - Seam, as in the code (`src/core/boot-reconcile.ts`): exports `BootReconcileContext {db, bootAt}`, `InterruptedWork {kind, id, spaceId, startedAt, detail}`, `BootReconciler {name, reconcile(ctx): InterruptedWork[]}` (sync, DB-only, idempotent, only `startedAt < ctx.bootAt`), `BootReconcileReport {bootAt, items, failed, byKind(kind)}`, `runBootReconciliation(reconcilers, ctx)`, `BOOT_RECONCILERS` (today `[taskRunReconciler]`), `TASK_RUN_KIND = "task_run"`, `InterruptedTaskRunDetail {taskId, manual, attempt}`. S7 appends its `chat_run` reconciler to `BOOT_RECONCILERS` — never a second sweep.
42
+ - `main()`: `const bootAt = Date.now();` is the FIRST statement; `const bootReport = runBootReconciliation(BOOT_RECONCILERS, { db: core.db, bootAt });` runs immediately before `core.startScheduler(messageSender, bootReport)`, which is before every adapter `initialize`. A structural test (`tests/boot-reconcile.test.ts`) pins both — S7 moving or wrapping the call must keep it green.
43
+ - The report is held on the runtime as `core.bootReconcileReport` (null until startScheduler). A consumer that notifies (S7's "your request was interrupted") reads `bootReconcileReport.byKind("chat_run")` from its own post-adapter step. The scheduler does NOT consume the report: it reads each task's newest `task_runs` row (so the graceful leg is covered too).
44
+ - Per-item log line `Reconciled interrupted work` spreads `detail` first, then kind/id/spaceId/startedAt/ageMs; summary `Boot reconciliation finished` with `interrupted: {<reconciler name>: n}` prints even at zero; a throwing reconciler logs `Boot reconciliation failed` and lands in `report.failed`.
45
+ - `src/core/system-messages.ts` (S7's file) gained 8 additive keys `alert_interrupted_*` / `alert_missed_runs_line_interrupted_*` in en + he (owner-approved 2026-09-24). No existing string changed; S7 rebases onto it.
46
+ - `TaskScheduler.stop()` now sets a `stopping` flag: a handler failure after it is ledgered `interrupted` (no retry/notice/schedule change), the in-flight tick starts no further due task and does not re-arm, and `triggerTask` returns `unavailable`. `start()` clears it.
47
+ - Tests that waited on "a task_runs row exists" as the finish signal must wait for `status !== "running"` — rows are now written at start.
48
+ --- final report (verbatim) ---
49
+ ~~~
50
+ S6 ledger-misses-interrupted-runs — squashed to LOCAL dev as 4e497de, NOT pushed (owner approval required per CLAUDE.local.md; landing derives once it is on origin/dev).
51
+ - task_runs rows are written 'running' at start and closed by id at the end; a run that never ends becomes 'interrupted' — by the scheduler's graceful leg (stop() then a failure: no retry, no "task failed", no heartbeat, next_run_at left on the slot) or by the boot reconciliation (hard kill; finished_at NULL).
52
+ - Migration: finished_at loses NOT NULL via a one-transaction rebuild from the single TASK_RUNS_COLUMNS constant; ids and the AUTOINCREMENT high-water mark kept; failure rolls back and refuses to open. Rehearsed on a VACUUM INTO copy of the live DB: 1,209 rows, id/cost sums identical, seq 1243, 10 ms.
53
+ - Seam src/core/boot-reconcile.ts wired in main() before startScheduler (bootAt first statement); report held on the runtime for S7.
54
+ - Missed-run boot pass relabels an interrupted slot (from the task's newest ledger row); catch_up policy unchanged; the graceful leg now matches the hard kill, so catch_up: once makes up after a graceful restart. Creator's report reworded, en + he (8 new message keys in system-messages.ts — owner-approved scope growth into S7's file).
55
+ - Readers: dashboard running/interrupted badges + null-safe duration; outcomeMix.interrupted; running rows out of every count, interrupted rows out of drift/prefix; football-audit column (run against a fixture).
56
+ - Docs: configuration.md "Interrupted is not missed", graceful-shutdown.md step 1, ARCHITECTURE invariant, 5 MANUAL-VERIFY lines under "## Plan p0-plan-0922 / S6".
57
+ - Gate after rebase onto dev: 12/12 links, 5928 pass / 9 skip / 0 fail. Every new guard negative-tested (deleted → red). Code review: 1 real bug (graceful slot still labelled missed) fixed + 2 reader fixes + trigger-while-stopping; 1 accepted residual. QA: 29/34 pass, 5 deferred to the box, 0 fail.
58
+ - Out-of-list files: system-messages.ts (approved), tests/api.test.ts (S5's, landed), src/dashboard/index.html (2 badge colours), 4 tests switched to wait for a closed row.
59
+ - PLAN-PASSED(overlap): none — preflight overlap check was clean (exit 0).
60
+ ~~~
@@ -0,0 +1,69 @@
1
+ ## Launched — 2026-09-24T23:22Z
2
+ Branch: worktree-feature-orphaned-run-recovery
3
+
4
+ ## Report — 2026-09-24T23:51Z
5
+ Outcome: delivered
6
+ Delivered: local — squashed into dev as b7b33e2, NOT pushed (owner chose merge-locally)
7
+ Blocked-on: —
8
+ Deploy needs: restart
9
+ Files changed:
10
+ - docs/ARCHITECTURE.md
11
+ - docs/archive/2026-09-25-orphaned-run-recovery.md
12
+ - docs/configuration.md
13
+ - docs/graceful-shutdown.md
14
+ - docs/ideas/stopped-scheduled-run-is-not-retried.md
15
+ - docs/ideas/whatsapp-outbound-queue-survives-restart.md
16
+ - docs/pending-updates/orphaned-run-recovery.md
17
+ - docs/pending-verification.md
18
+ - src/agent/container-error.ts
19
+ - src/agent/container-runner.ts
20
+ - src/core/boot-reconcile.ts
21
+ - src/core/chat-run-journal.ts
22
+ - src/core/handler.ts
23
+ - src/core/instance-lock.ts
24
+ - src/core/runtime.ts
25
+ - src/core/space-queue.ts
26
+ - src/core/system-messages.ts
27
+ - src/main.ts
28
+ - src/storage/db.ts
29
+ - tests/ambient-group-context.test.ts
30
+ - tests/chat-run-journal.test.ts
31
+ - tests/container-error.test.ts
32
+ - tests/handler.test.ts
33
+ - tests/instance-lock.test.ts
34
+ - tests/orphaned-run-recovery.e2e.test.ts
35
+ - tests/reply-to-member-does-not-trigger.test.ts
36
+ - tests/shutdown.test.ts
37
+ - tests/simulate-ingress.test.ts
38
+ - tests/space-queue.test.ts
39
+ - tests/system-messages.test.ts
40
+ Siblings must know:
41
+ - `src/core/handler.ts` (S8 shares it): `runIngress` now wraps a new `runTracked(ingress, run)`; it owns a `ChatRunHandle` and closes the row in `finally` unless the settle type is `"interrupted"`. The heartbeat records the new ⏳ id (`core.noteChatRunStatus`) BEFORE deleting the old one. Any fake core handed to `createMessageHandler` needs `noteChatRunStatus` and `closeChatRun` (no-ops are fine) — without them the handler's outer catch swallows a TypeError and tests stay green while the path is broken.
42
+ - `SpaceQueue.cancelAll(makeError = ContainerError.shutdown)` and `cancelPending(spaceId, makeError = ContainerError.cancelled)` now REJECT every dropped entry. A caller that enqueues must handle the rejection. `ContainerFailureReason` gained `"shutdown"` and `"cancelled"`; the runtime rethrows `shutdown` and maps `cancelled` to `{type:"ignore"}`.
43
+ - `BOOT_RECONCILERS` is now `[taskRunReconciler, chatRunReconciler]`; `main()` calls `deliverInterruptedChatRuns` (not awaited) after the adapter initialize loop. S8's replay-on-boot, if it joins the seam, appends a third reconciler and keeps delivery after the adapters.
44
+ - `main()` takes `acquireInstanceLock` right after `configureLogger` (exit 78 on refusal); `tests/instance-lock.test.ts` pins it before `Bun.serve`, `core.initialize` and `runBootReconciliation`.
45
+ - Follow-ups filed: `docs/ideas/stopped-scheduled-run-is-not-retried.md` (task-scheduler lane), `docs/ideas/whatsapp-outbound-queue-survives-restart.md`.
46
+ --- final report (verbatim) ---
47
+ ~~~
48
+ orphaned-run-recovery — implemented, reviewed, QA'd, archived, squash-merged into local dev as b7b33e2 (not pushed).
49
+
50
+ - Single-instance lock per data dir (src/core/instance-lock.ts), first in main(); a second process exits 78 naming the holder.
51
+ - chat_runs journal (src/storage/db.ts) opened at enqueue, closed by the handler on every path except a shutdown interruption.
52
+ - ContainerError.shutdown/.cancelled; SpaceQueue cancels now settle their promises; killAll records "shutdown".
53
+ - chat_run reconciler on the boot seam + deliverInterruptedChatRuns after the adapters (readiness-bounded, per-thread notice, ⏳ delete).
54
+ - run_interrupted_restart(_many) en + he; docs: graceful-shutdown, configuration, ARCHITECTURE.
55
+ - Gate: bun run check — 16/16 links PASS, 6088 pass / 9 skip / 0 fail (before the review fix; targeted suites green after it).
56
+ - Session review: 1 fix (⏳ recorded before the old one is deleted); 2 accepted residuals. QA: 30/30 PASS, 4 DEFERRED to the live bot (pending-verification.md, "Plan p0-plan-0922 / S7").
57
+ - Spec: docs/archive/2026-09-25-orphaned-run-recovery.md; docs/pending-updates/orphaned-run-recovery.md written (run /f-doc-sync); ARCHITECTURE.md updated.
58
+ - Not done: step 8 shadow rehearsal, deploy and live check — restart-only, in a gap, owner's go-ahead.
59
+ ~~~
60
+
61
+ ## Report — 2026-09-25T18:25Z
62
+ Outcome: delivered
63
+ Delivered: pushed — dev (b7b33e2 + shadow/deploy/verify docs through d55f31e)
64
+ Blocked-on: —
65
+ Deploy needs: none — deployed 2026-09-25 03:42:55 IDT (restart-only, gap), all 4 MANUAL-VERIFY lines ticked
66
+ --- final report (verbatim) ---
67
+ ~~~
68
+ S7 deployed and verified live. Shadow rehearsal (graceful + SIGKILL legs) passed; live second instance exits 78 naming the holder; live restart mid-answer with two queued messages produced exactly one "please send them again" notice (count=2), ⏳ deleted, chat_runs empty — owner confirmed on the phone. §4 evidence in docs/archive/2026-09-25-orphaned-run-recovery.md. Open: docs/pending-updates/orphaned-run-recovery.md awaits /f-doc-sync.
69
+ ~~~
@@ -0,0 +1,63 @@
1
+ # Brief S1 — the sensitive-guard replay runs with the yes message's reply metadata
2
+
3
+ **Plan**: docs/plans/sensitive-bugs-0922/plan.md **Session**: S1 **Round**: 1 **Kind**: bug
4
+ **Item slug**: sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata
5
+
6
+ > The only file this session reads from the plan. Self-contained: it never points at a sibling's brief. The `**Item slug**:` line is the key the execution commands grep for to detect that a brief is active — keep it exactly in that shape.
7
+
8
+ ## Launch
9
+
10
+ /f-ops-plan work sensitive-bugs-0922 S1
11
+
12
+ ## Run
13
+
14
+ /f-bug-fix sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata
15
+
16
+ ## Scope notes
17
+
18
+ Store the **blocked turn**, not the `yes`: extend `PendingSensitiveEntry` with `replyMeta` + `replyFlags`, and replay them. Build the stored shape so S2 can add `droppedAttachments` to it without reshaping it. Name the type and the reader (`readPendingSensitive`) in your log's **Siblings must know**. Every new stored field gets the refuse-whole validation that `readAttachmentRefs` applies, because `src/core/routes/console.ts` writes `space_config` keys unvalidated. A pre-fix row replays with the metadata **absent**, never borrowed from the `yes`. Do **not** add the dropped-attachment note here: that is S2's bug.
19
+
20
+ **Read first:** `docs/archive/permissions-and-config/2026-09-22-sensitive-guard-one-read.md` § *Legs × axes*. It maps every leg and axis of this guard. A fix that is right for its own cell and blind to the next one is how seven post-mortems happened. **Deploy needs: restart** (`src/core/runtime.ts` and the tests are not baked). Confirm with the release-gate §1 classification.
21
+
22
+ ## Owned surfaces (predicted)
23
+
24
+ - src/core/routes/console.ts
25
+ - src/core/runtime.ts
26
+ - tests/sensitive-guard-replay.test.ts
27
+
28
+ ## Do not touch
29
+
30
+ - tests/sensitive-guard-audience.test.ts (owned by S3 this plan)
31
+ - tests/sensitive-guard-audience.test.ts (owned by S4 this plan)
32
+ - tests/api-chat-auth.test.ts, tests/sensitive-guard-audience.test.ts, tests/sensitive-guard-scope.test.ts (owned by S5 this plan)
33
+ - `src/core/runtime.ts` is shared by all five sessions here. Change only the code your bug names; the other guard bugs are sibling sessions.
34
+ - Not ours (avishai tsabari / Avishai-Tsabari, last 14 d): `src/core/runtime.ts`: see plan.md "Not ours". The lane is cleared by Q1 (ours). Re-check with `/f-ops-plan next sensitive-bugs-0922`.
35
+
36
+ ## Edges
37
+
38
+ - start-after: none (satisfied — `next` launched you)
39
+ - merge-after: none
40
+
41
+ ## Overlap verdicts
42
+
43
+ none
44
+
45
+ > The command's preflight overlap check treats a hit whose branch AND every shared file match a verdict line as answered (c), prints one `PLAN-PASSED(overlap): …` line, and carries it into the final report. Any other hit prompts as usual. The merge-time check is never waived.
46
+
47
+ ## Decisions answered
48
+
49
+ - Q1: ours (owner, 2026-09-22). `src/core/runtime.ts` is this plan's lane.
50
+
51
+ ## Ledger rules
52
+
53
+ - `docs/ROADMAP.md`: never edit. Your command writes `docs/pending-updates/sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata.md` as usual.
54
+ - `docs/pending-verification.md`: append only under `## Plan sensitive-bugs-0922 / S1`.
55
+ - Any goal's `decisions.md`: never edit; put decisions in your log's "Siblings must know".
56
+
57
+ ## Environment
58
+
59
+ - Read `CLAUDE.md` → Worktree Setup and Gotchas before opening the worktree. Nothing from them is copied here. `bun install` once in the fresh worktree; the check gate is `bun run check` (every link runs to completion — read every line).
60
+
61
+ ## Report
62
+
63
+ Your command's final report is appended to `docs/plans/sensitive-bugs-0922/sessions/S1.md` by its report step (path resolved through `.claude/scripts/resolve-docs-root.sh docs/plans/probe.md`). `work` verifies the append.
@@ -0,0 +1,62 @@
1
+ # Brief S2 — the sensitive-guard replay loses the dropped-attachment note
2
+
3
+ **Plan**: docs/plans/sensitive-bugs-0922/plan.md **Session**: S2 **Round**: 2 **Kind**: bug
4
+ **Item slug**: sensitive-guard-replay-loses-the-dropped-attachment-note
5
+
6
+ > The only file this session reads from the plan. Self-contained: it never points at a sibling's brief. The `**Item slug**:` line is the key the execution commands grep for to detect that a brief is active — keep it exactly in that shape.
7
+
8
+ ## Launch
9
+
10
+ /f-ops-plan work sensitive-bugs-0922 S2
11
+
12
+ ## Run
13
+
14
+ /f-bug-fix sensitive-guard-replay-loses-the-dropped-attachment-note
15
+
16
+ ## Scope notes
17
+
18
+ S1 has landed the stored blocked-turn shape. Read it from the code on `dev` and from S1's log **Siblings must know**, then add `droppedAttachments` (a validated `DroppedAttachment[]`, refuse-whole like `readAttachmentRefs`). Merge it into `dropNote` at the replay call site. A row stored before this fix replays with no note, and does not crash.
19
+
20
+ **Read first:** `docs/archive/permissions-and-config/2026-09-22-sensitive-guard-one-read.md` § *Legs × axes*. It maps every leg and axis of this guard. A fix that is right for its own cell and blind to the next one is how seven post-mortems happened. **Deploy needs: restart** (`src/core/runtime.ts` and the tests are not baked). Confirm with the release-gate §1 classification.
21
+
22
+ ## Owned surfaces (predicted)
23
+
24
+ - src/core/runtime.ts
25
+
26
+ ## Do not touch
27
+
28
+ - src/core/routes/console.ts, tests/sensitive-guard-replay.test.ts (owned by S1 this plan)
29
+ - tests/sensitive-guard-audience.test.ts (owned by S3 this plan)
30
+ - tests/sensitive-guard-audience.test.ts (owned by S4 this plan)
31
+ - tests/api-chat-auth.test.ts, tests/sensitive-guard-audience.test.ts, tests/sensitive-guard-scope.test.ts (owned by S5 this plan)
32
+ - `src/core/runtime.ts` is shared by all five sessions here. Change only the code your bug names; the other guard bugs are sibling sessions.
33
+ - Not ours (avishai tsabari / Avishai-Tsabari, last 14 d): `src/core/runtime.ts`: see plan.md "Not ours". The lane is cleared by Q1 (ours). Re-check with `/f-ops-plan next sensitive-bugs-0922`.
34
+
35
+ ## Edges
36
+
37
+ - start-after: none (satisfied — `next` launched you)
38
+ - merge-after: land only after S1 is landed (it will be: S1 is also a start-after predecessor). Check with `/f-ops-plan next sensitive-bugs-0922` before the rebase step.
39
+
40
+ ## Overlap verdicts
41
+
42
+ none
43
+
44
+ > The command's preflight overlap check treats a hit whose branch AND every shared file match a verdict line as answered (c), prints one `PLAN-PASSED(overlap): …` line, and carries it into the final report. Any other hit prompts as usual. The merge-time check is never waived.
45
+
46
+ ## Decisions answered
47
+
48
+ - Q1: ours (owner, 2026-09-22). `src/core/runtime.ts` is this plan's lane.
49
+
50
+ ## Ledger rules
51
+
52
+ - `docs/ROADMAP.md`: never edit. Your command writes `docs/pending-updates/sensitive-guard-replay-loses-the-dropped-attachment-note.md` as usual.
53
+ - `docs/pending-verification.md`: append only under `## Plan sensitive-bugs-0922 / S2`.
54
+ - Any goal's `decisions.md`: never edit; put decisions in your log's "Siblings must know".
55
+
56
+ ## Environment
57
+
58
+ - Read `CLAUDE.md` → Worktree Setup and Gotchas before opening the worktree. Nothing from them is copied here. `bun install` once in the fresh worktree; the check gate is `bun run check` (every link runs to completion — read every line).
59
+
60
+ ## Report
61
+
62
+ Your command's final report is appended to `docs/plans/sensitive-bugs-0922/sessions/S2.md` by its report step (path resolved through `.claude/scripts/resolve-docs-root.sh docs/plans/probe.md`). `work` verifies the append.
@@ -0,0 +1,61 @@
1
+ # Brief S3 — the sensitive gate is re-checked when the queued turn runs
2
+
3
+ **Plan**: docs/plans/sensitive-bugs-0922/plan.md **Session**: S3 **Round**: 3 **Kind**: bug
4
+ **Item slug**: sensitive-gate-is-checked-before-the-space-queue
5
+
6
+ > The only file this session reads from the plan. Self-contained: it never points at a sibling's brief. The `**Item slug**:` line is the key the execution commands grep for to detect that a brief is active — keep it exactly in that shape.
7
+
8
+ ## Launch
9
+
10
+ /f-ops-plan work sensitive-bugs-0922 S3
11
+
12
+ ## Run
13
+
14
+ /f-bug-fix sensitive-gate-is-checked-before-the-space-queue
15
+
16
+ ## Scope notes
17
+
18
+ Add a re-check at dequeue, inside the `queue.enqueue` callback in `executePrompt`. It covers the space policy (`security.sensitive_connections_allowed`) and, for interactive turns, the caller's consent. What admitted the turn (the consent key, or "policy only" for a scheduled run) is passed to it **as a literal from the admitting leg**, never re-derived at dequeue. **Severity is an open owner question** (`moderate` now; `major` if `allowed=false` should cut off in-flight work). It does not change the fix. This fix unblocks the deferred `sensitive-opt-out-should-deny-not-block` idea: name the dequeue hook in your log's **Siblings must know**.
19
+
20
+ **Read first:** `docs/archive/permissions-and-config/2026-09-22-sensitive-guard-one-read.md` § *Legs × axes*. It maps every leg and axis of this guard. A fix that is right for its own cell and blind to the next one is how seven post-mortems happened. **Deploy needs: restart** (`src/core/runtime.ts` and the tests are not baked). Confirm with the release-gate §1 classification.
21
+
22
+ ## Owned surfaces (predicted)
23
+
24
+ - src/core/runtime.ts
25
+ - tests/sensitive-guard-audience.test.ts
26
+
27
+ ## Do not touch
28
+
29
+ - src/core/routes/console.ts, tests/sensitive-guard-replay.test.ts (owned by S1 this plan)
30
+ - tests/api-chat-auth.test.ts, tests/sensitive-guard-scope.test.ts (owned by S5 this plan)
31
+ - `src/core/runtime.ts` is shared by all five sessions here. Change only the code your bug names; the other guard bugs are sibling sessions.
32
+ - Not ours (avishai tsabari / Avishai-Tsabari, last 14 d): `src/core/runtime.ts`: see plan.md "Not ours". The lane is cleared by Q1 (ours). Re-check with `/f-ops-plan next sensitive-bugs-0922`.
33
+
34
+ ## Edges
35
+
36
+ - start-after: none (satisfied — `next` launched you)
37
+ - merge-after: none
38
+
39
+ ## Overlap verdicts
40
+
41
+ none
42
+
43
+ > The command's preflight overlap check treats a hit whose branch AND every shared file match a verdict line as answered (c), prints one `PLAN-PASSED(overlap): …` line, and carries it into the final report. Any other hit prompts as usual. The merge-time check is never waived.
44
+
45
+ ## Decisions answered
46
+
47
+ - Q1: ours (owner, 2026-09-22). `src/core/runtime.ts` is this plan's lane.
48
+
49
+ ## Ledger rules
50
+
51
+ - `docs/ROADMAP.md`: never edit. Your command writes `docs/pending-updates/sensitive-gate-is-checked-before-the-space-queue.md` as usual.
52
+ - `docs/pending-verification.md`: append only under `## Plan sensitive-bugs-0922 / S3`.
53
+ - Any goal's `decisions.md`: never edit; put decisions in your log's "Siblings must know".
54
+
55
+ ## Environment
56
+
57
+ - Read `CLAUDE.md` → Worktree Setup and Gotchas before opening the worktree. Nothing from them is copied here. `bun install` once in the fresh worktree; the check gate is `bun run check` (every link runs to completion — read every line).
58
+
59
+ ## Report
60
+
61
+ Your command's final report is appended to `docs/plans/sensitive-bugs-0922/sessions/S3.md` by its report step (path resolved through `.claude/scripts/resolve-docs-root.sh docs/plans/probe.md`). `work` verifies the append.
@@ -0,0 +1,61 @@
1
+ # Brief S4 — a scheduled run skipped by the sensitive deny gets a reader
2
+
3
+ **Plan**: docs/plans/sensitive-bugs-0922/plan.md **Session**: S4 **Round**: 4 **Kind**: bug
4
+ **Item slug**: scheduled-run-skipped-by-sensitive-deny-has-no-reader
5
+
6
+ > The only file this session reads from the plan. Self-contained: it never points at a sibling's brief. The `**Item slug**:` line is the key the execution commands grep for to detect that a brief is active — keep it exactly in that shape.
7
+
8
+ ## Launch
9
+
10
+ /f-ops-plan work sensitive-bugs-0922 S4
11
+
12
+ ## Run
13
+
14
+ /f-bug-fix scheduled-run-skipped-by-sensitive-deny-has-no-reader
15
+
16
+ ## Scope notes
17
+
18
+ Send one operator notice when the scheduler gate skips a run, through the `announceTaskFailure` channel **with its own label or kind**, because a block is not a failure (the 2026-09-07 post-mortem's reason). S3 has moved or duplicated the scheduler policy check to dequeue. Send the notice from whichever point now actually skips the run, once and not twice.
19
+
20
+ **Read first:** `docs/archive/permissions-and-config/2026-09-22-sensitive-guard-one-read.md` § *Legs × axes*. It maps every leg and axis of this guard. A fix that is right for its own cell and blind to the next one is how seven post-mortems happened. **Deploy needs: restart** (`src/core/runtime.ts` and the tests are not baked). Confirm with the release-gate §1 classification.
21
+
22
+ ## Owned surfaces (predicted)
23
+
24
+ - src/core/runtime.ts
25
+ - tests/sensitive-guard-audience.test.ts
26
+
27
+ ## Do not touch
28
+
29
+ - src/core/routes/console.ts, tests/sensitive-guard-replay.test.ts (owned by S1 this plan)
30
+ - tests/api-chat-auth.test.ts, tests/sensitive-guard-scope.test.ts (owned by S5 this plan)
31
+ - `src/core/runtime.ts` is shared by all five sessions here. Change only the code your bug names; the other guard bugs are sibling sessions.
32
+ - Not ours (avishai tsabari / Avishai-Tsabari, last 14 d): `src/core/runtime.ts`: see plan.md "Not ours". The lane is cleared by Q1 (ours). Re-check with `/f-ops-plan next sensitive-bugs-0922`.
33
+
34
+ ## Edges
35
+
36
+ - start-after: none (satisfied — `next` launched you)
37
+ - merge-after: none
38
+
39
+ ## Overlap verdicts
40
+
41
+ none
42
+
43
+ > The command's preflight overlap check treats a hit whose branch AND every shared file match a verdict line as answered (c), prints one `PLAN-PASSED(overlap): …` line, and carries it into the final report. Any other hit prompts as usual. The merge-time check is never waived.
44
+
45
+ ## Decisions answered
46
+
47
+ - Q1: ours (owner, 2026-09-22). `src/core/runtime.ts` is this plan's lane.
48
+
49
+ ## Ledger rules
50
+
51
+ - `docs/ROADMAP.md`: never edit. Your command writes `docs/pending-updates/scheduled-run-skipped-by-sensitive-deny-has-no-reader.md` as usual.
52
+ - `docs/pending-verification.md`: append only under `## Plan sensitive-bugs-0922 / S4`.
53
+ - Any goal's `decisions.md`: never edit; put decisions in your log's "Siblings must know".
54
+
55
+ ## Environment
56
+
57
+ - Read `CLAUDE.md` → Worktree Setup and Gotchas before opening the worktree. Nothing from them is copied here. `bun install` once in the fresh worktree; the check gate is `bun run check` (every link runs to completion — read every line).
58
+
59
+ ## Report
60
+
61
+ Your command's final report is appended to `docs/plans/sensitive-bugs-0922/sessions/S4.md` by its report step (path resolved through `.claude/scripts/resolve-docs-root.sh docs/plans/probe.md`). `work` verifies the append.
@@ -0,0 +1,62 @@
1
+ # Brief S5 — tests for sensitive-guard cells that hold only by construction
2
+
3
+ **Plan**: docs/plans/sensitive-bugs-0922/plan.md **Session**: S5 **Round**: 5 **Kind**: bug
4
+ **Item slug**: sensitive-guard-axis-coverage-gaps
5
+
6
+ > The only file this session reads from the plan. Self-contained: it never points at a sibling's brief. The `**Item slug**:` line is the key the execution commands grep for to detect that a brief is active — keep it exactly in that shape.
7
+
8
+ ## Launch
9
+
10
+ /f-ops-plan work sensitive-bugs-0922 S5
11
+
12
+ ## Run
13
+
14
+ /f-bug-fix sensitive-guard-axis-coverage-gaps
15
+
16
+ ## Scope notes
17
+
18
+ Tests and stale comments only, with no behaviour change. Add one test per numbered cell in the bug: relink → warned afresh in B, `POST /api/chat` gated, `triggerTask` in a denied space, and `createdBy` without the permission runs ungated. Each test gets its negative control. S1–S4 have landed and may already cover a cell. Re-check each one against `dev` first, and tick an already-covered cell with the test that covers it rather than adding a duplicate. `tests/api-chat-auth.test.ts` was reshaped by p0-plan-0922 S5 (container caller tokens), so read it as landed.
19
+
20
+ **Read first:** `docs/archive/permissions-and-config/2026-09-22-sensitive-guard-one-read.md` § *Legs × axes*. It maps every leg and axis of this guard. A fix that is right for its own cell and blind to the next one is how seven post-mortems happened. **Deploy needs: restart** (`src/core/runtime.ts` and the tests are not baked). Confirm with the release-gate §1 classification.
21
+
22
+ ## Owned surfaces (predicted)
23
+
24
+ - src/core/runtime.ts
25
+ - tests/api-chat-auth.test.ts
26
+ - tests/sensitive-guard-audience.test.ts
27
+ - tests/sensitive-guard-scope.test.ts
28
+
29
+ ## Do not touch
30
+
31
+ - src/core/routes/console.ts, tests/sensitive-guard-replay.test.ts (owned by S1 this plan)
32
+ - `src/core/runtime.ts` is shared by all five sessions here. Change only the code your bug names; the other guard bugs are sibling sessions.
33
+ - Not ours (avishai tsabari / Avishai-Tsabari, last 14 d): `src/core/runtime.ts`: see plan.md "Not ours". The lane is cleared by Q1 (ours). Re-check with `/f-ops-plan next sensitive-bugs-0922`.
34
+
35
+ ## Edges
36
+
37
+ - start-after: none (satisfied — `next` launched you)
38
+ - merge-after: none
39
+
40
+ ## Overlap verdicts
41
+
42
+ none
43
+
44
+ > The command's preflight overlap check treats a hit whose branch AND every shared file match a verdict line as answered (c), prints one `PLAN-PASSED(overlap): …` line, and carries it into the final report. Any other hit prompts as usual. The merge-time check is never waived.
45
+
46
+ ## Decisions answered
47
+
48
+ - Q1: ours (owner, 2026-09-22). `src/core/runtime.ts` is this plan's lane.
49
+
50
+ ## Ledger rules
51
+
52
+ - `docs/ROADMAP.md`: never edit. Your command writes `docs/pending-updates/sensitive-guard-axis-coverage-gaps.md` as usual.
53
+ - `docs/pending-verification.md`: append only under `## Plan sensitive-bugs-0922 / S5`.
54
+ - Any goal's `decisions.md`: never edit; put decisions in your log's "Siblings must know".
55
+
56
+ ## Environment
57
+
58
+ - Read `CLAUDE.md` → Worktree Setup and Gotchas before opening the worktree. Nothing from them is copied here. `bun install` once in the fresh worktree; the check gate is `bun run check` (every link runs to completion — read every line).
59
+
60
+ ## Report
61
+
62
+ Your command's final report is appended to `docs/plans/sensitive-bugs-0922/sessions/S5.md` by its report step (path resolved through `.claude/scripts/resolve-docs-root.sh docs/plans/probe.md`). `work` verifies the append.
@@ -0,0 +1,9 @@
1
+ # Decisions — plan sensitive-bugs-0922
2
+
3
+ > Answer in the `Answer` column. Everything blocked on a question stays blocked until this column is non-empty for it. `build` and `next` add rows; only you write answers. Delete nothing — an answered row is the audit trail.
4
+
5
+ | ID | Blocks | Question | Options | Answer | Answered |
6
+ |----|--------|----------|---------|--------|----------|
7
+ | Q1 | S1, S2, S3, S4, S5 | Lane: does avishai tsabari own `src/core/runtime.ts` (latest touch 2026-09-13)? | ours / theirs / split | ours: no worktree open, not mid-change | 2026-09-22 (owner) |
8
+
9
+ > Open, non-blocking: B1's severity (`moderate` vs `major`, if an admin's `allowed=false` is meant to cut off in-flight work). Re-rate it in the bug doc if you want to. It does not change the fix.
@@ -0,0 +1,147 @@
1
+ # Plan: sensitive-bugs-0922
2
+
3
+ **Status**: Active
4
+ **Created**: 2026-09-22 **Last updated**: 2026-09-22
5
+ **Landing mode**: pr
6
+ **Max width**: 4
7
+ **Inputs**: sensitive-bugs-0922 sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata sensitive-guard-replay-loses-the-dropped-attachment-note sensitive-gate-is-checked-before-the-space-queue scheduled-run-skipped-by-sensitive-deny-has-no-reader sensitive-guard-axis-coverage-gaps (the four bugs bench-patterns-0915 S6 filed, plus the open drop-note bug; split out of the p0-plan-0922 rebuild)
8
+ **Me**: saharw01, Saharw01@gmail.com (also 92163791+saharw01@users.noreply.github.com, avishailevi2@gmail.com)
9
+
10
+ > Written by `/f-ops-plan` build (and `--rebuild`) and `archive` only. Nothing perishable lives here — no base sha, no other author's file list, no story counts; `next` measures those every time it runs. `plan-state.sh` parses the tables below by their header row, so keep the column order.
11
+
12
+ > **Why this batch.** The sensitive-connection guard read (`docs/archive/permissions-and-config/2026-09-22-sensitive-guard-one-read.md`) filed four bugs and cross-linked a fifth. Its recommended order is kept: B2 with the drop-note bug (one shared fix: store the blocked turn), then B1 (re-check at dequeue), then B3, with B4 riding along. **Features first (owner, 2026-09-22).** Every session here touches `src/core/runtime.ts`, and so do `p0-plan-0922` S6–S8. The owner put those P0 features ahead of these bugs, so S1 starts after p0-plan-0922's last runtime.ts session. The rest follow in a strict chain on the same file.
13
+
14
+ ## Items
15
+
16
+ | ID | Kind | Slug | Doc | Command | Round | Needs |
17
+ |----|------|------|-----|---------|-------|-------|
18
+ | S1 | bug | sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata | docs/bugs/sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata.md | /f-bug-fix sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata | 1 | — |
19
+ | S2 | bug | sensitive-guard-replay-loses-the-dropped-attachment-note | docs/bugs/sensitive-guard-replay-loses-the-dropped-attachment-note.md | /f-bug-fix sensitive-guard-replay-loses-the-dropped-attachment-note | 2 | — |
20
+ | S3 | bug | sensitive-gate-is-checked-before-the-space-queue | docs/bugs/sensitive-gate-is-checked-before-the-space-queue.md | /f-bug-fix sensitive-gate-is-checked-before-the-space-queue | 3 | — |
21
+ | S4 | bug | scheduled-run-skipped-by-sensitive-deny-has-no-reader | docs/bugs/scheduled-run-skipped-by-sensitive-deny-has-no-reader.md | /f-bug-fix scheduled-run-skipped-by-sensitive-deny-has-no-reader | 4 | — |
22
+ | S5 | bug | sensitive-guard-axis-coverage-gaps | docs/bugs/sensitive-guard-axis-coverage-gaps.md | /f-bug-fix sensitive-guard-axis-coverage-gaps | 5 | — |
23
+
24
+ > Kind ∈ `feature` | `bug` | `refactor` | `plan` (a Round 0 planning session) | `story` (from `--goal`; runs `/f-feature-dev`).
25
+ > Needs = decision IDs from `decisions.md` that must be answered before this session may launch, or `—`.
26
+ > IDs are never renumbered; `--drop` moves a row to Deferred, `--add` takes the next unused ID.
27
+
28
+ ## Edges
29
+
30
+ | From | To | Kind | Basis |
31
+ |------|----|------|-------|
32
+ | p0-plan-0922/S5 | S1 | start-after | file: src/core/routes/console.ts (predicted by plan p0-plan-0922, S5 not landed) |
33
+ | p0-plan-0922/S6 | S1 | start-after | file: src/core/runtime.ts (predicted by plan p0-plan-0922, S6 not landed) |
34
+ | p0-plan-0922/S7 | S1 | start-after | file: src/core/runtime.ts (predicted by plan p0-plan-0922, S7 not landed) |
35
+ | p0-plan-0922/S8 | S1 | start-after | file: src/core/runtime.ts (predicted by plan p0-plan-0922, S8 not landed) |
36
+ | p0-plan-0922/S5 | S5 | start-after | file: tests/api-chat-auth.test.ts (predicted by plan p0-plan-0922, S5 not landed) |
37
+ | S1 | S2 | start-after | file: src/core/runtime.ts (both predicted) |
38
+ | S2 | S3 | start-after | file: src/core/runtime.ts (both predicted) |
39
+ | S3 | S4 | start-after | file: src/core/runtime.ts (both predicted; also tests/sensitive-guard-audience.test.ts) |
40
+ | S4 | S5 | start-after | file: src/core/runtime.ts (both predicted; also tests/sensitive-guard-audience.test.ts) |
41
+ | S1 | S2 | merge-after | semantic: the drop-note fix adds `droppedAttachments` to the stored blocked turn that the reply-metadata fix introduces (user) |
42
+
43
+ > Kind ∈ `start-after` (To may not launch until From is landed) | `merge-after` (To may launch now, may not land until From is landed).
44
+ > Basis starts with `file:` (inferred from the collision map) or `semantic:` (asked) — never mixed in one row.
45
+ > From may name a session of ANOTHER active plan as `{plan-slug}/S{n}` — always `start-after`, always `file:`; resolved at `next` time by deriving that plan's state.
46
+ > Every pair of sessions here shares `src/core/runtime.ts`. Only the chain edges are written, because the remaining pairs follow from it. S2–S4 reach the cross-plan predecessors through S1.
47
+
48
+ ## Rounds
49
+
50
+ ### Round 0 — unblock
51
+
52
+ | Session | Action | Clears |
53
+ |---------|--------|--------|
54
+ | — | nothing to plan; Q1 (lane) answered at build | — |
55
+
56
+ ### Round 1 — width 1 (after p0-plan-0922 S5–S8 land)
57
+
58
+ | Session | Action | Clears |
59
+ |---------|--------|--------|
60
+ | S1 | /f-bug-fix sensitive-guard-replay-runs-with-the-yes-messages-reply-metadata | S2 |
61
+
62
+ ### Round 2 — width 1
63
+
64
+ | Session | Action | Clears |
65
+ |---------|--------|--------|
66
+ | S2 | /f-bug-fix sensitive-guard-replay-loses-the-dropped-attachment-note | S3 |
67
+
68
+ ### Round 3 — width 1
69
+
70
+ | Session | Action | Clears |
71
+ |---------|--------|--------|
72
+ | S3 | /f-bug-fix sensitive-gate-is-checked-before-the-space-queue | S4; the deferred opt-out idea |
73
+
74
+ ### Round 4 — width 1
75
+
76
+ | Session | Action | Clears |
77
+ |---------|--------|--------|
78
+ | S4 | /f-bug-fix scheduled-run-skipped-by-sensitive-deny-has-no-reader | S5 |
79
+
80
+ ### Round 5 — width 1
81
+
82
+ | Session | Action | Clears |
83
+ |---------|--------|--------|
84
+ | S5 | /f-bug-fix sensitive-guard-axis-coverage-gaps | — |
85
+
86
+ ## Collision map
87
+
88
+ > Whole files, predicted; `docs/` and infrastructure paths are excluded exactly as `overlap-check.sh` excludes them (7 such paths ignored, 19 predicted → 12 kept).
89
+
90
+ | File | Sessions |
91
+ |------|----------|
92
+ | src/core/runtime.ts | S1, S2, S3, S4, S5 (+ p0-plan-0922 S6, S7, S8) |
93
+ | tests/sensitive-guard-audience.test.ts | S3, S4, S5 |
94
+ | src/core/routes/console.ts | S1 (+ p0-plan-0922 S5) |
95
+ | tests/api-chat-auth.test.ts | S5 (+ p0-plan-0922 S5) |
96
+
97
+ ## Predicted surfaces
98
+
99
+ ### S1
100
+
101
+ - src/core/routes/console.ts
102
+ - src/core/runtime.ts
103
+ - tests/sensitive-guard-replay.test.ts
104
+
105
+ ### S2
106
+
107
+ - src/core/runtime.ts
108
+
109
+ ### S3
110
+
111
+ - src/core/runtime.ts
112
+ - tests/sensitive-guard-audience.test.ts
113
+
114
+ ### S4
115
+
116
+ - src/core/runtime.ts
117
+ - tests/sensitive-guard-audience.test.ts
118
+
119
+ ### S5
120
+
121
+ - src/core/runtime.ts
122
+ - tests/api-chat-auth.test.ts
123
+ - tests/sensitive-guard-audience.test.ts
124
+ - tests/sensitive-guard-scope.test.ts
125
+
126
+ ## Adjudicated overlaps
127
+
128
+ > Pre-answers for the execution commands' preflight overlap prompt. Scoped to exact branch + file pairs on branches that belong to NO active plan (a hand-opened worktree, another contributor). A branch of a sibling plan's session is never listed here — it becomes a cross-plan edge instead.
129
+
130
+ | Session | Branch (not in this plan) | Files | Verdict | Decided |
131
+ |---------|---------------------------|-------|---------|---------|
132
+
133
+ > Build measurement 2026-09-22: no worktree other than the primary checkout, and no `worktree-*` branch.
134
+
135
+ ## Not ours
136
+
137
+ > Last measurement (`git log --since=14.days`, authors not in `Me`). Re-measured on every `next`; this table is the snapshot the briefs were written from.
138
+
139
+ | File / area | Author | Basis |
140
+ |-------------|--------|-------|
141
+ | src/core/runtime.ts | avishai tsabari / Avishai-Tsabari | 14-day log (latest `eaf58ba` 2026-09-13, /spaces link, which added the relink axis); every session. Lane: Q1 = ours |
142
+
143
+ ## Deferred
144
+
145
+ | Item | Reason | Home |
146
+ |------|--------|------|
147
+ | sensitive-opt-out-should-deny-not-block | an idea with no architecture; plan it after S3 (B1) lands, because it changes the policy half B1 moves to dequeue, and it must deny at the broker (L4) and env injection (L5) too. Moved here from p0-plan-0922 (2026-09-22) | docs/ideas/sensitive-opt-out-should-deny-not-block.md |
@@ -266,7 +266,7 @@ re-proposed as a rule:
266
266
  - **What each preflight check proves**: `container-lifecycle.md`
267
267
  § "`mercury preflight`".
268
268
  - **What the dump contains and what the diff blocks on**:
269
- `archive/2026-09-07-env-dump.md`, `archive/2026-09-09-env-diff.md`.
269
+ `archive/release-gate/2026-09-07-env-dump.md`, `archive/release-gate/2026-09-09-env-diff.md`.
270
270
  - **The shadow's refusals and the snapshot's exclusions**:
271
271
  `archive/rehearsal/2026-09-03-shadow-snapshot.md`,
272
272
  `archive/rehearsal/2026-09-07-shadow-service.md`.
@@ -89,6 +89,15 @@ reply** of the run — the model's own text as the bridge was asked to send it
89
89
  (`sent[]` raw form), before the platform's markdown rewrite. `produced-file`
90
90
  reads every send of the run.
91
91
 
92
+ **And one the fixture does not write: the role read-back.** Every fixture
93
+ fails when the run resolved the sender as a role other than the one the
94
+ fixture asked for (see *Roles* below) — the report's `role.ran` beside
95
+ `role.expectedReadBack`, the `because` line naming both. It is a check the
96
+ runner always applies, not an assertion key, and its gate id in a
97
+ post-mortem's `Gate check:` line is `smoke.role`. It is what saw the runner
98
+ promote its synthetic admin under the bare jid so every `admin` fixture ran
99
+ as `member` (2026-09-13).
100
+
92
101
  ## The refusal lexicon
93
102
 
94
103
  `src/smoke/refusal.ts` — `REFUSAL_LEXICON`, keyed by primary language subtag,