immune-brain 5.1.0 → 5.1.2

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 (54) hide show
  1. package/package.json +1 -1
  2. package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
  3. package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +37 -5
  4. package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +230 -10
  5. package/plugins/immune-brain/.pi-extension/pi-canary-assurance-progression.ts +66 -3
  6. package/plugins/immune-brain/agents/immune-brain-reviewer.md +26 -1
  7. package/plugins/immune-brain/dist/claude/mcp-server.mjs +8712 -7410
  8. package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +1 -1
  9. package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +8 -6
  10. package/plugins/immune-brain/dist/imm-brainstorm.md +4 -4
  11. package/plugins/immune-brain/dist/imm-planner.md +41 -5
  12. package/plugins/immune-brain/dist/imm-run.md +123 -9
  13. package/plugins/immune-brain/dist/role-prompts/code-review.md +25 -0
  14. package/plugins/immune-brain/dist/role-prompts/executor.md +7 -0
  15. package/plugins/immune-brain/hooks/hooks.json +22 -1
  16. package/plugins/immune-brain/runtime/assurance/active_task_notice.ts +42 -0
  17. package/plugins/immune-brain/runtime/assurance/coordinator.ts +31 -4
  18. package/plugins/immune-brain/runtime/assurance/delivery_workspace.ts +4 -1
  19. package/plugins/immune-brain/runtime/assurance/review_mediation.ts +107 -0
  20. package/plugins/immune-brain/runtime/assurance/verdict_authority.ts +3 -0
  21. package/plugins/immune-brain/runtime/claude/interaction.ts +15 -0
  22. package/plugins/immune-brain/runtime/claude/kernel_ports.ts +120 -59
  23. package/plugins/immune-brain/runtime/claude/lane_guard.ts +33 -0
  24. package/plugins/immune-brain/runtime/claude/mcp_server.ts +142 -31
  25. package/plugins/immune-brain/runtime/commands/kernel.ts +54 -1
  26. package/plugins/immune-brain/runtime/github_issue_tracker.ts +235 -7
  27. package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +4 -1
  28. package/plugins/immune-brain/runtime/kernel/canary_application.ts +9 -2
  29. package/plugins/immune-brain/runtime/kernel/completion.ts +24 -1
  30. package/plugins/immune-brain/runtime/kernel/reducer.ts +15 -2
  31. package/plugins/immune-brain/runtime/kernel/runtime_contracts.json +7 -0
  32. package/plugins/immune-brain/runtime/kernel/runtime_contracts.ts +75 -0
  33. package/plugins/immune-brain/runtime/kernel/spec_binding.ts +6 -0
  34. package/plugins/immune-brain/runtime/kernel/storage.ts +20 -4
  35. package/plugins/immune-brain/runtime/kernel/storage_paths.ts +33 -3
  36. package/plugins/immune-brain/runtime/kernel/types.ts +7 -0
  37. package/plugins/immune-brain/runtime/kernel/validation.ts +13 -2
  38. package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
  39. package/plugins/immune-brain/runtime/prompts/code-review.md +25 -0
  40. package/plugins/immune-brain/runtime/prompts/executor.md +7 -0
  41. package/plugins/immune-brain/runtime/role_prompt_bridge.ts +1 -1
  42. package/plugins/immune-brain/runtime/unattended/batch_delegation.ts +160 -0
  43. package/plugins/immune-brain/runtime/unattended/batch_disposition.ts +222 -0
  44. package/plugins/immune-brain/runtime/unattended/batch_final_verification.ts +72 -0
  45. package/plugins/immune-brain/runtime/unattended/batch_lanes.ts +447 -37
  46. package/plugins/immune-brain/runtime/unattended/batch_leak_restore.ts +131 -0
  47. package/plugins/immune-brain/runtime/unattended/batch_preflight.ts +33 -16
  48. package/plugins/immune-brain/runtime/unattended/batch_reasons.ts +16 -3
  49. package/plugins/immune-brain/runtime/unattended/batch_runner.ts +44 -0
  50. package/plugins/immune-brain/runtime/unattended/batch_schedule.ts +40 -0
  51. package/plugins/immune-brain/runtime/unattended/batch_state.ts +199 -6
  52. package/plugins/immune-brain/runtime/unattended/lane_workspace.ts +58 -0
  53. package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +1 -1
  54. package/plugins/immune-brain/skills/imm-planner/SKILL.md +3 -1
@@ -25,7 +25,7 @@ Apply the gate when the task touches one or more of these surfaces:
25
25
  - **TaskIntent decomposition**: use Technical Design boundaries as one retain/split criterion with outcome, Verification, dependency, risk, rollback, compatibility, and authority. Split a successor TaskIntent only when a service, state-machine owner, migration, independently promotable layer, or sequence dependency needs independent verification, rollback, authorization, or settlement. A TaskIntent should normally change one primary trust-boundary invariant, but traversing several boundaries or updating both sides of one authority chain does not itself require a split. Split independently verifiable, reversible, authorizable, migratable, or settleable trust invariants. Keep multiple trust-boundary changes together only for one atomic security outcome whose split would create an unsafe or unusable intermediate state, and record that rationale in the Spec. Treat this as Planner judgment rather than a schema field or Enrollment counting rule. Do not split merely because the design names several layers, files, or services, and do not revive prose Plan authority. For a multi-TaskIntent Initiative only, require each Slice to state what is observable and verifiable when that Slice alone has landed, and no acceptance may require another unfinished Slice's work to pass; a Slice with no such statement is a horizontal layer slice and is re-cut. Record the exception for a wide mechanical refactor whose blast radius prevents any green vertical slice, with the rationale and a final integrate-and-verify Slice that the batches block.
26
26
  - **Mermaid intent**: use Mermaid only when it clarifies structure, sequence, data flow, or state transitions; it is not a universal gate or a second source of truth. Medium/High risk Specs record `**Diagram decision**: required|not_required` and a non-empty `**Diagram reason**:`. A `required` decision must include Mermaid; `not_required` explains why prose is sufficient. Low-risk Specs omit the empty ceremony and record neither field.
27
27
  - **Design Conformance**: before final closure, require Spec-to-implementation evidence. A local implementation mismatch routes to `rework`; a structural or intended design change routes to `replan` through Planner. QA cannot silently approve a design change.
28
- - **Brainstorm traceability**: ensure every `BR-*` item listed in `Brainstorm manifest` is mapped in `Brainstorm Trace`. Every Spec decision and every acceptance names exactly one source — an upstream `BR-*` ID, repository evidence, or a delegated technical choice — with or without an upstream manifest, and a user-owned decision that names no source is a defect the Planner removes or returns for clarification.
28
+ - **Brainstorm traceability**: ensure every `BR-*` item listed in `Brainstorm manifest` is mapped in `Brainstorm Trace`. Every Spec decision and every acceptance names exactly one source — an upstream `BR-*` ID, repository evidence, or a delegated technical choice — with or without an upstream manifest, and a user-owned decision that names no source is a defect the Planner removes or returns for clarification. A Spec for work with a user-observable change has a `User scenarios` section; each scenario keeps its upstream `BR-SCN-<n>` ID, or on direct entry is confirmed in clarification and recorded as Spec-local `SCN-<n>`, is tagged `automated` with at least one acceptance ID or `manual` with the reason it cannot be automated, and the mapping is one-way. In a multi-TaskIntent Initiative a scenario belongs to exactly one Slice, the one that first makes it observable. `BR-SCN-*` items fall under the existing handoff completeness check, a scenario with no source is a defect, and `no user-visible change` requires a recorded reason.
29
29
  - **roadmap information preservation**: for large or multi-phase work, distinguish the Roadmap from the current executable slice, preserve deferred phase goals, open questions, promotion criteria, and candidate next Plans.
