immune-brain 4.4.0 → 4.5.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 (36) 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 +19 -2
  4. package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +10 -2
  5. package/plugins/immune-brain/.pi-extension/pi-canary-tool-failure.ts +6 -1
  6. package/plugins/immune-brain/.pi-extension/runtime-stub.ts +11 -6
  7. package/plugins/immune-brain/dist/BASELINE.md +18 -1
  8. package/plugins/immune-brain/dist/claude/mcp-server.mjs +1039 -465
  9. package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +30 -0
  10. package/plugins/immune-brain/dist/imm-brainstorm.md +21 -0
  11. package/plugins/immune-brain/dist/imm-loop.md +76 -4
  12. package/plugins/immune-brain/dist/imm-planner.md +65 -1
  13. package/plugins/immune-brain/dist/role-prompts/code-review.md +19 -0
  14. package/plugins/immune-brain/dist/role-prompts/executor.md +6 -0
  15. package/plugins/immune-brain/dist/role-prompts/qa.md +13 -1
  16. package/plugins/immune-brain/runtime/assurance/coordinator.ts +160 -12
  17. package/plugins/immune-brain/runtime/assurance/qa.ts +57 -42
  18. package/plugins/immune-brain/runtime/claude/kernel_ports.ts +20 -8
  19. package/plugins/immune-brain/runtime/github_issue_tracker.ts +321 -60
  20. package/plugins/immune-brain/runtime/kernel/storage.ts +8 -3
  21. package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
  22. package/plugins/immune-brain/runtime/prompts/code-review.md +19 -0
  23. package/plugins/immune-brain/runtime/prompts/executor.md +6 -0
  24. package/plugins/immune-brain/runtime/prompts/qa.md +13 -1
  25. package/plugins/immune-brain/runtime/unattended/batch_plan.ts +1 -1
  26. package/plugins/immune-brain/runtime/unattended/batch_preflight.ts +73 -26
  27. package/plugins/immune-brain/runtime/unattended/batch_reasons.ts +6 -0
  28. package/plugins/immune-brain/runtime/unattended/batch_reconfirmation.ts +194 -0
  29. package/plugins/immune-brain/runtime/unattended/batch_runner.ts +126 -151
  30. package/plugins/immune-brain/runtime/unattended/batch_state.ts +34 -4
  31. package/plugins/immune-brain/runtime/v4_runtime.ts +54 -0
  32. package/plugins/immune-brain/runtime/workspace_scope.ts +31 -4
  33. package/plugins/immune-brain/skills/BASELINE.md +18 -1
  34. package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +5 -0
  35. package/plugins/immune-brain/skills/imm-loop/SKILL.md +4 -2
  36. package/plugins/immune-brain/skills/imm-planner/SKILL.md +10 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "immune-brain",
3
- "version": "4.4.0",
3
+ "version": "4.5.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.4.0",
3
+ "version": "4.5.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"
@@ -64,7 +64,7 @@ import {
64
64
  type UserAttentionEventV1,
65
65
  type UserAttentionReason,
66
66
  } from "./pi-canary-interaction";
67
- import { isToolFailureState, throwToolFailure, type ToolFailureV1 } from "./pi-canary-tool-failure";
67
+ import { isToolFailureState, throwToolFailure, type AssuranceFailureDetails, type ToolFailureV1 } from "./pi-canary-tool-failure";
68
68
  import { resolveUxLanguage, uxText } from "./ux-language";
69
69
 
70
70
  /** Host-native UI language; see ux-language.ts. Resolved once per process. */
