immune-brain 4.1.0 → 4.3.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.
package/README.md CHANGED
@@ -198,29 +198,55 @@ The three repair/maintenance skills are host-native: they never create a managed
198
198
 
199
199
  ## Lifecycle
200
200
 
201
+ ```mermaid
202
+ flowchart TD
203
+ subgraph Planning ["1. Planning Phase"]
204
+ B["imm-brainstorm<br/>Clarify Requirements & Constraints"] --> P["imm-planner<br/>Author Spec & TaskIntent"]
205
+ P --> TI["TaskIntent (.intent.json)<br/>• goal / scope_hint<br/>• risk tier<br/>• acceptance descriptors"]
206
+ end
207
+
208
+ subgraph Enrollment ["2. Enrollment Gate"]
209
+ TI --> EG{"Native User Gate<br/>Host Modal Confirmation"}
210
+ EG -->|Confirm| KS[(".imm/state/kernel.sqlite<br/>Atomic TaskRecord<br/>Exclusive Workspace Claim")]
211
+ end
212
+
213
+ subgraph Loop ["3. Execution & Assurance Loop (imm-loop)"]
214
+ KS --> EX["Executor Role<br/>Edit code strictly inside scope_hint"]
215
+ EX --> FRZ["advance_assurance<br/>Artifacts frozen (active:frozen)"]
216
+ FRZ --> QA["Deterministic QA Engine<br/>Run acceptance verification commands<br/>Generate QA Attestation"]
217
+
218
+ QA -->|Fail| RW1["Rework / Fix"]
219
+ RW1 --> EX
220
+
221
+ QA -->|Pass| RK{"Risk Tier?"}
222
+ RK -->|routine| ST["Settlement"]
223
+ RK -->|material / critical| RV["Review Role<br/>Structured verdict (Pass / Rework)"]
224
+
225
+ RV -->|Rework| RW2["Rework"]
226
+ RW2 --> EX
227
+ RV -->|Pass| ST
228
+ end
229
+
230
+ subgraph Settlement ["4. Settlement & Learnings"]
231
+ ST --> CLS["Atomic Closure<br/>• Lifecycle: done<br/>• Audit evidence in .imm/audit/<br/>• Release Workspace Claim"]
232
+ CLS -.-> CP["Compounder Role<br/>Extract Learnings to docs/solutions/"]
233
+ end
201
234
  ```