30
30
  - **executable-slice discipline**: the Plan promises only the current executable slice — one coherent set of Steps sharing acceptance, review, rollback, and authority boundaries. Future-phase logic, speculative architecture, and unvalidated assumptions belong in the Roadmap or as explicitly Deferred items, never as active Steps. Do not enforce a fixed Step count: multiple Steps inside one shared boundary are fine, and a single Step that smuggles in unvalidated future architecture is still over-planning.
31
31
  - **Plan boundary cohesion**: treat Plan granularity separately from Step and Roadmap Phase granularity. Confirm the current Plan contains one coherent executable slice and explain why its outcome, authority, risk, verification, review, and rollback boundaries belong together. Promote an independent boundary into a sequential Plan instead of hiding it inside a large Step. One Phase may therefore span multiple Plans.
@@ -118,9 +118,11 @@ Agent defaults to the current Pi session model. Only an explicit Parent requirem
118
118
  selects another Pi-configured model through host-native `Agent.model`. Kernel
119
119
  Review returns a complete foreground `Agent` envelope with empty `name`, `model`,
120
120
  `thinking`, `resume`, and `schedule` fields so Pi Host resolves the Review agent
121
- configuration. The Parent may use any compatible foreground Agent adapter and
122
- submits the resulting structured verdict directly; lifecycle event matching is
123
- not part of the local authority contract.
121
+ configuration. A compatible foreground Agent adapter must expose the reserved
122
+ dispatch and its paired result to the host's receipt observer. The Parent submits
123
+ without a verdict to apply those observed bytes; a provided verdict must match
124
+ the correlated receipt. Dispatch/result correlation is part of the local authority
125
+ contract.
124
126
 
125
127
  ## Pi Agent Invocation
126
128
 
@@ -144,12 +146,12 @@ Pi `Agent` has no `readonly` parameter; the empty tool policy, child type, and p
144
146
 
145
147
  1. Planning、repository exploration、specialist advisory Review 和 work probe 必须 foreground 执行。Parent launches one child at a time,消费 direct terminal result,并在每个结果后 re-evaluates the remaining dispatch budget;不得把多个 foreground Agent 假定为并发 batch。
146
148
  2. 普通 interactive advisory 不建立 acknowledgement deadline、后台 progress UI、completion push 或 late-notification recovery。活跃 Agent 使用 Pi 原生 foreground Tool row、host cancellation 和 steer。Footer 保持严格为空;No defined-value `setStatus` call is allowed。
147
- 3. Kernel Assurance runs in the foreground Tool call. `advance_assurance` awaits deterministic QA, emits bounded native updates, and returns `review_ready` with foreground `Agent` parameters. The Parent invokes a reviewer, reads its direct result, and passes the structured verdict to `submit_review`. `request_authorization` is the only literal-user confirmation path. AbortSignal cancellation before authority commit and snapshot/CAS revalidation remain mandatory.
149
+ 3. Kernel Assurance runs in the foreground Tool call. `advance_assurance` awaits deterministic QA, emits bounded native updates, and returns `review_ready` with foreground `Agent` parameters. The Parent invokes a reviewer, then calls `submit_review`: omitting the verdict applies the host-observed reviewer receipt, or the Parent may relay the reviewer's structured verdict for fingerprint comparison. `request_authorization` is the only literal-user confirmation path. AbortSignal cancellation before authority commit and snapshot/CAS revalidation remain mandatory.
148
150
  4. Kernel Assurance chat uses native Tool rendering only. It does not publish completion messages or wake a later parent turn; the direct Tool result is the continuation boundary. `awaiting_user` remains valid until the host-built authorization operation is confirmed and the immutable snapshot is revalidated.
149
151
  5. Child 不得再次派发 child,nested delegation 一律禁止。Parent 保留综合与最终判断责任。
150
152
  6. Kernel authority Review 对每个 immutable snapshot 恰好一个 primary reviewer,turn 预算按 workload 缩放(Quick 12 / Standard 16 / Heavy 24),并使用该 snapshot 对应的 Quick/Standard/Heavy 执行档位;不存在从 initial dispatch 起算的单一端到端总预算。Reviewer 必须先验证 immutable v5 manifest 的 `base_head`、`review_commit`、单一 parent、`review_tree` 与 `manifest_digest`,再用只读 Git 命令从 synthetic revision 获取源码;manifest 只有 metadata,不复制 source bytes,也不枚举 neighborhood files。审查只围绕 acceptance assertions 与 `changed_paths`,未变更路径只有在 acceptance、changed caller 或同一 state machine 直接需要时按需读取并注明理由;Reviewer 不探索无关 repository paths。对 settlement-class change,Reviewer 必须先枚举 immutable revision 内每条 terminal、cancellation、timeout 与 race path,再对全部路径给出判断;finding summary 必须以受影响路径开头。每个 acceptance 的执行结果已由 deterministic QA 在 review 前验证并内嵌于 manifest 的 `outcomes` 字段(acceptance_id -> {status, summary});Reviewer 不得重跑 descriptor,也不得把本地没有测试运行当作 finding——Review 只审 revision provenance、代码正确性、回归、安全与缺失测试。除该 reviewer 外,同一触发点最多两个相互独立的 advisory/discovery children;它们只能并行读,不能写 workflow state、关闭 QA 或产生 authority。
151
153
  7. Foreground assurance never sleeps, polls, or schedules a completion callback. One Tool call owns QA; one Parent turn obtains and submits the reviewer verdict. User authorization is reserved for unresolved decisions, explicit stop, and breaking Intent revisions; no risk tier adds a generic final confirmation.
152
- 8. `submit_review` validates the verdict contract, task identity, immutable snapshot digest, and fresh record/Intent/workspace/diff revisions before applying Review authority. Malformed verdicts are retryable without rebuilding evidence. Stale verdicts leave the TaskRecord unchanged and release the stale reservation. Local execution trusts the Parent to relay the reviewer verdict and does not require Agent lifecycle receipts.
154
+ 8. `submit_review` validates the verdict contract, task identity, immutable snapshot digest, and fresh record/Intent/workspace/diff revisions before applying Review authority. An omitted verdict applies the host-observed reviewer receipt bytes (ADR 0017); a relayed verdict must fingerprint-match the observed receipt. Malformed verdicts are retryable without rebuilding evidence. Stale verdicts leave the TaskRecord unchanged and release the stale reservation. Each host adapter observes its own reviewer receipt edge — the Claude host via the dispatch hook log, the Pi host via the generic `Agent` tool events correlated by the reserved prompt digest — and a submission with no recorded receipt fails closed.
153
155
  9. The assurance Tool checks the immutable snapshot before each phase and returns a terminal structured state directly: `cancelled`, `rework`, `review_ready`, `awaiting_user`, `blocked`, or `settlement_unknown`. The commit boundary is non-cancellable. No silent task, status timer, completion notification, or result retrieval path is permitted.
154
156
 
155
157
  ## Result Synthesis
@@ -165,6 +167,6 @@ Parent workflow role 必须:
165
167
 
