immune-brain 3.6.5 → 3.6.7

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 (49) hide show
  1. package/README.md +69 -3
  2. package/README.zh-CN.md +23 -3
  3. package/package.json +2 -1
  4. package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
  5. package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +60 -21
  6. package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +932 -0
  7. package/plugins/immune-brain/.pi-extension/pi-canary-interaction.ts +38 -10
  8. package/plugins/immune-brain/.pi-extension/runtime-stub.ts +200 -2
  9. package/plugins/immune-brain/dist/BASELINE.md +48 -15
  10. package/plugins/immune-brain/dist/claude/mcp-server.mjs +3415 -228
  11. package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +1 -1
  12. package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +1 -1
  13. package/plugins/immune-brain/dist/imm-agent-doc-maintain.md +9 -1
  14. package/plugins/immune-brain/dist/imm-brainstorm.md +49 -35
  15. package/plugins/immune-brain/dist/imm-doc-prune.md +7 -1
  16. package/plugins/immune-brain/dist/imm-loop.md +45 -11
  17. package/plugins/immune-brain/dist/imm-planner.md +65 -28
  18. package/plugins/immune-brain/dist/imm-pr-fix.md +6 -2
  19. package/plugins/immune-brain/dist/role-prompts/code-review.md +9 -1
  20. package/plugins/immune-brain/dist/role-prompts/executor.md +18 -10
  21. package/plugins/immune-brain/dist/role-prompts/pr-fix.md +5 -2
  22. package/plugins/immune-brain/runtime/assurance/coordinator.ts +101 -3
  23. package/plugins/immune-brain/runtime/assurance/verification.ts +13 -2
  24. package/plugins/immune-brain/runtime/claude/interaction.ts +16 -3
  25. package/plugins/immune-brain/runtime/claude/kernel_ports.ts +840 -17
  26. package/plugins/immune-brain/runtime/claude/mcp_server.ts +72 -10
  27. package/plugins/immune-brain/runtime/github_issue_tracker.ts +1018 -17
  28. package/plugins/immune-brain/runtime/kernel/canary_application.ts +16 -1
  29. package/plugins/immune-brain/runtime/kernel/completion.ts +19 -1
  30. package/plugins/immune-brain/runtime/kernel/reducer.ts +165 -12
  31. package/plugins/immune-brain/runtime/kernel/refutation.ts +82 -0
  32. package/plugins/immune-brain/runtime/kernel/types.ts +34 -1
  33. package/plugins/immune-brain/runtime/kernel/validation.ts +202 -9
  34. package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
  35. package/plugins/immune-brain/runtime/prompts/code-review.md +9 -1
  36. package/plugins/immune-brain/runtime/prompts/executor.md +18 -10
  37. package/plugins/immune-brain/runtime/prompts/pr-fix.md +5 -2
  38. package/plugins/immune-brain/runtime/unattended/batch_git.ts +775 -0
  39. package/plugins/immune-brain/runtime/unattended/batch_plan.ts +203 -0
  40. package/plugins/immune-brain/runtime/unattended/batch_runner.ts +1224 -0
  41. package/plugins/immune-brain/runtime/unattended/batch_state.ts +360 -0
  42. package/plugins/immune-brain/runtime/unattended/types.ts +60 -0
  43. package/plugins/immune-brain/skills/BASELINE.md +48 -15
  44. package/plugins/immune-brain/skills/imm-agent-doc-maintain/SKILL.md +20 -4
  45. package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +24 -64
  46. package/plugins/immune-brain/skills/imm-doc-prune/SKILL.md +18 -3
  47. package/plugins/immune-brain/skills/imm-loop/SKILL.md +21 -6
  48. package/plugins/immune-brain/skills/imm-planner/SKILL.md +35 -8
  49. package/plugins/immune-brain/skills/imm-pr-fix/SKILL.md +17 -3