@@ -1737,6 +1737,11 @@ async function enrichAssuranceResult(
1737
1737
  taskId: string,
1738
1738
  result: Record<string, unknown>,
1739
1739
  ): Promise<Record<string, unknown>> {
1740
+ if (result.recovery_error) {
1741
+ // Do not replay a failed observation for Host decoration or tracker hygiene.
1742
+ const taskState = { error: typeof result.reason === "string" ? result.reason : "authority projection unavailable" };
1743
+ return { ...result, task_state: taskState, next_action: nextActionForAssuranceResult(result, taskState) };
1744
+ }
1740
1745
  const projection = await projectAssuranceState(ctx.cwd, taskId);
1741
1746
  const taskState: AssuranceTaskState = projection.error
1742
1747
  ? { error: projection.error }
@@ -1761,11 +1766,13 @@ async function enrichAssuranceResult(
1761
1766
  }
1762
1767
 
1763
1768
  function nextActionForAssuranceResult(result: Record<string, unknown>, taskState: AssuranceTaskState): string {
1769
+ const derived = result.recovery as { next_action: string } | undefined;
1770
+ if (derived) return derived.next_action;
1764
1771
  const recovery = recoveryActionForAssuranceFailure(
1765
1772
  "error" in taskState ? taskState.error : result.reason,
1766
1773
  );
1767
1774
  if (recovery) return recovery;
1768
- if ("error" in taskState) return "inspect authority state";
1775
+ if (result.recovery_error || "error" in taskState) return "inspect authority state";
1769
1776
  if (result.state === "review_preparation_failed") return "repair Review preparation, then retry advance_assurance; QA is already committed";
1770
1777
  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";
1771
1778
  if (taskState.lifecycle === "done" || taskState.lifecycle === "stopped") return "none";
@@ -1872,6 +1879,7 @@ function failCanaryTool(
1872
1879
  code: string,
1873
1880
  message: string,
1874
1881
  nextAction: string,
1882
+ details: AssuranceFailureDetails = {},
1875
1883
  ): never {
1876
1884
  return throwToolFailure({
1877
1885
  tool: "imm_kernel_canary",
@@ -1881,6 +1889,7 @@ function failCanaryTool(
1881
1889
  code,
1882
1890
  message,
1883
1891
  next_action: nextAction,
1892
+ ...details,
1884
1893
  });
1885
1894
  }
1886
1895
 
@@ -1890,6 +1899,7 @@ function throwIfCanaryToolFailure(
1890
1899
  result: Record<string, unknown>,
1891
1900
  ): void {
1892
1901
  if (!isToolFailureState(result.state)) return;
1902
+ const details = result as AssuranceFailureDetails;
1893
1903
  failCanaryTool(
1894
1904
  taskId,
1895
1905
  operation,
@@ -1903,6 +1913,13 @@ function throwIfCanaryToolFailure(
1903
1913
  typeof result.next_action === "string"
1904
1914
  ? result.next_action
1905
1915
  : "inspect authority state",
1916
+ // Preserve only the coordinator's bounded observation metadata.
1917
+ {
1918
+ ...(details.diagnostics ? { diagnostics: details.diagnostics } : {}),
1919
+ ...(details.environment_failure ? { environment_failure: true } : {}),
1920
+ ...(details.recovery ? { recovery: details.recovery } : {}),
1921
+ ...(details.recovery_error ? { recovery_error: details.recovery_error } : {}),
1922
+ },
1906
1923
  );
1907
1924
  }
1908
1925
 
@@ -5,6 +5,7 @@ import {
5
5
  createBatchAuthorityRegistry,
6
6
  deriveChildEnrollment,
7
7
  startBatch,
8
+ batchQaFailureFacts,
8
9
  readTaskRecord,
9
10
  advancePiTask,
10
11
  projectAssuranceForTask,
@@ -114,7 +115,7 @@ export async function executePiUnattendedBatch(
114
115
  // working-tree cleanliness against the authorized scope, recovery children,
115
116
  // plan digest, and base HEAD are one shared projection, so neither Host
116
117
  // re-implements a batch decision.
117
- const now = new Date().toISOString();
118
+ let now = new Date().toISOString();
118
119
  const preflight = await projectBatchPreflight({
119
120
  root,
120
121
  initiative_slug: initiativeSlug,
@@ -203,6 +204,9 @@ export async function executePiUnattendedBatch(
203
204
  // review-2: verify cancellation signal right before authority issuance and startBatch
204
205
  if (signal?.aborted) return batchReason("cancelled_before_execution");
205
206
 
207
+ now = new Date().toISOString();
208
+ if (Date.parse(expiresAt) <= Date.parse(now))
209
+ return batchReason("confirmation_failed", "confirmed batch deadline expired before authorization issuance");
206
210
  const capability = batchRegistry.issue(binding, recoveryChildren as any, now);
207
211
 
208
212
  let activeReviewDispatch: { operation_id: string; agent_params: Record<string, unknown> } | null = null;
@@ -251,7 +255,11 @@ export async function executePiUnattendedBatch(
251
255
  return res;
252
256
  },
253
257
  projectTask: async (taskRoot, taskId) => {
254
- return projectAssuranceForTask(taskRoot, taskId);
258
+ const fresh = await projectAssuranceForTask(taskRoot, taskId);
259
+ if (!fresh.claim || fresh.projection.lifecycle !== "active" || fresh.error !== null) return fresh;
260
+ const { record, revision } = await readTaskRecord(taskRoot, taskId);
261
+ if (!record || revision !== fresh.projection.record_revision) throw new Error(`Kernel recovery projection changed for ${taskId}`);
262
+ return { ...fresh, ...batchQaFailureFacts(record) };
255
263
  },
256
264
  ownsTaskClaim: (taskId) => {
257
265
  if (isResuming && taskId === readActiveClaimTaskId(root)) {
@@ -1,4 +1,9 @@
1
- export interface ToolFailureV1 {
1
+ import type { AssuranceAdvanceResult } from "../runtime/assurance/coordinator";
2
+
3
+ export type AssuranceFailureDetails = Pick<AssuranceAdvanceResult,
4
+ "diagnostics" | "environment_failure" | "recovery" | "recovery_error">;
5
+
6
+ export interface ToolFailureV1 extends AssuranceFailureDetails {
2
7
  contract: "immune_brain/tool_failure/v1";
3
8
  tool: "imm_canary_enrollment" | "imm_kernel_canary";
4
9
  task_id: string;
@@ -171,10 +171,12 @@ export interface TaskRecordRead {
171
171
  manifest_digest: string;
172
172
  };
173
173
  }>;
174
+ history: import("../runtime/kernel/types").TaskRecord["history"];
174
175
  findings: Array<{
175
176
  id: string;
176
177
  kind: string;
177
178
  status: string;
179
+ acceptance_id?: string | null;
178
180
  summary?: string;
179
181
  anchor?: string | null;
180
182
  evidence?: unknown;
@@ -451,6 +453,7 @@ export type {
451
453
  BatchAuthorizationBinding,
452
454
  ValidatedBatchAuthorization,
453
455
  } from "../runtime/kernel/batch_authority";
456
+ export { batchQaFailureFacts } from "../runtime/unattended/batch_runner";
454
457
  export type {
455
458
  BatchRunnerKernelPort,
456
459
  StartBatchInput,
@@ -553,12 +556,14 @@ export async function getSharedPiProgression(): Promise<any> {
553
556
  export async function advancePiTask(root: string, taskId: string): Promise<any> {
554
557
  const progression = await getSharedPiProgression();
555
558
  const result = await progression.advance(taskId, { cwd: root });
556
- if (result.state === "completed") return { state: "completed" };
557
- if (result.state === "stopped") return { state: "stopped" };
558
- if (result.state === "rework") return { state: "rework", operation: result.operation, summary: result.summary };
559
- if (result.state === "review_ready") return { state: "review_ready", operation_id: result.operation_id, agent_params: result.agent_params };
560
- if (result.state === "blocked") return { state: "blocked", reason: result.reason };
561
- return { state: "failed", reason: (result as { reason?: string }).reason ?? "advance failed" };
559
+ const facts = { diagnostics: result.diagnostics, environment_failure: result.environment_failure, recovery: result.recovery };
560
+ if (result.state === "completed") return { state: "completed", ...facts };
561
+ if (result.state === "stopped") return { state: "stopped", ...facts };
562
+ if (result.state === "rework") return { state: "rework", operation: result.operation, summary: result.summary, ...facts };
563
+ if (result.state === "review_ready") return { state: "review_ready", operation_id: result.operation_id, agent_params: result.agent_params, ...facts };
564
+ if (result.state === "review_preparation_failed") return { state: result.state, operation: result.operation, operation_id: result.operation_id, reason: result.reason, ...facts };
565
+ if (result.state === "blocked") return { state: "blocked", reason: result.reason, ...facts };
566
+ return { state: "failed", reason: (result as { reason?: string }).reason ?? "advance failed", ...facts };
562
567
  }
563
568
 
564
569
  export async function projectAssuranceForTask(root: string, taskId: string): Promise<any> {
@@ -9,6 +9,20 @@
9
9
  - Stage only explicit task-owned paths. This staging authority does not grant
10
10
  commit, push, broad staging, or authority over pre-existing user changes.
11
11
  Never use `git add .` or `git add -A` in a dirty worktree.
12
+ - Grant effects separately: staging, commit, push, publication, and outbound
13
+ model/data calls are distinct operations. Recovering a stage-only instruction
14
+ restores staging and nothing more; commit, push, and publication each keep
15
+ their own existing grant. An explicit approval already covering the same
16
+ operation, target, and impact — including a valid batch capability — stays
17
+ usable without another gate.
18
+ - Before a new outbound effect, check the project's existing research channel and
19
+ data scope. A public article, a mock, a documentation example, or a local
20
+ read-only database gives no authority to call an arbitrary provider or send new
21
+ data; authorize only a genuinely new channel/data/effect delta. Distinguish
22
+ documentation, mock, real-channel, and model-quality evidence when reporting.
23
+ - These text rules are instruction contracts, not a hard bash sandbox. They
24
+ guide behavior and are checked as contracts; they do not technically prevent an
25
+ arbitrary shell command, so authority gates and review remain the real boundary.
12
26
  - Do not create, switch, or delete Git worktrees; operate only in the Host launch directory.
13
27
  - Record reproducible evidence before reporting closure.
14
28
  - 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.
@@ -81,7 +95,6 @@ non-Kernel operations, an explicit user approval already covering the same
81
95
  operation, target, and impact is sufficient; ask again only for a material delta.
82
96
  A generic continuation or configured preference is not blanket authorization.
83
97
  Mandatory native gates and hash-bound manifest approvals still apply.
84
-
85
98
  Privileged effects include:
86
99
 
87
100
  - publish, release, deployment, or remote-system mutation;
@@ -91,6 +104,10 @@ Privileged effects include:
91
104
  override; and
92
105
  - external writes whose target or impact cannot be safely reversed locally.
93
106
 
107
+ An existing grant for one of these never inherits another: a stage-only
108
+ instruction stays stage-only, a commit is not push or publication authority,
109
+ and a valid batch capability commits only its own scope-bounded child.
110
+
94
111
  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.
95
112
 
96
113
  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.