166
168
  普通 advisory/discovery 的每次启动都消耗一个 candidate budget slot;失败、取消、timeout 或 result_untrusted 均丢弃该输出且不得自动重试。Parent 仅在剩余候选仍独立有用且 evidence budget 仍需要时继续,否则转 solo/fail-closed fallback,并记录 `dispatch_failed` 或 `child_timeout`。Read-only eligibility 与 Pi 的 one-foreground-child 调度限制是两回事:只读调查可同时存在多个待派发候选,但实际执行仍逐个 foreground child 串行消费,不得把多个 foreground Agent 假定为并发 batch。该规则不改变 Kernel authority Review 的显式恢复协议。Child 永远不获得实现、Plan write、workflow mutation 或 QA closure authority。
167
169
 
168
- If Kernel Review dispatch fails, the Parent does not call `submit_review`; the existing Review reservation and immutable evidence remain available for a later foreground retry. A malformed verdict may be corrected and resubmitted. A stale snapshot, explicit release, successful settlement, or session shutdown removes the reservation and evidence. There is no retry counter, dispatch receipt state machine, or provider-specific recovery path.
170
+ If Kernel Review dispatch fails, the Parent does not call `submit_review`; the existing Review reservation and immutable evidence remain available for a later foreground retry. A malformed verdict may be corrected and resubmitted. A stale snapshot, explicit release, successful settlement, or session shutdown removes the reservation and evidence. There is no retry counter or provider-specific recovery path; the only dispatch record is the receipt a host adapter binds to the reservation for receipt-bound submission.
169
171
 
170
172
  This protocol is provider- and host-adapter-agnostic for local execution.
@@ -95,8 +95,8 @@ reflected without change. If the summary introduces or changes a
95
95
  decision, ask for explicit confirmation of only that decision delta
96
96
  and block Planner handoff until it is answered. Agent judgment alone never
97
97
  confirms a proposed direction or scope. Persist only final decisions: map them
98
- to `BR-REQ-*`, `BR-DEC-*`, `BR-OUT-*`, `BR-DEFER-*`, and resolved `BR-Q-*`
99
- manifest entries; do not copy the question transcript into repository artifacts.
98
+ to `BR-REQ-*`, `BR-DEC-*`, `BR-OUT-*`, `BR-DEFER-*`, resolved `BR-Q-*`,
99
+ and `BR-SCN-*` manifest entries; do not copy the question transcript into repository artifacts.
100
100
 
101
101
  ## Explicit exhaustive interrogation
102
102
 
@@ -119,7 +119,7 @@ empty, or report remaining blocked/open nodes if the user stops early.
119
119
  - **Dependency-Aware Rounds**: Ask every independent question on the complete currently unblocked frontier together. Ask fewer questions only because dependencies keep downstream branches blocked, never because of an arbitrary question budget.
120
120
  - **Read-only by default**: Inspect context and summarize the problem. do not implement inside this skill.
121
121
  - **Handoff**: Write concise design notes under `docs/brainstorms/` only if explicitly requested.
122
- - **Handoff Manifest**: When framing is stable, user-confirmed, and routes to planner, include a compact `Brainstorm manifest` with stable IDs for every planner-relevant item: `BR-REQ-*` for confirmed requirements, `BR-DEC-*` for confirmed decisions, `BR-OUT-*` for non-goals, `BR-DEFER-*` for explicitly deferred items, and `BR-Q-*` for open questions. The manifest is the closed-world handoff; the planner must account for every ID instead of relying on prose memory.
122
+ - **Handoff Manifest**: When framing is stable, user-confirmed, and routes to planner, include a compact `Brainstorm manifest` with stable IDs for every planner-relevant item: `BR-REQ-*` for confirmed requirements, `BR-DEC-*` for confirmed decisions, `BR-OUT-*` for non-goals, `BR-DEFER-*` for explicitly deferred items, `BR-Q-*` for open questions, and `BR-SCN-*` for a confirmed user scenario. A `BR-SCN-*` item has exactly `Actor`, `Given`, `When`, `Then`, written in English. `Then` states a result the actor observes directly — command output, a file, an Issue or UI state — and never an internal state or a test file name. Record a scenario only for framing that changes user-observable behavior: framing without a user-observable change gets no invented scenario. The manifest is the closed-world handoff; the planner must account for every ID instead of relying on prose memory.
123
123
  - **Default Next Route**: Route to `imm-planner` when the material frontier is empty, no required fact blocks the handoff, and every material user decision is settled by a direct requirement, explicit answer, or adopted recommendation. An unchanged final summary is a correction window, not another gate. If Brainstorm introduces a new decision, ask for that delta and do not name `imm-planner` as the current next skill.
124
124
  - **Subagents**: Only when optional research is needed, read Research Dispatch and its shared dispatch reference. Default to inline evidence gathering; do not load dispatch instructions merely because Brainstorm was invoked.
125
125
  - **Rejected Decision Evidence**: Use on-demand rejected-decision evidence instead of a global preflight. When a live branch resembles a rejected decision, resolve its recorded reason and optional `reconsider_if` conditions through code/docs inspection before asking the user. Treat each `reconsider_if` list item as an independently sufficient trigger (OR semantics): if available evidence satisfies none, keep the rejection as a current constraint or non-goal without re-litigation; if evidence satisfies one, reopen the decision and cite the condition plus changed evidence; if a condition cannot be resolved, ask only for that concrete missing fact. When `reconsider_if` is absent, preserve the backwards-compatible "what has changed?" fallback after inspection. When `rejection_reason` is absent, inspect an explicit rejection-reason section in the body; if no reason exists, report the metadata gap without inventing a reason or reconsideration condition.
@@ -150,7 +150,7 @@ Agreement becomes framing evidence. Disagreement becomes decision criteria or an
150
150
 
151
151
  **Dispatch behavior:** Use Pi native `Explore` subagents (`subagent_type: "Explore"`). Each prompt must state a bounded read-only scope and returns a structured summary (affected files, conventions found, risks). The parent brainstorm agent merges summaries before producing the output artifact. Research subagents do not write files, specs, plans, or `.imm/` state.
152
152
 
153
- **Manifest mapping:** Repo-local runtimes use `imm_core.brainstorm_research` to keep research probes host-bound and manifest-oriented. Every child summary that affects planning must map to a Brainstorm manifest ID: `BR-REQ-*`, `BR-DEC-*`, `BR-OUT-*`, `BR-DEFER-*`, or `BR-Q-*`. Any unmapped or unresolved research question becomes a `BR-Q-*`; open `BR-Q-*` items block planner handoff.
153
+ **Manifest mapping:** Repo-local runtimes use `imm_core.brainstorm_research` to keep research probes host-bound and manifest-oriented. Every child summary that affects planning must map to a Brainstorm manifest ID: `BR-REQ-*`, `BR-DEC-*`, `BR-OUT-*`, `BR-DEFER-*`, `BR-Q-*`, or `BR-SCN-*`. Any unmapped or unresolved research question becomes a `BR-Q-*`; open `BR-Q-*` items block planner handoff.
154
154
 
155
155
  **Failure handling:** If research dispatch is unavailable or fails, continue with solo inline investigation. Record the fallback reason per the shared protocol.
156
156
 
@@ -175,7 +175,10 @@ and clarification. It does not apply to Enrolled Intent Revision.
175
175
  Before authoring a TaskIntent, trace each expected behavior from its public or
176
176
  runtime entry point through existing imports and callers to the highest focused
177
177
  behavioral tests. Include generated or packaged mirrors and every owner of the