@@ -0,0 +1,360 @@
1
+ // Batch run state persistence for unattended Initiative batch runs.
2
+ // State lives at .imm/state/batches/<batch_id>.json, written only under the
3
+ // kernel store lock, and is Git-ignored with the rest of .imm/state/.
4
+ import { existsSync, mkdirSync, openSync, closeSync, writeFileSync, renameSync, lstatSync, constants, rmSync } from "node:fs";
5
+ import { randomUUID } from "node:crypto";
6
+ import { dirname, join } from "node:path";
7
+ import { readSecureProjectFile, withKernelStoreLock } from "../kernel/storage";
8
+ import type { BatchPlanChild } from "./types";
9
+
10
+ export type BatchRunState =
11
+ | "prepared"
12
+ | "running"
13
+ | "needs_human"
14
+ | "completed"
15
+ | "budget_stopped"
16
+ | "failed"
17
+ | "rejected";
18
+
19
+ export type BatchChildRunState =
20
+ | "pending"
21
+ | "enrolled"
22
+ | "settled"
23
+ | "committed"
24
+ | "needs_human"
25
+ | "skipped_blocked";
26
+
27
+ export interface BatchChildRun {
28
+ task_id: string;
29
+ slice_id: string;
30
+ blocked_by: string[];
31
+ state: BatchChildRunState;
32
+ /** Terminal reason for needs_human / skipped_blocked / failed children. */
33
+ reason: string | null;
34
+ /** Commit created after the Kernel reported this child done. */
35
+ commit: string | null;
36
+ }
37
+
38
+ export interface BatchRunStateRecord {
39
+ contract: "assurance_kernel/batch_run_state/v1";
40
+ batch_id: string;
41
+ initiative_slug: string;
42
+ plan_digest: string;
43
+ base_head: string;
44
+ /** Dedicated batch branch, e.g. imm/<initiative-slug>. */
45
+ branch?: string;
46
+ /** Timestamp of the literal-user batch confirmation. */
47
+ confirmation_time: string;
48
+ /** Authorization expiry from the batch capability binding. */
49
+ authorization_expires_at: string;
50
+ budget: {
51
+ max_children: number;
52
+ deadline_at: string;
53
+ qa_failure_limit: number;
54
+ };
55
+ batch_state: BatchRunState;
56
+ children: BatchChildRun[];
57
+ /** Consecutive deterministic-QA failures across children in this run. */
58
+ consecutive_qa_failures: number;
59
+ /** Commit heads produced by this batch, in child order. */
60
+ commits: string[];
61
+ created_at: string;
62
+ updated_at: string;
63
+ }
64
+
65
+ const BATCH_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
66
+
67
+ const CHILD_RUN_STATES: ReadonlySet<string> = new Set([
68
+ "pending",
69
+ "enrolled",
70
+ "settled",
71
+ "committed",
72
+ "needs_human",
73
+ "skipped_blocked",
74
+ ]);
75
+
76
+ const BATCH_RUN_STATES: ReadonlySet<string> = new Set([
77
+ "prepared",
78
+ "running",
79
+ "needs_human",
80
+ "completed",
81
+ "budget_stopped",
82
+ "failed",
83
+ "rejected",
84
+ ]);
85
+
86
+ const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
87
+
88
+ function isCanonicalTimestamp(value: unknown): value is string {
89
+ if (typeof value !== "string" || !ISO_TIMESTAMP_PATTERN.test(value)) return false;
90
+ const milliseconds = Date.parse(value);
91
+ return Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === value;
92
+ }
93
+
94
+ function validateBatchId(batchId: string): void {
95
+ if (typeof batchId !== "string" || !BATCH_ID_PATTERN.test(batchId))
96
+ throw new Error("batch id is not a safe file identity");
97
+ }
98
+
99
+ function statePath(batchId: string): string {
100
+ validateBatchId(batchId);
101
+ return join(".imm", "state", "batches", `${batchId}.json`);
102
+ }
103
+
104
+ function canonicalBytes(record: BatchRunStateRecord): string {
105
+ return `${JSON.stringify(record, null, 2)}\n`;
106
+ }
107
+
108
+ function validateRecordShape(value: unknown, batchId: string): asserts value is BatchRunStateRecord {
109
+ if (typeof value !== "object" || value === null)
110
+ throw new Error(`batch run state ${batchId} is not an object`);
111
+ const record = value as Record<string, unknown>;
112
+ if (record.contract !== "assurance_kernel/batch_run_state/v1")
113
+ throw new Error(`batch run state ${batchId} has an unknown contract`);
114
+ if (record.batch_id !== batchId)
115
+ throw new Error(`batch run state ${batchId} carries batch_id ${String(record.batch_id)}`);
116
+ if (typeof record.plan_digest !== "string" || !record.plan_digest)
117
+ throw new Error(`batch run state ${batchId} has an invalid plan_digest`);
118
+ if (typeof record.base_head !== "string" || !record.base_head)
119
+ throw new Error(`batch run state ${batchId} has an invalid base_head`);
120
+ if (record.branch !== undefined && (typeof record.branch !== "string" || !record.branch))
121
+ throw new Error(`batch run state ${batchId} has an invalid branch`);
122
+ if (!isCanonicalTimestamp(record.confirmation_time))
123
+ throw new Error(`batch run state ${batchId} has an invalid confirmation_time`);
124
+ if (!isCanonicalTimestamp(record.authorization_expires_at))
125
+ throw new Error(`batch run state ${batchId} has an invalid authorization_expires_at`);
126
+ if (!isCanonicalTimestamp(record.created_at) || !isCanonicalTimestamp(record.updated_at))
127
+ throw new Error(`batch run state ${batchId} has invalid state timestamps`);
128
+ if (!Array.isArray(record.children) || record.children.length === 0)
129
+ throw new Error(`batch run state ${batchId} has no children`);
130
+ if (!BATCH_RUN_STATES.has(String(record.batch_state)))
131
+ throw new Error(`batch run state ${batchId} has an invalid batch_state`);
132
+ if (
133
+ typeof record.consecutive_qa_failures !== "number" ||
134
+ !Number.isInteger(record.consecutive_qa_failures) ||
135
+ record.consecutive_qa_failures < 0
136
+ )
137
+ throw new Error(`batch run state ${batchId} has an invalid consecutive_qa_failures`);
138
+ const budget = record.budget as Record<string, unknown>;
139
+ if (
140
+ typeof record.budget !== "object" ||
141
+ record.budget === null ||
142
+ typeof budget.max_children !== "number" ||
143
+ !Number.isInteger(budget.max_children) ||
144
+ budget.max_children <= 0 ||
145
+ !isCanonicalTimestamp(budget.deadline_at) ||
146
+ typeof budget.qa_failure_limit !== "number" ||
147
+ !Number.isInteger(budget.qa_failure_limit) ||
148
+ budget.qa_failure_limit <= 0
149
+ )
150
+ throw new Error(`batch run state ${batchId} has an invalid budget`);
151
+ if (!Array.isArray(record.commits) || record.commits.some((c) => typeof c !== "string"))
152
+ throw new Error(`batch run state ${batchId} has an invalid commits list`);
153
+ const seenTaskIds = new Set<string>();
154
+ for (const child of record.children) {
155
+ if (
156
+ typeof child !== "object" ||
157
+ child === null ||
158
+ typeof child.task_id !== "string" ||
159
+ !child.task_id ||
160
+ typeof child.slice_id !== "string" ||
161
+ !child.slice_id
162
+ )
163
+ throw new Error(`batch run state ${batchId} has an invalid child entry`);
164
+ if (!CHILD_RUN_STATES.has(String(child.state)))
165
+ throw new Error(`batch run state ${batchId} child ${child.task_id} has an invalid state`);
166
+ if (!Array.isArray(child.blocked_by) || child.blocked_by.some((b: unknown) => typeof b !== "string"))
167
+ throw new Error(`batch run state ${batchId} child ${child.task_id} has an invalid blocked_by`);
168
+ if (
169
+ (child.reason !== null && typeof child.reason !== "string") ||
170
+ (child.commit !== null && typeof child.commit !== "string")
171
+ )
172
+ throw new Error(`batch run state ${batchId} child ${child.task_id} has invalid terminal fields`);
173
+ // State invariants: commit/reason pair coherently with the state.
174
+ if (child.state === "committed" && child.commit === null)
175
+ throw new Error(`batch run state ${batchId} child ${child.task_id} is committed without a commit`);
176
+ if (child.state !== "committed" && child.commit !== null)
177
+ throw new Error(`batch run state ${batchId} child ${child.task_id} has a commit but is not committed`);
178
+ if (
179
+ (child.state === "needs_human" || child.state === "skipped_blocked") &&
180
+ child.reason === null
181
+ )
182
+ throw new Error(`batch run state ${batchId} child ${child.task_id} needs a terminal reason`);
183
+ if (seenTaskIds.has(child.task_id))
184
+ throw new Error(`batch run state ${batchId} has a duplicate child ${child.task_id}`);
185
+ seenTaskIds.add(child.task_id);
186
+ }
187
+ // A committed child must appear in the commits list (review-1).
188
+ for (const child of record.children) {
189
+ if (child.state === "committed" && child.commit !== null && !record.commits.includes(child.commit))
190
+ throw new Error(`batch run state ${batchId} child ${child.task_id} commit is missing from commits`);
191
+ }
192
+ if (
193
+ (record.batch_state === "budget_stopped" ||
194
+ record.batch_state === "failed" ||
195
+ record.batch_state === "rejected") &&
196
+ record.children.some((child) => child.state === "enrolled" || child.state === "settled")
197
+ )
198
+ throw new Error(`batch run state ${batchId} is ${String(record.batch_state)} but a child is still mid-flight`);
199
+ if (
200
+ record.batch_state === "completed" &&
201
+ record.children.some((child) => child.state !== "committed")
202
+ )
203
+ throw new Error(`batch run state ${batchId} is completed but a child is not committed`);
204
+ }
205
+
206
+ export function prepareBatchRunState(input: {
207
+ batch_id: string;
208
+ initiative_slug: string;
209
+ children: BatchPlanChild[];
210
+ plan_digest: string;
211
+ base_head: string;
212
+ branch?: string;
213
+ confirmation_time: string;
214
+ authorization_expires_at: string;
215
+ budget: { max_children: number; deadline_at: string; qa_failure_limit: number };
216
+ now: string;
217
+ }): BatchRunStateRecord {
218
+ validateBatchId(input.batch_id);
219
+ const prepared: BatchRunStateRecord = {
220
+ contract: "assurance_kernel/batch_run_state/v1",
221
+ batch_id: input.batch_id,
222
+ initiative_slug: input.initiative_slug,
223
+ plan_digest: input.plan_digest,
224
+ base_head: input.base_head,
225
+ branch: input.branch ?? `imm/${input.initiative_slug}`,
226
+ confirmation_time: input.confirmation_time,
227
+ authorization_expires_at: input.authorization_expires_at,
228
+ budget: input.budget,
229
+ batch_state: "prepared",
230
+ children: input.children.map((child) => ({
231
+ task_id: child.task_id,
232
+ slice_id: child.slice_id,
233
+ blocked_by: [...child.blocked_by],
234
+ state: "pending",
235
+ reason: null,
236
+ commit: null,
237
+ })),
238
+ consecutive_qa_failures: 0,
239
+ commits: [],
240
+ created_at: input.now,
241
+ updated_at: input.now,
242
+ };
243
+ return prepared;
244
+ }
245
+
246
+ export function readBatchRunState(root: string, batchId: string): BatchRunStateRecord | null {
247
+ const path = statePath(batchId);
248
+ if (!existsSync(join(root, path))) return null;
249
+ const parsed: unknown = JSON.parse(readSecureProjectFile(root, path));
250
+ validateRecordShape(parsed, batchId);
251
+ return parsed;
252
+ }
253
+
254
+ function ensureSecureDirectory(root: string, relative: string): string {
255
+ // review-4: create the directory with lstat verification so a pre-existing
256
+ // symlink cannot redirect state writes outside the project; mirrors the
257
+ // kernel storage boundary.
258
+ const target = join(root, relative);
259
+ const parent = dirname(target);
260
+ if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
261
+ if (existsSync(target)) {
262
+ const stats = lstatSync(target);
263
+ if (!stats.isDirectory()) throw new Error(`${relative} exists but is not a directory`);
264
+ } else {
265
+ mkdirSync(target);
266
+ }
267
+ return target;
268
+ }
269
+
270
+ function writeFileAtomically(root: string, relative: string, bytes: string): void {
271
+ // review-4: no-symlink atomic write; lstat each created directory and the
272
+ // target so writes cannot be redirected by a pre-existing symlink.
273
+ const target = join(root, relative);
274
+ const targetDir = dirname(target);
275
+ const stats = lstatSync(targetDir);
276
+ if (!stats.isDirectory()) throw new Error(`${dirname(relative)} is not a directory`);
277
+ const tempPath = `${target}.${randomUUID()}.tmp`; let fd: number | null = null;
278
+ try {
279
+ fd = openSync(tempPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
280
+ writeFileSync(fd, bytes, "utf8");
281
+ closeSync(fd);
282
+ fd = null;
283
+ renameSync(tempPath, target);
284
+ } finally {
285
+ if (fd !== null) closeSync(fd);
286
+ if (existsSync(tempPath)) { try { rmSync(tempPath); } catch { /* temp already moved */ } }
287
+ }
288
+ }
289
+
290
+ export function writeBatchRunState(
291
+ root: string,
292
+ record: BatchRunStateRecord,
293
+ ): BatchRunStateRecord {
294
+ const path = statePath(record.batch_id);
295
+ validateRecordShape(record, record.batch_id);
296
+ return withKernelStoreLock(root, () => {
297
+ const existing = existsSync(join(root, path)) ? readSecureProjectFile(root, path) : null;
298
+ if (existing !== null && existing === canonicalBytes(record)) return record;
299
+ const stored: BatchRunStateRecord = {
300
+ ...record,
301
+ updated_at: new Date().toISOString(),
302
+ };
303
+ // review-4: secure directory + atomic no-symlink write.
304
+ ensureSecureDirectory(root, join(".imm", "state", "batches"));
305
+ writeFileAtomically(root, path, canonicalBytes(stored));
306
+ return stored;
307
+ });
308
+ }
309
+
310
+ export interface BatchRunReport {
311
+ contract: "assurance_kernel/batch_run_report/v1";
312
+ batch_id: string;
313
+ initiative_slug: string;
314
+ batch_state: BatchRunState;
315
+ children: BatchChildRun[];
316
+ commits: string[];
317
+ reason: string | null;
318
+ /** The single next action for the operator. */
319
+ next_action: string;
320
+ created_at: string;
321
+ }
322
+
323
+ function reportPath(batchId: string): string {
324
+ validateBatchId(batchId);
325
+ return join(".imm", "state", "batches", `${batchId}.report.json`);
326
+ }
327
+
328
+ /** Persist one current stop report per batch. Terminal reports are immutable;
329
+ * a resumable needs_human report may be replaced by the later stop reached
330
+ * after a fresh literal-user confirmation. */
331
+ export function writeBatchRunReport(root: string, report: BatchRunReport): BatchRunReport {
332
+ const relative = reportPath(report.batch_id);
333
+ return withKernelStoreLock(root, () => {
334
+ const path = join(root, relative);
335
+ if (existsSync(path)) {
336
+ const original: unknown = JSON.parse(readSecureProjectFile(root, relative));
337
+ if (
338
+ typeof original !== "object" ||
339
+ original === null ||
340
+ (original as BatchRunReport).contract !== "assurance_kernel/batch_run_report/v1"
341
+ )
342
+ throw new Error(`batch run report ${report.batch_id} has an unknown contract`);
343
+ const prior = original as BatchRunReport;
344
+ if (canonicalReportBytes(prior) === canonicalReportBytes(report)) return prior;
345
+ if (prior.batch_state !== "needs_human") return prior;
346
+ }
347
+ ensureSecureDirectory(root, join(".imm", "state", "batches"));
348
+ writeFileAtomically(root, relative, canonicalReportBytes(report));
349
+ return report;
350
+ });
351
+ }
352
+
353
+ function canonicalReportBytes(report: BatchRunReport): string {
354
+ return `${JSON.stringify(report, null, 2)}\n`;
355
+ }
356
+
357
+ /** All terminal batch states; needs_human is a park, not terminal. */
358
+ export function isTerminalBatchState(state: BatchRunState): boolean {
359
+ return state === "completed" || state === "budget_stopped" || state === "failed" || state === "rejected";
360
+ }
@@ -0,0 +1,60 @@
1
+ import type { GithubInitiativeObservation } from "../github_issue_tracker";
2
+
3
+ export interface BatchPlanBudget {
4
+ max_children: number;
5
+ deadline_at: string;
6
+ qa_failure_limit: number;
7
+ }
8
+
9
+ export interface BatchPlanBudgetInput {
10
+ max_children?: number;
11
+ deadline_at?: string;
12
+ qa_failure_limit?: number;
13
+ }
14
+
15
+ export interface BatchPlanDigestChild {
16
+ task_id: string;
17
+ intent_path: string;
18
+ intent_revision: number;
19
+ intent_content_hash: string;
20
+ blocked_by: string[];
21
+ }
22
+
23
+ export type BatchPlanChildStatus =
24
+ | "enrollable"
25
+ | "already_settled"
26
+ | "already_owned"
27
+ | "needs_human"
28
+ | "blocked";
29
+
30
+ export interface BatchPlanChild {
31
+ task_id: string;
32
+ slice_id: string;
33
+ blocked_by: string[];
34
+ status: BatchPlanChildStatus;
35
+ reason: "critical" | "invalid_intent" | "dependency_unavailable" | null;
36
+ intent_path: string | null;
37
+ intent_revision: number | null;
38
+ intent_content_hash: string | null;
39
+ }
40
+
41
+ export interface BatchPlan {
42
+ contract: "assurance_kernel/batch_plan/v1";
43
+ initiative_slug: string;
44
+ confirmation_time: string;
45
+ tracker_observation: GithubInitiativeObservation;
46
+ children: BatchPlanChild[];
47
+ enrollable: BatchPlanDigestChild[];
48
+ plan_digest: string;
49
+ budget: BatchPlanBudget;
50
+ }
51
+
52
+ export interface ProjectBatchPlanInput {
53
+ confirmation_time: string;
54
+ budget?: BatchPlanBudgetInput;
55
+ }
56
+
57
+ export type InitiativeObservationReader = (
58
+ root: string,
59
+ initiativeSlug: string,
60
+ ) => Promise<GithubInitiativeObservation>;
@@ -2,15 +2,39 @@
2
2
 
3
3
  ## Shared Guards
4
4
 
5
- - Load detailed workflow text from `dist/` only on invocation.
6
- - Ask only when missing information changes outcome or risk.
7
- - Keep edits inside the user-requested Direct scope or the active Managed step boundary.
5
+ - On explicit invocation, read only common constraints and selected section routes
6
+ from `dist/`; nested modes, examples, recovery, and references load on demand.
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
+ - 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.
10
+ - Do not create, switch, or delete Git worktrees; operate only in the Host launch directory.
8
11
  - Record reproducible evidence before reporting closure.
9
- - Use shallow discovery first.
12
+ - 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.
13
+ - Inspect unknown test scripts before execution. A script named `test` that
14
+ deploys, writes production data, or uses credentials is a protected effect,
15
+ not routine local verification; apply Host Confirmation Boundary before it runs.
16
+ - Unanswered questions block only dependent commitments or execution. Continue
17
+ independent read-only investigation and local alternative drafts; label drafts
18
+ unapproved and never treat silence as consent or finalize a dependent Spec.
19
+ - Disclose unrelated pre-existing failures without repairing them or widening
20
+ scope. If they prevent a required check from passing, report that blocker and
21
+ incomplete verification rather than claiming completion.
22
+ - Use bounded evidence to cover affected callers and state owners for shared-contract, security, migration, persistence, or authority changes; the category alone never requires full-directory reads. Stop expanding once the relevant behavior and verification are understood.
10
23
  - Lead with conclusion, evidence, and Next Action.
11
24
  - Advisory roles do not implement; Managed execution roles do not close QA.
12
25
  - An explicit Managed Skill entry stops host-native mutation and routes scope changes to `imm-planner`; Managed execution remains inside its accepted boundary.
13
26
 
27
+ Before local edits or checks, read Verification and Local Recovery below. Read-only
28
+ framing does not load that execution guidance.
29
+
30
+ ## Verification and Local Recovery
31
+
32
+ - Select required checks from the requested outcome, affected behavior, and project requirements before claiming completion. Documentation changes use relevant link, mirror, and contract checks; local logic uses focused tests; shared behavior, build configuration, cross-module changes, and release preparation widen regression coverage. Never reduce required checks merely because they fail.
33
+ - Reuse a recorded passing local result only while the relevant code, test inputs, command, dependencies, and environment remain unchanged. Changed or uncertain inputs require rerunning affected checks. Role handoff alone does not invalidate local evidence; local evidence never replaces Kernel-owned deterministic QA or fresh snapshot-bound Review.
34
+ - Recover routine local failures within the authorized scope: select an unused port, create temporary outputs, and remove only task-generated disposable files. Never overwrite user data or stop an unrelated process to free a port. Before dependency installation, inspect the existing project command, lifecycle scripts, network use, and credential effects; use it only within existing authorization, without dependency or lockfile changes. Unknown or protected effects follow Host Confirmation Boundary.
35
+ - Retry a failed ordinary operation only after new evidence or a relevant condition changes; do not repeat identical attempts indefinitely. This does not authorize retrying a failed native authority gate or an uncertain remote write.
36
+ - When changing a supported behavior or contract, update its existing tests, remove tests whose protected behavior has retired, merge redundant coverage, and remove orphaned fixtures/helpers within the affected scope. For each removal, identify the retired behavior or the remaining coverage and run the surviving related checks. Never delete by age, count, slowness, or flakiness alone. Temporary tests name their exit condition; no per-test registry or repository-wide audit is required.
37
+
14
38
  ## Workflow Activation
15
39
 
16
40
  Ordinary host input stays host-native and does not run natural-language Managed
@@ -46,11 +70,15 @@ returns to `imm-planner`; an enrolled task resumes through `imm-loop` from the
46
70
  current Assurance projection. Do not create or mutate workflow state while
47
71
  classifying a non-mutating request.
48
72
 
49
- Stage only explicit task-owned paths. Never use `git add .` or `git add -A` in a dirty worktree.
50
-
51
73
  ### Host Confirmation Boundary
52
74
 
53
- Require exact host confirmation only for privileged effects:
75
+ Require exact host confirmation only for privileged effects. For ordinary
76
+ non-Kernel operations, an explicit user approval already covering the same
77
+ operation, target, and impact is sufficient; ask again only for a material delta.
78
+ A generic continuation or configured preference is not blanket authorization.
79
+ Mandatory native gates and hash-bound manifest approvals still apply.
80
+
81
+ Privileged effects include:
54
82
 
55
83
  - publish, release, deployment, or remote-system mutation;
56
84
  - destructive or irreversible operations and Git history rewrite;
@@ -65,7 +93,10 @@ Routine Managed enrollment uses one current-Host native confirmation bound to th
65
93
 
66
94
  State mutations, step activations, QA decisions, and plan switches remain
67
95
  strictly sequential. Read-only work — repo exploration, advisory review,
68
- host probing, planner research — may be dispatched in parallel.
96
+ host probing, planner research — is eligible for parallel dispatch in
97
+ capability terms, but Pi schedules one foreground child at a time: launch one
98
+ child, consume its direct result, then decide whether another child is needed.
99
+ Do not assume multiple foreground Agents run as a concurrent batch.
69
100
 
70
101
  Parallel dispatch is restricted by capability, not by a closed Skill list. Every
71
102
  child delegation packet must enforce read-only advisory behavior: no file edits,
@@ -99,13 +130,15 @@ always run sequentially.
99
130
 
100
131
  ## Success Criteria
101
132
 
102
- - Direct work closes only under the Direct completion contract above.
103
- - A Managed Step is ready to execute only when the target result, boundary, and
133
+ - Direct work closes only when the requested result is delivered and the required verification passes: a failed or unavailable required check is reported as incomplete work with its concrete blocker, never as completion. Check breadth follows the request and established project requirements, not a universal full-repository rule. Apply Shared Guards and Verification and Local Recovery for in-scope repair and evidence reuse.
134
+ - A Managed task is ready to execute only when the target result, boundary, and
104
135
  verification path are clear enough to avoid speculative edits.
105
- - A Managed Step is closable only when execution evidence proves the recorded
106
- verification path and the active boundary still matches the Plan.
107
- - Managed scope changes, missing evidence, or structural mismatch return to
108
- `imm-planner` instead of being hidden inside execution or QA.
136
+ - A Managed task is closable only when execution evidence proves the recorded
137
+ verification path and the TaskRecord still matches the enrolled TaskIntent boundary.
138
+ - Collect missing in-scope evidence and continue under the current owner. Missing
139
+ evidence alone does not require replanning. Escalate only a demonstrated
140
+ scope/acceptance mismatch or protected decision through the current owner's
141
+ revision or authorization path; never silently expand execution or QA scope.
109
142
 
110
143
  ## Retirement Completion
111
144
 
@@ -135,4 +168,4 @@ Immune-Brain commands and `.imm` state.
135
168
 
136
169
  Prefer shallow discovery before full-file reads. Start with file lists,
137
170
  `rg` hits, symbol/signature scans, and targeted line ranges; read whole files
138
- only when the narrower evidence path cannot answer the active Step question.
171
+ only when the narrower evidence path cannot answer the active task question.
@@ -1,11 +1,27 @@
1
1
  ---
2
2
  name: imm-agent-doc-maintain
3
- description: Use to minimize tracked AGENTS.md, CLAUDE.md, and GEMINI.md files to necessary non-discoverable context after an explicit, hash-bound, user-approved manifest; never installs or validates project contracts.
3
+ description: Use when the user explicitly requests Immune-Brain minimization of tracked AGENTS.md, CLAUDE.md, or GEMINI.md.
4
4
  ---
5
5
 
6
6
  # Immune-Brain: Agent Doc Maintain
7
7
 
8
- Load [`../../dist/imm-agent-doc-maintain.md`](../../dist/imm-agent-doc-maintain.md),
9
- then minimize tracked agent instruction files to necessary non-discoverable
10
- context. This is a standalone host-native maintenance entry, not a Managed Path
8
+ Use [`../../dist/imm-agent-doc-maintain.md`](../../dist/imm-agent-doc-maintain.md)
9
+ as the canonical contract index, not a whole-document read.
10
+ This is a standalone host-native maintenance entry, not a Managed Path
11
11
  continuation and not an `imm-loop` internal-role dispatch.
12
+
13
+ Mandatory constraints: audit is read-only. Mutation requires exact hash-bound
14
+ manifest approval and immediate revalidation. Preserve active Managed ownership;
15
+ never install project contracts, modify external files, or commit.
16
+ Interruption requires a fresh scan.
17
+
18
+ Section routes - load a section's instructions only when its branch applies.
19
+ Read each linked heading body up to the next heading; nested sections and
20
+ references load only under their own condition. Never read the whole contract
21
+ or all references as an entry prerequisite.
22
+
23
+ - common: [Authority Boundary](../../dist/imm-agent-doc-maintain.md#authority-boundary), [Invocation](../../dist/imm-agent-doc-maintain.md#invocation), [Non-goals](../../dist/imm-agent-doc-maintain.md#non-goals)
24
+ - audit or manifest preparation: [Inventory and Manifest](../../dist/imm-agent-doc-maintain.md#inventory-and-manifest), [Manifest Approval and Recovery](../../dist/imm-agent-doc-maintain.md#manifest-approval-and-recovery)
25
+ - approved mutation before any edit: [Mutation Envelope](../../dist/imm-agent-doc-maintain.md#mutation-envelope), [Approved Mutation](../../dist/imm-agent-doc-maintain.md#approved-mutation)
26
+ - completion: [Verify and Report](../../dist/imm-agent-doc-maintain.md#verify-and-report)
27
+ - interruption or changed candidate before resuming: [Manifest Approval and Recovery](../../dist/imm-agent-doc-maintain.md#manifest-approval-and-recovery); start a fresh scan
@@ -1,70 +1,30 @@
1
1
  ---
2
2
  name: imm-brainstorm
3
- description: Use to frame and clarify an ambiguous problem and its open questions before planning; framing only, no implementation or plan writing.
3
+ description: Use when the user explicitly requests Immune-Brain requirement clarification.
4
4
  ---
5
5
 
6
6
  # Immune-Brain: Brainstorm
7
7
 
8
- Load [`../../dist/imm-brainstorm.md`](../../dist/imm-brainstorm.md), then frame the
9
- task before planning. Do not edit project files. Return goal, constraints,
10
- unknowns, readiness, and Next Action.
11
-
12
- ## Default exhaustive decision tree
13
-
14
- Exhaustive clarification traverses every current-goal branch grounded in the
15
- user request, repository evidence, or a settled parent decision. Do not use a
16
- materiality or task-type judgment to decide whether a sourced user decision is
17
- worth asking. Seed the fixed framing roots: goal, beneficiary and scenario,
18
- current state, desired behavior, scope and non-goals, constraints, failure and
19
- edge behavior, compatibility and migration, success and Verification, and
20
- deferred items; then expand branches from each answer.
21
-
22
- Classify each unresolved node only as a repository fact or a user-owned decision.
23
- Resolve repository facts with bounded, on-demand read-only evidence. A blocked
24
- fact blocks only its dependent subtree and remains explicit; never convert it
25
- into a user preference. Place every sourced user decision on the complete
26
- currently unblocked frontier. Hold dependent questions until their parents are
27
- settled, but ask all independent questions together. Number each question,
28
- include grounded options and a recommended answer with a short reason, and
29
- accept bulk approval of all recommendations with explicit exceptions.
30
-
31
- Direct requirements and adopted recommendations settle only the current nodes;
32
- they never complete the Brainstorm session by themselves. Recompute the tree
33
- after every response and continue through newly unlocked downstream branches.
34
- Minimally clarify an ambiguous answer. If later evidence invalidates a settled
35
- choice, reopen only that decision delta. An explicit defer stops its subtree and
36
- becomes `BR-DEFER-*`, unless it still changes the current Result, interface, or
37
- compatibility and therefore cannot be deferred.
38
-
39
- Brainstorm is complete only when the frontier is empty and no blocked fact
40
- prevents traversal. A zero-question fast path is valid only when the complete
41
- seeded and dynamically expanded tree contains no unresolved user decision. If
42
- the user stops early, emit every open node as `BR-Q-*` and do not mark the frame
43
- planning-ready. Otherwise present a result-only summary as a non-blocking
44
- correction window and retain final decisions in the `BR-*` manifest rather than
45
- copying the question transcript. Do not ask the user to reconfirm decisions
46
- reflected without change. If the summary introduces or changes a decision, ask
47
- for explicit confirmation of only that decision delta and block Planner handoff
48
- until it is answered. Agent judgment alone never confirms a proposed direction
49
- or scope.
50
-
51
- Brainstorm supports `default`, `roundtable`, and `adversarial` modes. All use the
52
- same exhaustive frontier protocol; `roundtable` and `adversarial` add analysis
53
- lenses only when explicitly selected by the user. Required failure, rollback,
54
- compatibility, migration, and risk branches remain part of default traversal.
55
- Consult ADRs and on-demand rejected-decision evidence only when a live branch
56
- reaches that topic.
57
- Architecture mapping is a bounded, read-only `arch-explorer` role selected
58
- through the internal Loop bridge; it cannot write a Spec, Plan, or workflow
59
- state. Pi's adapter may consume `brainstorm_ensemble` dispatch JSON to prepare
60
- advisory Pi subagent envelopes, but envelope construction is not child execution
61
- and does not transfer framing authority. Pi itself may launch those subagents,
62
- collect completed child outputs, and feed them to
63
- `normalizePiBrainstormAgentResults`; runtime does not call any agent, poll
64
- background work, mutate state, or own final Spec/Plan authority.
65
- Agreement becomes framing evidence, Disagreement becomes decision criteria or
66
- `BR-Q-*`, and strong-model blockers become risks or verification requirements.
67
-
68
- When framing discusses later execution, describe Enrollment only as the current
69
- Host's native gate. Never recommend another Host, worktree, or unmanaged
70
- implementation as a fallback for a failed Managed authority interaction.
8
+ Use [`../../dist/imm-brainstorm.md`](../../dist/imm-brainstorm.md) as the
9
+ canonical contract index, not a whole-document read. Explicit entry only: ordinary host questions do not
10
+ start this workflow.
11
+
12
+ Mandatory constraints before any action: Brainstorm is read-only — no code,
13
+ test, or runtime edits; no Spec, Plan, or workflow-state writes. All modes
14
+ produce a `brainstorm_framing` result with goal, constraints, unknowns,
15
+ readiness, and Next Action.
16
+
17
+ Section routes - load a section's instructions only when its branch applies.
18
+ Read each linked heading body up to the next heading; nested sections and
19
+ references load only under their own condition. Never read the whole contract
20
+ or all references as an entry prerequisite.
21
+
22
+ - common: all modes read [Shared Guards](../../dist/BASELINE.md#shared-guards), [Workflow Activation](../../dist/BASELINE.md#workflow-activation), [Host Confirmation Boundary](../../dist/BASELINE.md#host-confirmation-boundary), [Boundary](../../dist/imm-brainstorm.md#boundary), [Invocation modes](../../dist/imm-brainstorm.md#invocation-modes), [Default clarification](../../dist/imm-brainstorm.md#default-clarification), [Workflow Rules](../../dist/imm-brainstorm.md#workflow-rules), [Output artifact](../../dist/imm-brainstorm.md#output-artifact), [Output style](../../dist/imm-brainstorm.md#output-style), [Next Action](../../dist/imm-brainstorm.md#next-action)
23
+ - explicit thorough interrogation: [Explicit exhaustive interrogation](../../dist/imm-brainstorm.md#explicit-exhaustive-interrogation)
24
+ - optional research, including explicitly selected `roundtable` or `adversarial` advisory: [Research Dispatch](../../dist/imm-brainstorm.md#research-dispatch), [Brainstorm Ensemble Advisory](../../dist/imm-brainstorm.md#brainstorm-ensemble-advisory)
25
+ - requested framing document: [Output Language Policy](../../dist/BASELINE.md#output-language-policy)
26
+
27
+ When framing discusses later execution, describe Enrollment only as the
28
+ current Host's native gate. A failed Managed authority interaction stays
29
+ fail-closed: report one same-Host recovery action, never another Host,
30
+ worktree, or unmanaged implementation.