202
- Ordinary request: normal coding / Q&A (Host-native, zero overhead)
203
- │
204
- Explicit skill call (/imm-brainstorm or /imm-planner)
205
- │
206
- ┌──────────────┴──────────────┐
207
- ▼ ▼
208
- imm-brainstorm imm-planner
209
- (clarify requirements, (author Spec + TaskIntent,
210
- read-only framing) define acceptance checks)
211
- │ │
212
- └──────────────┬──────────────┘
213
- ▼
214
- Native Host Confirmation
215
- (Pi TUI dialog / Claude MCP elicitation)
216
- │
217
- ▼
218
- imm-loop
219
- ├── Executor (edits strictly inside scope)
220
- ├── Deterministic QA (runs all acceptance checks)
221
- ├── Isolated Review (independent subagent audit)
222
- └── Settled (.imm/audit/<task-id>/)
223
- ```
235
+
236
+ ### Core Logic: Three Pillars
237
+
238
+ 1. **Two Paths**
239
+ - **Host-native Path**: Daily conversation, code inspections, and ad-hoc fixes stay 100% native with zero workflow overhead.
240
+ - **Managed Path**: Explicitly entered via `imm-brainstorm`, `imm-planner`, or `imm-loop`, strictly governed by the Assurance Kernel.
241
+
242
+ 2. **Authority & Contract**
243
+ - **TaskIntent (`.intent.json`)**: Machine-readable behavioral contract locking `scope_hint` (file boundaries), `risk` tier, and `acceptance` descriptors.
244
+ - **Native Gate (Enrollment)**: The single human-authority confirmation gate; Kernel atomically acquires exclusive workspace ownership (`.imm/state/kernel.sqlite` CAS) to prevent concurrency conflicts and scope drift.
245
+
246
+ 3. **Deterministic Assurance**
247
+ - **QA-First**: The Kernel directly runs verification commands and checks exit codes/byte bounds; never relies on conversational claims.
248
+ - **Risk-Tiered Gates**: `routine` tasks complete upon QA pass; `material` and `critical` tasks require an isolated Review subagent to issue a structured verdict.
249
+ - **Unattended Batch**: Serial execution driven by GitHub Issues and bound by `plan_digest`, where each child independently completes its own Enrollment → QA → Review → Commit cycle.
224
250
 
225
251
  Key invariants:
226
252
 
package/README.zh-CN.md CHANGED
@@ -198,29 +198,55 @@ Executor、QA、Review、Compounder 等为 `imm-loop` 内部调度的角色,
198
198
 
199
199
  ## 生命周期
200
200
 
201
+ ```mermaid
202
+ flowchart TD
203
+ subgraph Planning ["1. 规划阶段"]
204
+ B["imm-brainstorm<br/>需求澄清/约束"] --> P["imm-planner<br/>编写 Spec & TaskIntent"]
205
+ P --> TI["TaskIntent (.intent.json)<br/>• goal / scope_hint<br/>• risk tier<br/>• acceptance descriptors"]
206
+ end
207
+
208
+ subgraph Enrollment ["2. 准入登记"]
209
+ TI --> EG{"Native User Gate<br/>当前 Host 弹窗确认"}
210
+ EG -->|确认| KS[(".imm/state/kernel.sqlite<br/>原子生成 TaskRecord<br/>独占 Workspace Claim")]
211
+ end
212
+
213
+ subgraph Loop ["3. 执行与验证循环 (imm-loop)"]
214
+ KS --> EX["Executor 角色<br/>在 scope_hint 范围内修改代码"]
215
+ EX --> FRZ["advance_assurance<br/>制品冻结 (active:frozen)"]
216
+ FRZ --> QA["确定性 QA 引擎<br/>原子运行 acceptance 校验命令<br/>生成 QA Attestation"]
217
+
218
+ QA -->|失败| RW1["Rework 返工修正"]
219
+ RW1 --> EX
220
+
221
+ QA -->|通过| RK{"Risk 等级?"}
222
+ RK -->|routine| ST["Settlement 结算"]
223
+ RK -->|material / critical| RV["Review 审查角色<br/>结构化裁决 (Pass / Rework)"]
224
+
225
+ RV -->|Rework| RW2["Rework 驳回"]
226
+ RW2 --> EX
227
+ RV -->|Pass| ST
228
+ end
229
+
230
+ subgraph Settlement ["4. 结算与沉淀"]
231
+ ST --> CLS["原子结项<br/>• Lifecycle: done<br/>• 写入审计日志 .imm/audit/<br/>• 释放 Workspace Claim"]
232
+ CLS -.-> CP["Compounder 角色<br/>提取经验至 docs/solutions/"]
233
+ end
201
234
  ```
202
- 普通请求:日常编程 / 问答(Host-native,零流程开销)
203
- │
204
- 显式调用 Skill(/imm-brainstorm 或 /imm-planner)
205
- │
206
- ┌──────────────┴──────────────┐
207
- ▼ ▼
208
- imm-brainstorm imm-planner
209
- (澄清需求、约束与风险, (编写 Spec + TaskIntent,
210
- 只读输出 framing) 定义可自动化验证的验收条件)
211
- │ │
212
- └──────────────┬──────────────┘
213
- ▼
214
- 当前 Host 原生确认
215
- (Pi TUI 弹窗 / Claude MCP elicitation)
216
- │
217
- ▼
218
- imm-loop
219
- ├── Executor(严格在 scope 内修改代码)
220
- ├── 确定性 QA(前台逐项执行验收命令)
221
- ├── 隔离式 Review(独立 subagent 审查代码)
222
- └── 落盘结算(.imm/audit/<task-id>/)
223
- ```
235
+
236
+ ### 核心逻辑三要素
237
+
238
+ 1. **双轨制 (Two Paths)**
239
+ - **Host-native Path**:日常对话、代码检视、单点修改,不触碰 Kernel 权限,零流程开销。
240
+ - **Managed Path**:由 `imm-brainstorm` / `imm-planner` / `imm-loop` 显式驱动,全程受 Kernel 约束。
241
+
242
+ 2. **权限与契约 (Authority & Contract)**
243
+ - **TaskIntent (`.intent.json`)**:机器契约本体,严格锁定 `scope_hint`(文件修改范围)、`risk`(风险层级)与 `acceptance`(确定性断言)。
244
+ - **Native Gate (Enrollment)**:唯一一次人工介入确认,Kernel 原子抢占工作区所有权(SQLite CAS),防止多任务并发冲突与范围漂移。
245
+
246
+ 3. **客观验证 (Deterministic Assurance)**
247
+ - **QA 优先**:由 Kernel 直接前台执行命令并校验退出码/输出,不依赖 LLM 口头汇报。
248
+ - **按险定级**:`routine` 仅需 QA;`material`/`critical` 必须追加独立只读 Reviewer 产出结构化裁决。
249
+ - **批处理 (Unattended Batch)**:基于 GitHub Issue / `plan_digest` 串行推进,每个子任务独立走完 Enrollment → QA → Review → Commit 闭环。
224
250
 