178
- same state machine. Record the concrete paths in the Spec's discovery evidence when the work is
178
+ same state machine. For configuration changes, follow deployment copies and
179
+ integration assertions; for runtime changes, include the generated Claude bundle
180
+ when it inlines that runtime and the required release changeset. Resolve these
181
+ paths from the build and test references before Enrollment. Record the concrete paths in the Spec's discovery evidence when the work is
179
182
  complex; simple TaskIntent-only work records them in `scope_hint`.
180
183
  Do not author while a referenced sibling is unresolved. Use the smallest
181
184
  coherent module directory for ordinary implementation scope. Keep Kernel,
@@ -234,14 +237,21 @@ confirm a name, slug, Child, or dependency that had not yet been shown.
234
237
 
235
238
  Once decomposition is complete, present one review table containing the Parent
236
239
  result and every Child's stable Slice ID, result, scope boundary, risk, blockers,
237
- and proposed execution order. Ask one focused question: whether the coverage,
240
+ each Slice's scenario IDs or its exemption with the reason, and proposed execution order. Ask one focused question: whether the coverage,
238
241
  granularity, and dependencies are correct. Recommend the complete current
239
242
  frontier so the user can approve it in one response. Before that approval,
240
243
  perform zero GitHub mutations. A partial or progressively disclosed issue set is
241
244
  not eligible for publication.
242
245
 
243
246
  After approval, author, stage, and validate every TaskIntent in the decomposition
244
- with `valid: true` and `enrollment_ready: true`. Resolve `../bin/imm-tracker` from this packaged contract; do not assume a bare command is on `PATH`. Submit the entire approved set once through
247
+ with `valid: true` and `enrollment_ready: true`. Before handoff, compare the
248
+ scopes by machine: run `imm-kernel intent overlap <path> <path>... --json` over
249
+ every TaskIntent of the Initiative, with one `--blocked-by <task-id>=<task-id>,...`
250
+ per dependency, and report its `overlaps` (each shared entry pair) and
251
+ `parallel_groups`. Parallel recommendations follow that output, which uses the
252
+ same projection as the Batch Plan. Never write "disjoint" or "verified" for
253
+ scopes by hand; when the output shows an overlap the Slices run serially unless
254
+ a revised `scope_hint` removes it. Resolve `../bin/imm-tracker` from this packaged contract; do not assume a bare command is on `PATH`. Submit the entire approved set once through
245
255
  `imm-tracker publish-initiative --stdin --json`. Its input contains the confirmed
246
256
  Initiative slug and goal, Parent projection, and every Child's `slice_id`,
247
257
  canonical TaskIntent path, bounded public `acceptance` summaries, and public
@@ -260,7 +270,27 @@ published Child; future checklist-only Slices are not allowed in the batch.
260
270
  Each Child must provide public `acceptance` entries with `id` and a 1-500
261
271
  character `summary`. Their IDs must match every canonical TaskIntent acceptance
262
272
  ID exactly once. Canonical assertion prose is authority evidence and must never
263
- be copied into public GitHub projection. Each Child projection requires
273
+ be copied into public GitHub projection. Each Child may also provide an optional
274
+ `scenarios` array. Omitting it, or giving an empty array, renders no
275
+ `## User scenarios` section for that Child and contributes nothing to the Parent;
276
+ when no Task supplies scenarios the Parent and every Child body are unchanged.
277
+ A scenario is public projection of one user-observable result: `id`
278
+ (`BR-SCN-<n>` or `SCN-<n>`), `actor`, `given`, `when`, `then`, and `mode`.
279
+ `automated` maps to one or more of that Task's canonical acceptance IDs and
280
+ renders as a plain bullet; `manual` requires `manual_reason`, may name no
281
+ acceptance ID, and renders as an unchecked task-list item that also shows the
282
+ reason. The Parent derives one plain bullet per scenario (its ID, owning Slice
283
+ ID, mode, When, and Then) and never a checkbox. Scenarios never widen TaskIntent
284
+ scope or authority. The tracker rejects the whole batch before any remote write
285
+ when a scenario lacks a field, has an id matching neither form, repeats an id
286
+ within the Initiative, is automated with no acceptance ID, names an acceptance ID
287
+ its TaskIntent lacks, or is manual without `manual_reason`. Every scenario list
288
+ item starts at the beginning of its line, so no metadata comment precedes the
289
+ marker. Each Child's ownership marker block also carries one
290
+ `<!-- immune-brain:scenarios=<percent-encoded JSON> -->` per Task holding all of
291
+ its scenarios verbatim; an amendment reads historical scenarios only from that
292
+ marker, never parses the visible section, and treats a Child without it as
293
+ having no scenarios. Each Child projection requires
264
294
  `title` (1-60 characters), the short Slice display title, and may contain
265
295
  `result`, `current_behavior`,
266
296
  `desired_behavior`, `key_interfaces`, `verification`, `blocked_by` Task IDs,
@@ -482,6 +512,12 @@ when breaking, `approve_breaking_intent_revision` with the complete next intent.
482
512
  The native gate is the single user decision for a breaking revision; candidate
483
513
  preparation does not apply the revision or authorize expanded execution.
484
514
 
515
+ A revision is warranted only when the goal, the `scope_hint`, or a user-visible
516
+ commitment changes. A finding whose defect lies inside the current goal and scope
517
+ is ordinary rework for the Loop, not a revision request: decline to prepare one
518
+ that only appends a single input case to an acceptance assertion. Acceptance
519
+ states principles; enumeration of cases belongs in tests.
520
+
485
521
  ## Planning Rules
486
522
 
487
523
  - **Entry Contract**: Use when Spec/TaskIntent planning is needed. An already enrolled owner remains on its current Kernel authority and resumes only through explicit `imm-run`; a validated candidate still needs native Enrollment.
@@ -512,7 +548,7 @@ preparation does not apply the revision or authorize expanded execution.
512
548
  follows Enrolled Intent Revision.
513
549
  - **Review Mapping**: In-scope rework stays with the enrolled TaskIntent and explicit `imm-run` entry. Cross-scope findings become a Planner decision delta with concrete missing paths and verification evidence; do not create a successor prose Plan.
514
550
  - **Decision Provenance**: Every Spec decision and every acceptance names exactly one source: an upstream `BR-*` ID, repository evidence with a concrete path, or a delegated technical choice as defined by Clarification supplement. The rule applies with or without an upstream Brainstorm manifest, and the source does not have to be a `BR-DEC` item: Direct Planner entry and delegated technical choices stay legitimate sources. A user-owned decision — goal, user, scope, observable behavior, compatibility preference, risk acceptance, or success criterion — that names no source is a defect the Planner removes or returns for clarification under the existing Clarification supplement routes. Simple TaskIntent-only work satisfies the rule when each acceptance traces to the request text and adds no table.