225
251
  核心不变量:
226
252
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "immune-brain",
3
- "version": "4.1.0",
3
+ "version": "4.3.0",
4
4
  "description": "Immune-Brain agent skill system",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "immune-brain",
3
- "version": "4.1.0",
3
+ "version": "4.3.0",
4
4
  "description": "Immune-Brain Claude Code Host: native Enrollment, QA, Review, and Kernel settlement.",
5
5
  "author": {
6
6
  "name": "Immune-Brain Team"
@@ -26,6 +26,9 @@ import {
26
26
  withKernelStoreLock,
27
27
  inspectStorageLayout,
28
28
  migrateLegacyLayout,
29
+ inspectEnrollmentGitBase,
30
+ enrollmentGitBaseNotice,
31
+ initializeEnrollmentGitBase,
29
32
  } from "./runtime-stub";
30
33
  import {
31
34
  presentTaskRail,
@@ -292,6 +295,7 @@ async function executeForegroundEnrollment(
292
295
  pi: Pick<ExtensionAPI, "events">,
293
296
  ): Promise<EnrollmentTerminal> {
294
297
  let stage = "preparing";
298
+ let gitBaseNote = "";
295
299
  const progress = (nextStage: string, summary: string) => {
296
300
  stage = nextStage;
297
301
  const update = updateResult(action, taskId, nextStage, summary);
@@ -303,7 +307,7 @@ async function executeForegroundEnrollment(
303
307
  taskId,
304
308
  "cancelled",
305
309
  stage,
306
- `Foreground enrollment cancelled during ${stage}; zero authority writes were requested`,
310
+ `Foreground enrollment cancelled during ${stage}; zero authority writes were requested${gitBaseNote}`,
307
311
  "retry by invoking the launcher again",
308
312
  );
309
313
 
@@ -437,7 +441,7 @@ async function executeForegroundEnrollment(
437
441
  `task ${taskId} is terminal in this worktree`,
438
442
  "inspect authority state; do not retry enrollment",
439
443
  );
440
- const preparation = await preparePiCanary(root, { task_id: taskId, now });
444
+ let preparation = await preparePiCanary(root, { task_id: taskId, now });
441
445
  signal.throwIfAborted();
442
446
  if (!preparation.intent)
443
447
  return terminal(action, taskId, "blocked", stage, "A Git-tracked TaskIntent is required for Kernel enrollment", "author and stage the canonical TaskIntent");
@@ -468,6 +472,8 @@ async function executeForegroundEnrollment(
468
472
  return terminal(action, taskId, "blocked", stage, `Enrollment is ineligible: ${reasons}`, "resolve the eligibility blockers");
469
473
  }
470
474
 
475
+ const gitBase = await inspectEnrollmentGitBase(root);
476
+ const gitNotice = await enrollmentGitBaseNotice(gitBase);
471
477
  const acceptanceDetails = taskIntent.intent.acceptance.length === 0
472
478
  ? "(none)"
473
479
  : taskIntent.intent.acceptance
@@ -483,6 +489,7 @@ async function executeForegroundEnrollment(
483
489
  `Risk: ${taskIntent.intent.risk}`,
484
490
  `Scope: ${taskIntent.intent.scope_hint.length > 0 ? taskIntent.intent.scope_hint.join(", ") : "(none)"}`,
485
491
  `Acceptance: ${taskIntent.intent.acceptance.length} descriptor(s)`,
492
+ ...(gitNotice ? [gitNotice] : []),
486
493
  ].join("\n");
487
494
  const confirmationDetails = [
488
495
  `Acceptance descriptors:`,
@@ -536,6 +543,11 @@ async function executeForegroundEnrollment(
536
543
  return terminal(action, taskId, "blocked", stage, "Workspace changed after confirmation; enrollment aborted before authority", "restore the intended snapshot and rerun enrollment");
537
544
  signal.throwIfAborted();
538
545
 
546
+ preparation = await initializeEnrollmentGitBase(root, { task_id: taskId, now }, preparation, gitBase, signal);
547
+ if (gitBase.state === "unborn") gitBaseNote = `; empty initial commit ${preparation.git_base_head} remains`;
548
+ if (!preparation.intent) throw new Error("Enrollment requires a readable TaskIntent");
549
+ signal.throwIfAborted();
550
+
539
551
  const nonce = randomUUID();
540
552
  const binding = {
541
553
  task_id: taskId,
@@ -562,7 +574,7 @@ async function executeForegroundEnrollment(
562
574
  progress("rehearsing", uxText(UX_LANG, "Running the zero-write Kernel owner rehearsal", "正在执行 Kernel 所有者零写入预演"));
563
575
  const rehearsal = await runEnrollmentRehearsal(root, input, capability, registry);
564
576
  if (!rehearsal.rehearsed || rehearsal.evidence.outcome !== "ready")
565
- return terminal(action, taskId, "failed", stage, `Kernel enrollment rehearsal failed: ${rehearsal.evidence.blockers.join("; ")}`, "resolve the final-lock preconditions and retry");
577
+ return terminal(action, taskId, "failed", stage, `Kernel enrollment rehearsal failed: ${rehearsal.evidence.blockers.join("; ")}${gitBaseNote}`, "resolve the final-lock preconditions and retry");
566
578
  if (signal.aborted) return cancelled();
567
579
  if (!beginCommit()) return cancelled();
568
580
 
@@ -579,11 +591,13 @@ async function executeForegroundEnrollment(
579
591
  "continue with imm-loop",
580
592
  );
581
593
  } catch (error) {
582
- return classifyCommitFailure(root, action, taskId, now, error);
594
+ const failure = await classifyCommitFailure(root, action, taskId, now, error);
595
+ failure.summary += gitBaseNote;
596
+ return failure;
583
597
  }
584
598
  } catch (error) {
585
599
  if (signal.aborted) return cancelled();
586
- return terminal(action, taskId, "failed", stage, `Foreground enrollment failed during ${stage}: ${errorMessage(error)}`, "correct the reported failure and retry");
600
+ return terminal(action, taskId, "failed", stage, `Foreground enrollment failed during ${stage}: ${errorMessage(error)}${gitBaseNote}`, "correct the reported failure and retry");
587
601
  }
588
602
  }
589
603
 
@@ -588,11 +588,12 @@ export default function (
588
588
  }
589
589
  const projection = await projectAssuranceState(ctx.cwd, taskId);
590
590
  if (projection.error) {
591
+ const nextAction = recoveryActionForAssuranceFailure(projection.error) ?? "inspect authority state";
591
592
  const details = {
592
593
  state: "blocked",
593
594
  operation: action.op,
594
595
  result: projection.error,
595
- next_action: "inspect authority state",
596
+ next_action: nextAction,
596
597
  };
597
598
  presentTaskRailResult(ctx, taskId, details);
598
599
  return failCanaryTool(taskId, action.op, "blocked", "projection_unavailable", projection.error, details.next_action);
@@ -1536,7 +1537,7 @@ async function buildAssuranceSnapshot(
1536
1537
  })
1537
1538
  : null;
1538
1539
  const taskSnapshot = !reviewBundle && !reviewManifest
1539
- ? captureGitTaskSnapshot(root, intent.scope_hint)
1540
+ ? captureGitTaskSnapshot(root, intent.scope_hint, taskId)
1540
1541
  : null;
1541
1542
  const dirtyFiles = reviewManifest
1542
1543
  ? Object.keys(reviewManifest.changed_paths)
@@ -1760,6 +1761,10 @@ async function enrichAssuranceResult(
1760
1761
  }
1761
1762
 
1762
1763
  function nextActionForAssuranceResult(result: Record<string, unknown>, taskState: AssuranceTaskState): string {
1764
+ const recovery = recoveryActionForAssuranceFailure(
1765
+ "error" in taskState ? taskState.error : result.reason,
1766
+ );
1767
+ if (recovery) return recovery;
1763
1768
  if ("error" in taskState) return "inspect authority state";
1764
1769
  if (result.state === "review_preparation_failed") return "repair Review preparation, then retry advance_assurance; QA is already committed";
1765
1770
  if (result.code === "verdict_invalid") return "fix the verdict payload and resubmit submit_review; the Review reservation remains active; do not re-dispatch the reviewer";
@@ -1779,6 +1784,17 @@ function nextActionForAssuranceResult(result: Record<string, unknown>, taskState
1779
1784
  }
1780
1785
  }
1781
1786
 
1787
+ function recoveryActionForAssuranceFailure(reason: unknown): string | null {
1788
+ if (typeof reason !== "string") return null;
1789
+ if (reason.includes("task scope contains unstaged or untracked changes:"))
1790
+ return "stage only the listed task-owned paths, then retry the blocked operation";
1791
+ if (reason.includes("QA resolution failed ("))
1792
+ return "repair the verification command or delivery environment, then retry advance_assurance";
1793
+ if (reason.includes("task delivery contains paths outside the authorization envelope:"))
1794
+ return "reconcile the listed paths: unstage unrelated paths or revise TaskIntent scope for task-owned paths, then retry advance_assurance";
1795
+ return null;
1796
+ }
1797
+
1782
1798
  function toolResult(text: string, details?: Record<string, unknown>) {
1783
1799
  return { content: [{ type: "text" as const, text }], details };
1784
1800
  }
@@ -43,18 +43,8 @@ export interface PiCanaryPrepareInput {
43
43
  task_id: string;
44
44
  now: string;
45
45
  }
46
- export interface PiCanaryPreparation {
47
- contract: "assurance_kernel/pi_canary_preparation/v1";
48
- task_id: string;
49
- generated_at: string;
50
- root_state_path: string;
51
- intent: { path: string; revision: number; content_hash: string } | null;
52
- backend_claim: { present: boolean; task_id: string | null; lifecycle_status: string | null };
53
- task_tombstone: { present: boolean; terminal_lifecycle: string | null };
54
- task_record_v3: { present: boolean; lifecycle: string | null; artifact_state: string | null } | null;
55
- workspace: { current_working: string | null };
56
- digest: string;
57
- }
46
+ export type PiCanaryPreparation = import("../runtime/kernel/pi_canary_prepare").PiCanaryPreparation;
47
+ export type EnrollmentGitBase = import("../runtime/assurance/enrollment_git_base").EnrollmentGitBase;
58
48
  export interface EnrollCanaryInput {
59
49
  task_id: string;
60
50
  intent_path: string;
@@ -242,6 +232,18 @@ export async function createEnrollmentAuthorityRegistry(): Promise<EnrollmentAut
242
232
  const mod = await import(/* @vite-ignore */ kernelPath("enrollment_authority"));
243
233
  return mod.createEnrollmentAuthorityRegistry();
244
234
  }
235
+ export async function inspectEnrollmentGitBase(root: string): Promise<EnrollmentGitBase> {
236
+ const mod = await import(/* @vite-ignore */ runtimePath("assurance/enrollment_git_base"));
237
+ return mod.inspectEnrollmentGitBase(root);
238
+ }
239
+ export async function enrollmentGitBaseNotice(base: EnrollmentGitBase): Promise<string | undefined> {
240
+ const mod = await import(/* @vite-ignore */ runtimePath("assurance/enrollment_git_base"));
241
+ return mod.enrollmentGitBaseNotice(base);
242
+ }
243
+ export async function initializeEnrollmentGitBase(root: string, input: PiCanaryPrepareInput, previous: PiCanaryPreparation, base: EnrollmentGitBase, signal?: AbortSignal): Promise<PiCanaryPreparation> {
244
+ const mod = await import(/* @vite-ignore */ runtimePath("assurance/enrollment_git_base"));
245
+ return mod.initializeEnrollmentGitBase(root, input, previous, base, signal);
246
+ }
245
247
  export async function preparePiCanary(
246
248
  root: string,
247
249
  input: PiCanaryPrepareInput,
@@ -6,7 +6,9 @@
6
6
  from `dist/`; nested modes, examples, recovery, and references load on demand.
7
7
  - Ask only when missing information would change the goal, scope, observable behavior, compatibility, risk acceptance, a protected effect, or a fact only the user can supply. Resolve repository facts and delegated technical choices with bounded evidence instead of asking.
8
8
  - Keep edits inside the user-requested Direct scope or the enrolled TaskIntent acceptance and `scope_hint`.
9
- - Stage only explicit task-owned paths. Never use `git add .` or `git add -A` in a dirty worktree.
9
+ - Stage only explicit task-owned paths. This staging authority does not grant
10
+ commit, push, broad staging, or authority over pre-existing user changes.
11
+ Never use `git add .` or `git add -A` in a dirty worktree.
10
12
  - Do not create, switch, or delete Git worktrees; operate only in the Host launch directory.
11
13
  - Record reproducible evidence before reporting closure.
12
14
  - Required verification must pass before reporting completion; disclosing a gap is not a substitute. Autonomously diagnose, repair, and rerun failing conventional local checks within the authorized scope; never delete, skip, or weaken a valid check to manufacture a pass. If a required check remains failing or cannot run, report the work as incomplete with the concrete blocker.
@@ -46,7 +48,9 @@ routing. A new Managed workflow starts only from explicit `imm-brainstorm`,
46
48
  Managed owner remains authoritative; the user resumes it with `imm-loop`.
47
49
  2. **Start explicitly**: the selected Immune-Brain Skill owns its planning or
48
50
  coordination work. It creates only requested artifacts and their required
49
- parent directories; it does not install project-wide contract files.
51
+ parent directories. Explicit Planner entry also owns absent routing-policy
52
+ activation under Planner's Kernel TaskIntent Routing section;
53
+ it does not otherwise install project-wide contract files.
50
54
  3. **Preserve authority**: Planner output is a candidate for later literal-user
51
55
  Enrollment, and Fast-Track preserves TaskIntent scope, Enrollment, QA,
52
56
  Review, authorization, and completion boundaries.
@@ -89,6 +93,8 @@ Privileged effects include:
89
93
 
90
94
  Routine Managed enrollment uses one current-Host native confirmation bound to the TaskIntent content hash after Planner validation. Explicit Plan-only requests stop with candidate artifacts and do not invoke Enrollment; execution-bearing requests open the native gate directly without chat pre-confirmation. Enrollment validates intent, Git ownership, scope, workspace claim, and final authority preconditions without executing acceptance descriptors; deterministic QA executes them after implementation. The routine task proceeds from that single confirmation through enrollment, execution, and QA without a second human stop. Do not request confirmation for local in-scope edits, local verification, ordinary Direct rework, scoped diff review, or completion reporting. Managed evidence, QA, Review, and completion authority remain governed by their Managed contracts; R2 does not weaken them. Managed native-authority failures fail closed with one stable reason and exactly one same-Host recovery action; never offer a Pi, Direct Path, cross-Host/worktree, unmanaged, or automatic-retry fallback.
91
95
 
96
+ For an unborn symbolic Git HEAD, the same Enrollment confirmation discloses an empty root commit. After approval, the Host creates that commit without staging project content or changing the index/worktree, then revalidates ownership and rehearses Enrollment against the new base. Git identity uses existing environment/config values with an explicit automation fallback, never config writes. Decline or pre-initialization cancellation leaves HEAD unborn; if later Enrollment fails or is cancelled, report the remaining commit. Invalid Git refs are errors, not initialization candidates.
97
+
92
98
  ## Parallel Read-Only Dispatch
93
99
 
94
100
  State mutations, step activations, QA decisions, and plan switches remain