515
- - **Brainstorm Manifest Mapping**: Record every upstream `BR-*` item in a Spec `Brainstorm Trace` when the work is complex, otherwise on the TaskIntent, mapped to acceptance, a captured decision, or an explicit reason for deferral or exclusion. Resolve every `BR-Q-*` item before handoff. Do not silently narrow confirmed framing.
551
+ - **Brainstorm Manifest Mapping**: Record every upstream `BR-*` item in a Spec `Brainstorm Trace` when the work is complex, otherwise on the TaskIntent, mapped to acceptance, a captured decision, or an explicit reason for deferral or exclusion. Resolve every `BR-Q-*` item before handoff. Do not silently narrow confirmed framing. A Spec for work with a user-observable change has a `User scenarios` section. Each scenario keeps its upstream `BR-SCN-<n>` ID; on direct entry each scenario is confirmed in clarification and recorded as Spec-local `SCN-<n>` sourced to that confirmation, and a scenario with no source is a defect the Planner removes or returns for clarification. Each scenario is tagged `automated` with at least one acceptance ID, or `manual` with the reason it cannot be automated; a `manual` scenario may name no acceptance, and the mapping is one-way — an acceptance need not serve a scenario. In a multi-TaskIntent Initiative a scenario belongs to exactly one Slice, the one that first makes it observable, and an `automated` scenario maps only to that Slice's acceptance. `BR-SCN-*` items fall under the existing handoff completeness check like every other `BR-*` item. When the work has no user-observable change, record `no user-visible change` with a one-line reason — in the Spec for complex work, on the TaskIntent goal for simple work — write no scenario section, and `no user-visible change` requires a recorded reason.
516
552
  - **Handoff Completeness Check**: Before reporting a candidate as handoff-ready, confirm every upstream item and required phase is mapped exactly once, that required phases are delivered rather than excused by a reason, and that no confirmed outcome was conserved as a smaller deliverable. Reject a narrowed deliverable as a completeness failure; ask again only for a new material delta, never to reconfirm an unchanged clear request or an already-confirmed decision.
517
553
  - **Session Lifecycle Ownership**: The user chooses the current or a new session. Tokens, compactions, tool counts, elapsed time, and review rounds never trigger automatic session creation or termination. Recovery uses TaskRecord and the fresh Kernel projection.
518
554
  - **Subagents**: Only when optional research is needed, read Research Dispatch and its shared dispatch reference. Default to inline evidence gathering. Plan conditional reviewers such as `security-reviewer` only if their trigger surfaces are explicit; do not manufacture them.
@@ -10,7 +10,12 @@ This skill adheres to the **[BASELINE.md](BASELINE.md)**.
10
10
  ## Kernel Canary Routing and Authority
11
11
 
12
12
  Only explicit `imm-run` entry starts or resumes this loop. Ordinary host input
13
- stays host-native; it never resumes a Managed owner implicitly. Read the current
13
+ stays host-native; it never resumes a Managed owner implicitly. While a
14
+ non-terminal TaskRecord holds the workspace, both Hosts add a read-only notice
15
+ to ordinary input (Pi `before_agent_start`, Claude Code `UserPromptSubmit`)
16
+ naming the task, its next obligation and its scope: before committing or
17
+ modifying files inside that scope, read `status` and tell the user the task is
18
+ active and what it waits for. The notice resumes nothing. Read the current
14
19
  Host's `status` projection first and verify the exact active backend claim,
15
20
  TaskIntent, and TaskRecord. Invalid or contradictory projections fail closed. A
16
21
  candidate TaskIntent is not Enrollment authority.
@@ -100,6 +105,12 @@ Continue while the current projection has a valid action:
100
105
  accepted contract and answer with `refute_finding` bound to fresh QA evidence
101
106
  for that acceptance. Escalating a local heuristic a third time instead of
102
107
  choosing one of those two responses is the loop this step exists to break.
108
+ A finding whose defect lies inside the TaskIntent goal and `scope_hint` is
109
+ ordinary rework even when no acceptance names its exact case: fix the code,
110
+ add the test that pins the case, and dispose the finding. It is never a
111
+ reason to revise the Intent. Acceptance text states principles; enumeration
112
+ of specific inputs belongs in tests, so never append a single input case to
113
+ an acceptance assertion in order to close a finding.
103
114
  6. An unresolved decision pauses only dependent execution. On `awaiting_user`,
104
115
  invoke `request_authorization` directly before ending the turn; use the
105
116
  Decisions and Recovery route for its native-gate handling. End the turn if
@@ -197,7 +208,46 @@ Only children whose scopes are provably disjoint run together; a child whose sco
197
208
  overlaps an in-flight Lane waits until that Lane is integrated. Settled children
198
209
  integrate serially, and a parked child ends only itself and its dependents while a
199
210
  disjoint sibling keeps moving. A lost Lane parks as `batch_lane_lost`, and
200
- `qa_failure_limit` counts each child separately. A resume with a different
211
+ `qa_failure_limit` counts each child separately. A completed settlement inside a
212
+ Lane changes Kernel state only and writes nothing to the tracker: the coordinator
213
+ closes the child's Issue after it integrates that child's commit, exactly once,
214
+ and reports the attempt in `tracker_observations`. A parked or lost child is
215
+ never integrated, so its Issue stays open; an explicit stop inside a Lane still
216
+ projects `not planned` at once.
217
+
218
+ The lane batch confirmation offers one opt-in choice, off by default: delegate
219
+ to the coordinator the approval of each Lane child's breaking Intent revisions
220
+ that stay inside that child's TaskIntent as authorized for the batch (ADR 0018).
221
+ The answer is recorded on the batch record as `revision_delegation`; a batch
222
+ confirmed without it behaves exactly as before, and the grant ends with that
223
+ batch — a terminal, retired or later batch never uses it. When an integrated
224
+ child's TaskIntent equals the last revision applied under that grant, the runner
225
+ reseals `plan_digest` and records both digests in `reseals`, so later children
226
+ continue without retiring the batch. Any other Intent change still fails closed
227
+ as `plan_changed` and takes the ADR 0016 retirement path.
228
+
229
+ Before a new lane batch provisions any Lane, the runner compares the Kernel
230
+ contract identifiers (TaskRecord, tombstone, projection, store schema) of the
231
+ coordinator's runtime with those of the runtime a Lane Executor loads — never the
232
+ plugin version number — and refuses with `batch_runtime_contract_mismatch`,
233
+ naming both sources, when they differ; a match adds no confirmation. A Lane whose
234
+ state exists but whose contract the coordinator refuses to read parks as
235
+ `batch_lane_contract_mismatch` with the original parse error, distinct from a
236
+ missing Lane's `batch_lane_lost`.
237
+
238
+ Inside a Lane, the Pi extension and the Claude Code `PreToolUse` hook refuse an
239
+ `edit`/`write` (Claude: `Edit`, `Write`, `MultiEdit`, `NotebookEdit`) whose
240
+ resolved target lies outside the Lane, naming the Lane path. A shell command is
241
+ not intercepted; this is no bash sandbox. When a lane batch is re-entered with a
242
+ dirty coordinator checkout, each dirty path is restored to HEAD only if every one
243
+ of them is an unstaged modification or untracked file that a Lane of this batch
244
+ changed (its bytes differ from that Lane's base) and whose bytes equal the Lane's,
245
+ in its worktree or a Lane-branch commit. The read-only preflight only attributes;
246
+ the runner restores after the batch is authorized. The bytes are
247
+ backed up first under `.imm/state/batches/restores/`, and the backup is recorded
248
+ in the batch record and report as `restores`. If any change has another origin,
249
+ or is staged or deleted, no file is touched and the refusal names it; never
250
+ stage such changes to get past the refusal. A resume with a different
201
251
  `max_parallel` is refused with `batch_parallel_mismatch`. A Lane holds no batch
202
252
  state and its Host session never re-enters the batch.
203
253
 
@@ -220,6 +270,7 @@ source directly rather than assuming the Host injected it into context:
220
270
  | `Lane Executor Host: <host>` | `claude-code` or `pi` | Every Lane's Executor Host is that Host. When the `lane-steward` did not report it available in a Lane, that Lane has no Executor Host; the Parent does not fall back to the other one. | The Parent's own Host type, else the other allowlisted Host |
221
271
  | `Lane Executor model: <model>` | a model identifier of the configured Executor Host | Passed to the Executor Host as `--model <model>` | The Executor Host's own default |
222
272
  | `Lane Executor effort: <level>` | a level the configured Executor Host accepts | Passed as `--effort <level>` to `claude-code` and as `--thinking <level>` to `pi` | The Executor Host's own default |
273
+ | `Batch final verification: <command>; <command>` | plain commands without shell syntax or quoting, at most 8 | Passed as `final_verification` when a new lane batch starts and shown in its native confirmation; the runner runs them on the batch branch once every child is integrated and writes each command's exit status and timing into the completion report as `final_verification`. A failing command reports the batch as not passed; integrated commits are never rolled back | The commands the repository instruction file names for its full test suite and type check, else none and the completion report says nothing was run |
223
274
 
224
275
  A model identifier and an effort level mean something only to one Host, so
225
276
  `Lane Executor model` and `Lane Executor effort` apply only together with
@@ -231,6 +282,11 @@ a resume the recorded `max_parallel` stands and the directive is not passed
231
282
  again. The directives configure how the Parent starts sessions; the runner reads
232
283
  none of them, and they grant no permission or trust option.
233
284
 
285
+ A repository that names its full test and type-check commands in prose (for
286
+ example "uses `bun test`; type checking is `bun run typecheck`") supplies them as
287
+ the final verification without a directive; the Parent never invents a command
288
+ the repository does not name.
289
+
234
290
  #### Lane Executor Supervision
235
291
 
236
292
  The Parent launches and supervises every Lane's Executor Host; no other role
@@ -300,7 +356,8 @@ a live tab:
300
356
  `herdr agent start <name> --kind <kind> --pane <pane_id> -- <host arguments>`.
301
357
  Host arguments are only those that load the Parent's plugin and those that
302
358
  [Lane Preferences](#lane-preferences) resolved for model and effort.
303
- 4. Submit the entry and nothing else, and confirm the turn began:
359
+ 4. Submit the entry and nothing else (later text follows only the instruction
360
+ rule below), and confirm the turn began:
304
361
  `herdr agent prompt <pane_id> "<imm-run entry> <task_id>" --wait --until working --until blocked --timeout 30000`.
305
362
  The entry is the Executor Host's own `imm-run` invocation as that Host names
306
363
  it: a Claude Code Host that loaded the plugin from a directory names it
@@ -312,13 +369,52 @@ a live tab:
312
369
 
313
370
  `agent_not_ready` from step 3, or a `blocked` state at any time, means the tab
314
371
  shows a dialog only the user may answer: workspace trust, sign-in, a permission
315
- request or a question. The Parent sends that tab no keys and no prompt. It
316
- tells the user which tab and `task_id` is waiting, leaves the tab open, and
317
- continues the other Lanes; once the user has answered, step 3's tab is resumed
372
+ request, a question, or a native confirmation such as an Intent revision gate.
373
+ The Parent sends that tab no keys and no prompt, and never answers such a dialog
374
+ for the user. As soon as the wait returns `blocked` it tells the user which tab
375
+ and `task_id` is waiting and what the dialog asks, without waiting to be asked,
376
+ leaves the tab open, and continues the other Lanes; once the user has answered, step 3's tab is resumed
318
377
  from step 4 if the entry was never submitted, otherwise from step 5. `idle` or
319
378
  `done` is a session end for the rules below, although the tab stays open: the
320
379
  Parent calls `start_unattended_batch`, and a relaunch submits the entry again in
321
- the same tab instead of creating another. A failed or timed-out `herdr` command
380
+ the same tab instead of creating another. When that report shows the child
381
+ neither settled nor waiting on a user decision, the Parent tells the user which
382
+ tab and `task_id` went idle and what the Kernel projection still requires.
383
+
384
+ The Parent may send one text instruction to a Lane session, and only while it is
385
+ `idle` and its child is still running in the Lane: a correction to the
386
+ Executor's approach, never an answer to a dialog. It first calls
387
+ `start_unattended_batch` with `lane_instruction` (`task_id`, `text`, `kind`
388
+ `instruction` or `correction`, and the `session_state` it just observed with
389
+ `herdr agent get`). The runner records the text in the batch record and report
390
+ as `interventions` and answers `lane_instruction.accepted`; only when accepted
391
+ does the Parent submit it with `herdr agent prompt <pane_id> "<text>" --wait
392
+ --until working --until blocked --timeout 30000` and re-arm step 5. A `blocked`
393
+ or `working` session is refused and receives nothing. Every instruction the
394
+ coordinator sent is therefore in the report, so Executor-initiated behavior and
395
+ coordinator intervention can be told apart afterwards. This never applies to a
396
+ Review agent: a dispatched reviewer is still never continued or re-prompted.
397
+
398
+ When a Lane child exhausts its rework budget (the per-child `qa_failure_limit`)
399
+ it does not park for the user at once: the report carries a
400
+ `{ role: "coordinator", action: "correct" }` handoff and the runner makes no
401
+ further attempt for that child. The Parent reads the findings of every round
402
+ from that Lane's Kernel state, decides whether they point at one root cause, and
403
+ writes one design-level correction — the invariant to hold and the shared seam to
404
+ fix — never a patch of its own. It starts a new Executor session in the Lane
405
+ (the old context is not reused; the new session resumes from the Kernel
406
+ projection and the Lane workspace alone) and sends the correction as
407
+ `lane_instruction` kind `correction`; the runner counts it. At most two
408
+ corrections are spent per child: the third exhaustion parks the child as
409
+ `batch_correction_limit_reached`, its dependents are skipped, siblings that do not
410
+ depend on it keep executing and integrating, and the batch reports it at the end.
411
+ Single tasks outside a batch keep their existing budget behavior, and Review
412
+ independence is unchanged. A Review rework budget that the Kernel turns into a
413
+ `replan_required` decision still needs the user's own native decision.
414
+
415
+ Every live Lane session keeps exactly one `herdr agent wait` armed, so `blocked`
416
+ and idle-without-settlement wake the Parent on their own; a Parent that has
417
+ nothing else to do still ends its turn with those waits armed. A failed or timed-out `herdr` command
322
418
  proves nothing about delivery; inspect the session's state before repeating it.
323
419
 
324
420
  The Parent only creates: it closes no tab, including the tabs it created, and
@@ -393,9 +489,23 @@ is recorded as a separate follow-up rather than resolved here.
393
489
  no new provider or data effect.
394
490
  - Text instructions are contracts, not a hard bash sandbox. They guide and are
395
491
  reviewed; the Kernel authority gates remain the real boundary.
492
+ - In a Lane whose batch confirmation delegated in-envelope revisions
493
+ (ADR 0018), the Executor does not open the gate for a revision that only
494
+ changes acceptance or narrows `scope_hint`: it stops and returns the complete
495
+ next intent to the Parent, which submits it as `lane_revision` on
496
+ `start_unattended_batch`. The runner applies it as a delegated approval
497
+ (actor `batch-coordinator`, confirmation `delegated-batch:<batch_id>@<time>`)
498
+ or refuses it with `batch_delegation_refused` and a `fallback`; on refusal the
499
+ Executor uses its own native gate below. Widening scope, changing risk, goal
500
+ or the bound Spec is never delegated, and nobody answers a dialog in a tab.
396
501
  - Invoke `approve_breaking_intent_revision` with the complete next intent
397
502
  directly; the native Host gate is the single user decision. Do not overwrite
398
- enrolled intent sidecars or ask for chat pre-confirmation.
503
+ enrolled intent sidecars or ask for chat pre-confirmation. A breaking revision
504
+ is used only when the goal, the `scope_hint`, or a user-visible commitment
505
+ changes. A defect inside the existing goal and scope is rework under the
506
+ Execution Loop, not a revision, and the acceptance length limit is never
507
+ worked around by shortening words: when an assertion grows toward it, the
508
+ assertion is enumerating cases that belong in tests.
399
509
  - On `awaiting_user`, invoke `request_authorization` directly for a concrete
400
510
  unresolved decision or rework authorization, not risk tier alone.
401
511
  - When the user explicitly asks to stop an active task, invoke the Kernel stop
@@ -505,7 +615,11 @@ Every line reports authority evidence, not prose inference: name the exact
505
615
  task/run identity and the observed lifecycle, artifact, obligation, and claim
506
616
  facts that decided the exit. If a summary claimed more than the projection
507
617
  proves — for example a later Slice as enrolled or the task as completed — report
508
- the projection instead and correct the prose.
618
+ the projection instead and correct the prose. When the bound Spec lists `manual`
619
+ scenarios owned by the settled Task's Slice, add one line naming those scenario
620
+ IDs as pending manual walkthrough; the line is informational and changes no
621
+ stop reason, obligation, or completion fact, and is absent when that Slice owns
622
+ no manual scenario.
509
623
 
510
624
  ## Failure Output
511
625
 
@@ -34,6 +34,13 @@ about the fix's scope, not a new bug. A trigger the accepted contract explicitly
34
34
  excludes is reported as an advisory note against that boundary, never as blocking
35
35
  rework.
36
36
 
37
+ A defect that lies inside the TaskIntent goal and `scope_hint` is a blocking
38
+ finding even when no acceptance assertion names its exact case word for word:
39
+ cite the acceptance whose principle it violates as `acceptance_id` and
40
+ `violated.ref`. Ask for the fix and a test that pins the case, never for the case
41
+ to be written into the acceptance text; a missing literal clause is not itself a
42
+ finding and does not call for an Intent revision.
43
+
37
44
  ## Invariant and Evidence Coverage
38
45
 
39
46
  Judge whether the change actually closes the invariant its acceptance names,
@@ -53,6 +60,24 @@ seam, or a seam silently replaced by a different one, is a finding carrying
53
60
  named, its absence is not a finding: candidates written before this rule carries
54
61
  no defect for lacking one.
55
62
 
63
+ When the Spec referenced by an acceptance maps an `automated` user scenario to
64
+ it, check that the delivered tests at that acceptance's agreed seam assert the
65
+ scenario's `Then`: a scenario whose observable result is asserted nowhere is a
66
+ finding carrying `violated.kind` of `acceptance` with `ref` the acceptance id.
67
+ A `manual` scenario, and an acceptance with no mapped scenario, produce no such
68
+ finding. This adds no finding kind and no verdict branch: it is the same
69
+ `acceptance` finding the seam rule above already defines.
70
+
71
+ Before pass, enumerate the immutable change set and read each complete per-file
72
+ diff, including deletions. Page bounded reads until every hunk is visible; a
73
+ truncated aggregate output or summary is incomplete coverage. Trace changed
74
+ high-risk behavior through its callers and state owner, and inspect a concrete
75
+ negative case against the embedded acceptance. If immutable evidence remains
76
+ unavailable or incomplete, report the concrete acceptance coverage gap in a
77
+ rework finding with required evidence. `inspected_paths` declares coverage; it
78
+ does not itself prove tool reading. Judge quality by evidence, not elapsed time
79
+ or tool-call count.
80
+
56
81
  A required check that is skipped, that matches zero tests, or whose prerequisite
57
82
  is absent does not prove the acceptance; do not accept it as evidence. Full
58
83
  suite coverage reported at a coarse pass count is not per-invariant evidence.
@@ -54,6 +54,13 @@ Record missing evidence as blocked and repair it before handoff. The Loop owns
54
54
  verdict submission, finding disposition and the second-rework generalize-or-refute
55
55
  rule.
56
56
 
57
+ A finding whose defect lies inside the TaskIntent goal and `scope_hint` is
58
+ ordinary rework even when no acceptance names its exact case: fix the code and
59
+ add the test that pins the case. Do not propose an Intent revision for it, and
60
+ never append a single input case to an acceptance assertion to close a finding;
61
+ acceptance states principles, tests enumerate cases. Route to `imm-planner` only
62
+ when the goal, the scope, or a user-visible commitment must change.
63
+
57
64
  ## Code Quality Guard
58
65
 
59
66
  Before handoff, check the implementation for real implementation rather than
@@ -1,6 +1,27 @@
1
1
  {
2
- "description": "Observe Claude Code Review and session lifecycle for Immune-Brain. Hooks never mint authority.",
2
+ "description": "Observe Claude Code Review and session lifecycle for Immune-Brain, keep a batch Lane's file edits inside its Lane, and announce an active Managed task without resuming it. Hooks never mint authority.",
3
3
  "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "Edit|Write|MultiEdit|NotebookEdit",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/claude/mcp-server.mjs\" --hook"
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/claude/mcp-server.mjs\" --hook"
21
+ }
22
+ ]
23
+ }
24
+ ],
4
25
  "SubagentStart": [
5
26
  {
6
27
  "hooks": [
@@ -0,0 +1,42 @@
1
+ // A read-only Host notice for ordinary input while a Managed task is active in
2
+ // the workspace. It tells the model, and through it the user, that the task
3
+ // exists and what it is waiting for, so a fresh session does not commit or edit
4
+ // inside that task's scope blind. It never resumes the task: ordinary input
5
+ // stays host-native, and only an explicit `imm-run` entry continues it.
6
+ import { readTaskRecordRaw } from "../kernel/storage";
7
+ import { readActiveClaimTaskId } from "../unattended/batch_preflight";
8
+
9
+ const MAX_SCOPE_ENTRIES = 12;
10
+
11
+ export async function activeTaskNotice(root: string): Promise<string | null> {
12
+ let taskId: string | null;
13
+ try {
14
+ taskId = readActiveClaimTaskId(root);
15
+ } catch {
16
+ return null;
17
+ }
18
+ if (!taskId) return null;
19
+ // One TaskRecord read, no workspace diff: this runs on every prompt, and the
20
+ // exact next obligation is what the model reads from `status` afterwards.
21
+ let lifecycle = "unknown";
22
+ let artifactState = "unknown";
23
+ let scope: string[] = [];
24
+ try {
25
+ const record = readTaskRecordRaw(root, taskId).record;
26
+ if (record) {
27
+ lifecycle = record.lifecycle;
28
+ artifactState = record.artifact_state;
29
+ scope = [...record.intent_snapshot.scope_hint];
30
+ }
31
+ } catch {
32
+ // The notice is advisory; an unreadable projection still names the task.
33
+ }
34
+ if (lifecycle === "done" || lifecycle === "stopped") return null;
35
+ const shown = scope.slice(0, MAX_SCOPE_ENTRIES).join(", ") + (scope.length > MAX_SCOPE_ENTRIES ? ", ..." : "");
36
+ return [
37
+ `Immune-Brain: Managed task ${taskId} is active in this workspace (lifecycle ${lifecycle}, artifacts ${artifactState}).`,
38
+ "Ordinary input does not resume it, and another session may be running its assurance right now.",
39
+ `Before committing or modifying files inside its scope${shown ? ` (${shown})` : ""}, read its status and tell the user that this task is active and which obligation it is waiting on.`,
40
+ "Only an explicit imm-run entry continues it.",
41
+ ].join(" ");
42
+ }
@@ -23,6 +23,7 @@ import type { AssuranceProjectionResult } from "../kernel/assurance_projection";
23
23
  import type { TaskIntentIdentityToken } from "../kernel/intent_token_registry";
24
24
  import type { AssuranceHostPort, HostReviewReservation } from "./host_port";
25
25
  import type { GithubTrackerResult as GithubTrackerProjectionResult } from "../github_issue_tracker";
26
+ import { isLaneWorkspaceForTask } from "../unattended/lane_workspace";
26
27
 
27
28
 
28
29
  export type AssuranceRole = "qa" | "review";
@@ -87,6 +88,10 @@ export function deriveGithubTerminalProjectionInput(
87
88
  * null otherwise), a tracker failure is reported as a retryable failure rather
88
89
  * than evidence or a blocker, and a failure never repeats the settling Kernel
89
90
  * mutation.
91
+ *
92
+ * A completed settlement inside a batch Lane writes nothing to the tracker: the
93
+ * Lane's delivery is not on the batch branch yet, so the coordinator closes the
94
+ * Child only after it integrates that child's commit.
90
95
  */
91
96
  export async function projectTerminalTrackerState(input: {
92
97
  root: string;
@@ -94,10 +99,14 @@ export async function projectTerminalTrackerState(input: {
94
99
  projection: AssuranceProjectionResult;
95
100
  tombstone: TaskTombstone | null;
96
101
  markTerminal: (root: string, projection: GithubTerminalProjectionInput) => Promise<GithubTrackerProjectionResult>;
102
+ isLaneWorkspace?: (root: string, taskId: string) => boolean;
97
103
  }): Promise<GithubTrackerProjectionResult | undefined> {
98
104
  if (input.projection.error) return undefined;
99
105
  const terminal = deriveGithubTerminalProjectionInput(input.task_id, input.projection, input.tombstone);
100
106
  if (!terminal) return undefined;
107
+ // Only a completed delivery waits for integration; an explicit stop inside a
108
+ // Lane is never integrated and still projects `not planned` here.
109
+ if (terminal.phase === "done" && (input.isLaneWorkspace ?? isLaneWorkspaceForTask)(input.root, input.task_id)) return undefined;
101
110
  try {
102
111
  return await input.markTerminal(input.root, terminal);
103
112
  } catch {
@@ -350,6 +359,18 @@ export type AssuranceSubmitReviewResult = AssuranceRecoveryFields & (
350
359
  | { state: "settlement_unknown"; operation: "qa" | "review"; operation_id: string; reason: string }
351
360
  | { state: "blocked"; reason: string; code?: "verdict_invalid"; recovery_action?: string });
352
361
 
362
+ /**
363
+ * ADR 0017: optional metadata a host adapter supplies alongside the verdict
364
+ * input. The coordinator threads it opaquely; it never resolves verdict sources.
365
+ */
366
+ export interface ReviewSubmissionOptions {
367
+ /** sha256 over the reviewer's own result bytes, bound into the review attestation. */
368
+ reviewer_verdict_sha256?: string;
369
+ /** Synchronous host check immediately before the reservation is consumed. */
370
+ validateReceipt?: () => string | null;
371
+ receiptRecoveryAction?: string;
372
+ }
373
+
353
374
  export type ActiveAssuranceState =
354
375
  | { state: "running"; operation: "qa"; operation_id: string; deadline_seconds: number }
355
376
  | { state: "review_ready"; operation: "review"; operation_id: string }
@@ -559,7 +580,10 @@ function parseVerdictEvidence(value: unknown, index: number): FindingEvidence {
559
580
  export function parseAssuranceVerdict(input: unknown, snapshot: SnapshotDescriptor): AssuranceVerdict {
560
581
  let raw: Record<string, unknown>;
561
582
  if (typeof input === "string") {
562
- const cleaned = input.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("{") && line.endsWith("}")).join("");
583
+ // Agent transport prefixes a status line to the strict JSON verdict.
584
+ const first = input.indexOf("{");
585
+ const last = input.lastIndexOf("}");
586
+ const cleaned = first >= 0 && last >= first ? input.slice(first, last + 1) : "";
563
587
  if (!cleaned) throw new Error("reviewer returned no strict JSON verdict");
564
588
  try { raw = JSON.parse(cleaned) as Record<string, unknown>; } catch { throw new Error("reviewer verdict is not valid JSON"); }
565
589
  } else if (typeof input === "object" && input !== null && !Array.isArray(input)) {
@@ -1192,11 +1216,11 @@ export class AssuranceCoordinator {
1192
1216
  }
1193
1217
  }
1194
1218
 
1195
- async submitReview(taskId: string, ctx: HostContext, verdictInput: unknown): Promise<AssuranceSubmitReviewResult> {
1196
- return this.withRecovery(taskId, ctx, await this.submitReviewOnce(taskId, ctx, verdictInput));
1219
+ async submitReview(taskId: string, ctx: HostContext, verdictInput: unknown, options?: ReviewSubmissionOptions): Promise<AssuranceSubmitReviewResult> {
1220
+ return this.withRecovery(taskId, ctx, await this.submitReviewOnce(taskId, ctx, verdictInput, options));
1197
1221
  }
1198
1222
 
1199
- private async submitReviewOnce(taskId: string, ctx: HostContext, verdictInput: unknown): Promise<AssuranceSubmitReviewResult> {
1223
+ private async submitReviewOnce(taskId: string, ctx: HostContext, verdictInput: unknown, options?: ReviewSubmissionOptions): Promise<AssuranceSubmitReviewResult> {
1200
1224
  const unknown = this.unknownOperations.get(taskId);
1201
1225
  if (unknown) return { state: "settlement_unknown", operation: unknown.operation, operation_id: unknown.operationId, reason: unknown.reason };
1202
1226
  const rejected = this.rejectedReviewOperations.get(taskId);
@@ -1233,6 +1257,8 @@ export class AssuranceCoordinator {
1233
1257
  reservation.verdictCorrectionRequired = true;
1234
1258
  return { state: "blocked", code: "verdict_invalid", reason: boundedAssuranceError(error) };
1235
1259
  }
1260
+ const receiptError = options?.validateReceipt?.();
1261
+ if (receiptError) return { state: "blocked", reason: receiptError, ...(options?.receiptRecoveryAction ? { recovery_action: options.receiptRecoveryAction } : {}) };
1236
1262
  const invocation = this.openInvocation(taskId);
1237
1263
  this.releaseReviewReservation(taskId, reservation);
1238
1264
  try {
@@ -1242,6 +1268,7 @@ export class AssuranceCoordinator {
1242
1268
  verdict,
1243
1269
  invocation,
1244
1270
  actorId: "parent-mediated-review",
1271
+ ...(options?.reviewer_verdict_sha256 ? { reviewer_verdict_sha256: options.reviewer_verdict_sha256 } : {}),
1245
1272
  });
1246
1273
  } catch (error) {
1247
1274
  const reason = boundedAssuranceError(error);