immune-brain 5.2.0 → 5.5.1
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 +4 -4
- package/README.zh-CN.md +4 -4
- package/package.json +2 -1
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/.pi-extension/imm-canary-enroll.ts +54 -70
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +163 -28
- package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +71 -10
- package/plugins/immune-brain/.pi-extension/pi-canary-tool-failure.ts +1 -1
- package/plugins/immune-brain/.pi-extension/pi-run-continuation.ts +161 -0
- package/plugins/immune-brain/agents/immune-brain-reviewer.md +26 -2
- package/plugins/immune-brain/dist/BASELINE.md +17 -1
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +16145 -12745
- package/plugins/immune-brain/dist/docs/reference/code-quality-guard.md +3 -1
- package/plugins/immune-brain/dist/docs/reference/immune-brain-config.md +41 -1
- package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +1 -1
- package/plugins/immune-brain/dist/imm-brainstorm.md +1 -1
- package/plugins/immune-brain/dist/imm-planner.md +63 -19
- package/plugins/immune-brain/dist/imm-run-batch.md +398 -0
- package/plugins/immune-brain/dist/imm-run.md +89 -344
- package/plugins/immune-brain/dist/role-prompts/code-review.md +26 -2
- package/plugins/immune-brain/dist/role-prompts/executor.md +35 -10
- package/plugins/immune-brain/hooks/hooks.json +22 -2
- package/plugins/immune-brain/runtime/assurance/coordinator.ts +203 -13
- package/plugins/immune-brain/runtime/assurance/delivery_workspace.ts +2 -1
- package/plugins/immune-brain/runtime/assurance/enrollment_git_base.ts +99 -14
- package/plugins/immune-brain/runtime/assurance/phase_entry.ts +10 -26
- package/plugins/immune-brain/runtime/assurance/qa.ts +126 -43
- package/plugins/immune-brain/runtime/assurance/qa_findings.ts +8 -0
- package/plugins/immune-brain/runtime/assurance/review_evidence.ts +48 -0
- package/plugins/immune-brain/runtime/assurance/run_continuation.ts +284 -0
- package/plugins/immune-brain/runtime/assurance/verdict_authority.ts +46 -4
- package/plugins/immune-brain/runtime/assurance/verification.ts +83 -35
- package/plugins/immune-brain/runtime/assurance/workflow_autocommit.ts +222 -0
- package/plugins/immune-brain/runtime/claude/interaction.ts +11 -2
- package/plugins/immune-brain/runtime/claude/kernel_ports.ts +199 -48
- package/plugins/immune-brain/runtime/claude/lane_guard.ts +30 -20
- package/plugins/immune-brain/runtime/claude/mcp_server.ts +105 -9
- package/plugins/immune-brain/runtime/claude/run_continuation.ts +274 -0
- package/plugins/immune-brain/runtime/commands/kernel.ts +41 -103
- package/plugins/immune-brain/runtime/github_issue_tracker.ts +60 -2
- package/plugins/immune-brain/runtime/kernel/application.ts +4 -1
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +103 -20
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +57 -7
- package/plugins/immune-brain/runtime/kernel/completion.ts +28 -4
- package/plugins/immune-brain/runtime/kernel/enrollment.ts +48 -8
- package/plugins/immune-brain/runtime/kernel/intent.ts +169 -104
- package/plugins/immune-brain/runtime/kernel/reducer.ts +136 -6
- package/plugins/immune-brain/runtime/kernel/risk_floor.ts +286 -0
- package/plugins/immune-brain/runtime/kernel/storage_layout_migration.ts +83 -1
- package/plugins/immune-brain/runtime/kernel/storage_paths.ts +1 -1
- package/plugins/immune-brain/runtime/kernel/types.ts +49 -3
- package/plugins/immune-brain/runtime/kernel/validation.ts +147 -7
- package/plugins/immune-brain/runtime/loop_contract.ts +5 -0
- package/plugins/immune-brain/runtime/plan_core.ts +15 -8
- package/plugins/immune-brain/runtime/plugin_source_skew.ts +58 -0
- package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
- package/plugins/immune-brain/runtime/prompts/code-review.md +26 -2
- package/plugins/immune-brain/runtime/prompts/executor.md +35 -10
- package/plugins/immune-brain/runtime/unattended/batch_delegation.ts +5 -3
- package/plugins/immune-brain/runtime/unattended/batch_disposition.ts +40 -10
- package/plugins/immune-brain/runtime/unattended/batch_final_verification.ts +57 -3
- package/plugins/immune-brain/runtime/unattended/batch_git.ts +424 -66
- package/plugins/immune-brain/runtime/unattended/batch_integration.ts +2 -1
- package/plugins/immune-brain/runtime/unattended/batch_kernel_port.ts +13 -0
- package/plugins/immune-brain/runtime/unattended/batch_lanes.ts +43 -24
- package/plugins/immune-brain/runtime/unattended/batch_plan.ts +6 -2
- package/plugins/immune-brain/runtime/unattended/batch_preflight.ts +102 -41
- package/plugins/immune-brain/runtime/unattended/batch_reasons.ts +9 -3
- package/plugins/immune-brain/runtime/unattended/batch_reconfirmation.ts +9 -9
- package/plugins/immune-brain/runtime/unattended/batch_runner.ts +90 -32
- package/plugins/immune-brain/runtime/unattended/batch_state.ts +102 -4
- package/plugins/immune-brain/runtime/unattended/parent_delivery_consolidation.ts +248 -0
- package/plugins/immune-brain/runtime/workspace_scope.ts +126 -18
- package/plugins/immune-brain/skills/BASELINE.md +17 -1
- package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +4 -3
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +8 -2
- package/plugins/immune-brain/skills/imm-run/SKILL.md +12 -8
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { projectTaskStatus } from "../runtime/assurance/coordinator";
|
|
4
|
+
import { readTaskRecordRaw } from "../runtime/kernel/storage";
|
|
5
|
+
import type { PhaseEntry } from "../runtime/assurance/phase_entry";
|
|
6
|
+
import { recordParentRepair } from "../runtime/unattended/batch_git";
|
|
7
|
+
import { readAnyBatchRunState } from "../runtime/unattended/batch_state";
|
|
8
|
+
import { bindRun, decideRunContinuation, observeParentRun, parkFailedCriticalGate, releasesRunBinding, scopedSourceDigest,
|
|
9
|
+
type RunContinuationState, type ReviewContinuationState } from "../runtime/assurance/run_continuation";
|
|
10
|
+
|
|
11
|
+
type SettleBoundary = {
|
|
12
|
+
outcome: "completed" | "aborted" | "error";
|
|
13
|
+
continue: boolean;
|
|
14
|
+
context: { canContinue: boolean; pendingMessages: unknown[] };
|
|
15
|
+
};
|
|
16
|
+
type ContinuationBoundaryResult = { continue?: boolean; entries?: Array<{ type: "custom_message"; customType: string; display: boolean; content: string }> } | void;
|
|
17
|
+
|
|
18
|
+
/** Continuation is session memory, never a capability or durable task owner. */
|
|
19
|
+
export function registerPiRunContinuation(pi: ExtensionAPI, ports: {
|
|
20
|
+
entry(text: string, root: string): PhaseEntry | null;
|
|
21
|
+
}): { selectedParent(root: string, sessionId: string, slug: string): boolean } {
|
|
22
|
+
let entered = false;
|
|
23
|
+
let state: RunContinuationState | undefined;
|
|
24
|
+
let hostFailure: string | undefined;
|
|
25
|
+
const failedGates = new Set<string>();
|
|
26
|
+
let reviewRecovery: string | undefined;
|
|
27
|
+
let review: ReviewContinuationState = "unobserved";
|
|
28
|
+
let reviewerPrompt: string | undefined;
|
|
29
|
+
let reviewerCallId: string | undefined;
|
|
30
|
+
const calls = new Map<string, { name: string; task_id?: string; op?: string; initiative_slug?: string }>();
|
|
31
|
+
const reset = () => { entered = false; state = undefined; hostFailure = undefined; reviewRecovery = undefined;
|
|
32
|
+
review = "unobserved"; reviewerPrompt = undefined; reviewerCallId = undefined; calls.clear(); };
|
|
33
|
+
pi.on("session_start", () => { failedGates.clear(); reset(); });
|
|
34
|
+
pi.on("session_shutdown", () => { failedGates.clear(); reset(); });
|
|
35
|
+
pi.on("input", (event, ctx) => {
|
|
36
|
+
if (event.source === "extension") return;
|
|
37
|
+
const entry = ports.entry(event.text, ctx.cwd);
|
|
38
|
+
if (releasesRunBinding(event.text, entry === "plan" ? "planner" : entry)) { reset(); return; }
|
|
39
|
+
if (entry === "run") { failedGates.clear(); reset(); entered = true; }
|
|
40
|
+
});
|
|
41
|
+
pi.on("tool_call", (event) => {
|
|
42
|
+
const gateInput = event.input as { task_id?: unknown; initiative_slug?: unknown; action?: { op?: unknown } };
|
|
43
|
+
const gate = `${event.toolName}:${gateInput.task_id ?? gateInput.initiative_slug ?? ""}:${gateInput.action?.op ?? ""}`;
|
|
44
|
+
if (failedGates.has(gate)) return { block: true, reason: "This native gate failed or was cancelled; enter imm-run explicitly before retrying" };
|
|
45
|
+
if (!entered) return;
|
|
46
|
+
if (event.toolName === "imm_canary_enrollment" && state?.binding.kind === "parent" &&
|
|
47
|
+
state.binding.blocked_native_tasks?.includes(String(event.input.task_id)))
|
|
48
|
+
return { block: true, reason: "This critical gate is parked; continue independent work or resolve its same-Host recovery and enter imm-run explicitly" };
|
|
49
|
+
if (event.toolName === "Agent" && reviewerPrompt &&
|
|
50
|
+
(event.input.subagent_type === "Review" || event.input.prompt === reviewerPrompt)) {
|
|
51
|
+
if (event.input.prompt !== reviewerPrompt || event.input.subagent_type !== "Review" || event.input.run_in_background !== false)
|
|
52
|
+
return { block: true, reason: "Kernel Review requires the complete exact agent_params from advance_assurance: preserve prompt bytes, subagent_type Review, and run_in_background false. No reviewer was dispatched." };
|
|
53
|
+
if (reviewerCallId) { review = "rejected"; return { block: true, reason: "Reserved Reviewer already dispatched; wait for its result and submit the verdict before any new dispatch" }; }
|
|
54
|
+
reviewerCallId = event.toolCallId; review = "running"; return;
|
|
55
|
+
}
|
|
56
|
+
if (!["imm_canary_enrollment", "imm_kernel_canary", "start_unattended_batch"].includes(event.toolName)) return;
|
|
57
|
+
const input = event.input as { task_id?: unknown; initiative_slug?: unknown; action?: { op?: unknown } };
|
|
58
|
+
calls.set(event.toolCallId, { name: event.toolName,
|
|
59
|
+
task_id: typeof input.task_id === "string" ? input.task_id : undefined,
|
|
60
|
+
initiative_slug: typeof input.initiative_slug === "string" ? input.initiative_slug : undefined,
|
|
61
|
+
op: typeof input.action?.op === "string" ? input.action.op : undefined });
|
|
62
|
+
});
|
|
63
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
64
|
+
if (entered && event.toolName === "Agent" && event.toolCallId === reviewerCallId) review = "returned";
|
|
65
|
+
const call = calls.get(event.toolCallId); calls.delete(event.toolCallId);
|
|
66
|
+
if (!entered || !call) return;
|
|
67
|
+
if (call.name === "start_unattended_batch") {
|
|
68
|
+
const result = event.details as { state?: string; batch_id?: string | null; plan_digest?: string; final_verification_commands?: string[] } | undefined;
|
|
69
|
+
if (!event.isError && result?.state === "native_parent" && typeof result.plan_digest === "string" && call.initiative_slug) {
|
|
70
|
+
if (!state || state.binding.kind !== "parent") state = bindRun({ kind: "parent", workspace: realpathSync(ctx.cwd),
|
|
71
|
+
session_id: ctx.sessionManager.getSessionId(), initiative_slug: call.initiative_slug, batch_id: null,
|
|
72
|
+
plan_digest: result.plan_digest, final_verification: result.final_verification_commands });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (event.isError || result?.state !== "started" || !result.batch_id || !call.initiative_slug) {
|
|
76
|
+
failedGates.add(`${call.name}:${call.initiative_slug ?? ""}:`);
|
|
77
|
+
hostFailure = "Batch native operation did not apply; inspect the same-Host recovery"; return;
|
|
78
|
+
}
|
|
79
|
+
const record = readAnyBatchRunState(ctx.cwd, result.batch_id);
|
|
80
|
+
if (!record || record.initiative_slug !== call.initiative_slug) { hostFailure = "Batch result has no matching persisted identity"; return; }
|
|
81
|
+
if (!state || state.binding.kind !== "parent") state = bindRun({ kind: "parent", workspace: realpathSync(ctx.cwd),
|
|
82
|
+
session_id: ctx.sessionManager.getSessionId(), initiative_slug: record.initiative_slug, batch_id: record.batch_id, plan_digest: record.plan_digest });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!call.task_id) return;
|
|
86
|
+
const details = event.details as { state?: unknown; reason?: unknown; recovery_action?: unknown; next_action?: unknown;
|
|
87
|
+
agent_params?: { prompt?: unknown } } | undefined;
|
|
88
|
+
if ((event.isError || ["blocked", "cancelled", "rejected", "failed"].includes(String(details?.state))) &&
|
|
89
|
+
(call.name === "imm_canary_enrollment" || ["request_authorization", "approve_breaking_intent_revision", "request_stop"].includes(call.op ?? ""))) {
|
|
90
|
+
failedGates.add(`${call.name}:${call.task_id}:${call.op ?? ""}`);
|
|
91
|
+
hostFailure = typeof details?.reason === "string" ? details.reason : "Native authority operation failed or was cancelled";
|
|
92
|
+
if (call.name === "imm_canary_enrollment" && state && await parkFailedCriticalGate(state.binding, call.task_id)) hostFailure = undefined;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (call.op === "advance_assurance" && !event.isError && details?.state === "review_ready") {
|
|
96
|
+
if (typeof details.agent_params?.prompt === "string" && details.agent_params.prompt !== reviewerPrompt) {
|
|
97
|
+
reviewerPrompt = details.agent_params.prompt; reviewerCallId = undefined; review = "ready";
|
|
98
|
+
}
|
|
99
|
+
reviewRecovery = undefined;
|
|
100
|
+
}
|
|
101
|
+
if (call.op === "submit_review") {
|
|
102
|
+
if (event.isError) {
|
|
103
|
+
review = "rejected";
|
|
104
|
+
const action = details?.recovery_action ?? details?.next_action;
|
|
105
|
+
reviewRecovery = typeof action === "string" ? action : undefined;
|
|
106
|
+
} else {
|
|
107
|
+
review = "unobserved"; reviewerPrompt = undefined; reviewerCallId = undefined; reviewRecovery = undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (call.name === "imm_canary_enrollment" && !event.isError && details?.state === "completed" && state?.binding.kind === "parent")
|
|
111
|
+
await recordParentRepair(ctx.cwd, state.binding.batch_id, call.task_id, state.binding.initiative_slug);
|
|
112
|
+
if (state || event.isError) return;
|
|
113
|
+
const status = await projectTaskStatus(ctx.cwd, call.task_id);
|
|
114
|
+
if (status.error || status.claim?.task_id !== call.task_id || status.projection.lifecycle !== "active" || !status.projection.run_id) return;
|
|
115
|
+
state = bindRun({ kind: "task", workspace: realpathSync(ctx.cwd), session_id: ctx.sessionManager.getSessionId(),
|
|
116
|
+
task_id: call.task_id, run_id: status.projection.run_id });
|
|
117
|
+
});
|
|
118
|
+
// shortcut: optional workspace SDK types predate Pi 1.2; remove this declaration when that peer reaches 1.2 (owner: Immune-Brain).
|
|
119
|
+
const boundaryApi = pi as ExtensionAPI & { on(event: "agent_before_settle", handler: (event: SettleBoundary, ctx: ExtensionContext) => Promise<ContinuationBoundaryResult>): void };
|
|
120
|
+
boundaryApi.on("agent_before_settle", async (event, ctx) => {
|
|
121
|
+
if (!entered || !state) return;
|
|
122
|
+
if (event.outcome !== "completed" || !event.context.canContinue) { reset(); return; }
|
|
123
|
+
if (event.continue || event.context.pendingMessages.length) return;
|
|
124
|
+
try {
|
|
125
|
+
if (state.binding.kind === "parent") {
|
|
126
|
+
const decision = decideRunContinuation(state, { ...await observeParentRun(state.binding,
|
|
127
|
+
ctx.sessionManager.getLeafId() ?? "empty-session", review, reviewRecovery), host_failure: hostFailure });
|
|
128
|
+
state = decision.state;
|
|
129
|
+
if (decision.action === "settled" || decision.action === "unbound") { reset(); return; }
|
|
130
|
+
if (decision.action === "wait") return;
|
|
131
|
+
if (decision.action === "pause") { reset(); return { entries: [{ type: "custom_message", customType: "immune-brain-run-paused", display: true, content: `Parent paused: ${decision.reason}. ${decision.next_action ?? ""}` }] }; }
|
|
132
|
+
return { continue: true, entries: [{ type: "custom_message", customType: "immune-brain-run-continuation", display: false,
|
|
133
|
+
content: `Parent remains incomplete. ${decision.next_action}. Continue the authorized Loop and batch; individual Slice settlement does not complete the Parent.` }] };
|
|
134
|
+
}
|
|
135
|
+
const taskId = state.binding.task_id;
|
|
136
|
+
const status = await projectTaskStatus(ctx.cwd, taskId);
|
|
137
|
+
const record = await readTaskRecordRaw(ctx.cwd, taskId);
|
|
138
|
+
if (!record.record && status.projection.lifecycle === "active") throw new Error("bound run has no live TaskRecord");
|
|
139
|
+
const decision = decideRunContinuation(state, { kind: "task", workspace: realpathSync(ctx.cwd),
|
|
140
|
+
session_id: ctx.sessionManager.getSessionId(), boundary_id: ctx.sessionManager.getLeafId() ?? "empty-session",
|
|
141
|
+
status, source_digest: record.record ? scopedSourceDigest(ctx.cwd, record.record.intent_snapshot.scope_hint) : "terminal",
|
|
142
|
+
review, recovery_action: reviewRecovery, host_failure: hostFailure });
|
|
143
|
+
state = decision.state;
|
|
144
|
+
if (decision.action === "settled" || decision.action === "unbound") { reset(); return; }
|
|
145
|
+
if (decision.action === "wait") return;
|
|
146
|
+
if (decision.action === "pause") {
|
|
147
|
+
reset();
|
|
148
|
+
return { entries: [{ type: "custom_message" as const, customType: "immune-brain-run-paused", display: true,
|
|
149
|
+
content: `Run paused: ${decision.reason}${decision.next_action ? `. Recovery: ${decision.next_action}` : ""}` }] };
|
|
150
|
+
}
|
|
151
|
+
return { continue: true, entries: [{ type: "custom_message" as const, customType: "immune-brain-run-continuation", display: false,
|
|
152
|
+
content: `Explicit imm-run remains bound to task ${taskId}, run ${state.binding.kind === "task" ? state.binding.run_id : ""}. Kernel settlement is incomplete. ${decision.next_action}. Continue the authorized Loop; report completion only after authoritative settlement.` }] };
|
|
153
|
+
} catch (error) {
|
|
154
|
+
reset();
|
|
155
|
+
return { entries: [{ type: "custom_message" as const, customType: "immune-brain-run-paused", display: true,
|
|
156
|
+
content: `Run paused because continuation facts could not be verified: ${error instanceof Error ? error.message : String(error)}` }] };
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return { selectedParent: (root, sessionId, slug) => entered && state?.binding.kind === "parent" &&
|
|
160
|
+
state.binding.workspace === realpathSync(root) && state.binding.session_id === sessionId && state.binding.initiative_slug === slug };
|
|
161
|
+
}
|
|
@@ -52,6 +52,27 @@ cite the acceptance whose principle it violates as `acceptance_id` and
|
|
|
52
52
|
to be written into the acceptance text; a missing literal clause is not itself a
|
|
53
53
|
finding and does not call for an Intent revision.
|
|
54
54
|
|
|
55
|
+
## Trigger and Threat Model
|
|
56
|
+
|
|
57
|
+
`evidence.trigger` must read "concrete input or state → observable wrong
|
|
58
|
+
result". A finding that cannot name the wrong result is not blocking. Judge
|
|
59
|
+
against the threat model and supported environment declared in the Spec or
|
|
60
|
+
TaskIntent goal: a defect that needs an attacker, input source, repository
|
|
61
|
+
format, encoding, or platform outside that declaration is advisory. When
|
|
62
|
+
nothing is declared, assume inputs come from the repository's own tooling and
|
|
63
|
+
the user's trusted workspace. `security_boundary` means a security, data-loss,
|
|
64
|
+
or authority boundary the change actually crosses.
|
|
65
|
+
|
|
66
|
+
From Review round 3 the manifest carries `prior_review_commit` and
|
|
67
|
+
`delta_paths`: the paths the latest rework changed. Review that delta and its
|
|
68
|
+
direct interactions. A blocking finding is admitted only when at least one
|
|
69
|
+
`caller_chain` entry names a path in `delta_paths`, or `violated.kind` is
|
|
70
|
+
`security_boundary`; an edge case that already existed in round 1 is advisory
|
|
71
|
+
unless it crosses a security, data-loss, or authority boundary. A verdict that
|
|
72
|
+
blocks outside the delta is rejected for resubmission. When the manifest carries
|
|
73
|
+
`delta_skipped` instead, no delta exists for the round and this rule does not
|
|
74
|
+
apply.
|
|
75
|
+
|
|
55
76
|
## Invariant and Evidence Coverage
|
|
56
77
|
|
|
57
78
|
Judge whether the change actually closes the invariant its acceptance names,
|
|
@@ -101,7 +122,7 @@ contract: `contract`, `role`, `task_id`, `snapshot_digest`, `decision` (`pass`
|
|
|
101
122
|
or `rework`), and for `pass` include `approval` (`kind`, `authority_role`,
|
|
102
123
|
`summary`), for `rework` include `findings` (`id`, `kind`, `acceptance_id`,
|
|
103
124
|
`summary`, `evidence`). Every rework finding's `evidence` must carry
|
|
104
|
-
`trigger` (the concrete
|
|
125
|
+
`trigger` (the concrete input or state that reaches the defect and the observable wrong result it produces), a non-empty
|
|
105
126
|
`caller_chain` (ordered repository paths or symbols), and `violated`
|
|
106
127
|
(`kind`: `acceptance` or `security_boundary`, `ref`: the acceptance id or
|
|
107
128
|
boundary name). Do not invent fields, and never send an anchor yourself: the
|
|
@@ -110,7 +131,10 @@ violated.ref, caller_chain}`, so an identical claim keeps one stable identity
|
|
|
110
131
|
across review rounds while a different call chain is a different claim. A
|
|
111
132
|
passing review carries no blocking findings; non-blocking notes may ride along as
|
|
112
133
|
`kind: "advisory"` findings, and every finding, advisory included, needs the
|
|
113
|
-
same machine-checkable evidence.
|
|
134
|
+
same machine-checkable evidence. Advisory notes are archived on a pass and never
|
|
135
|
+
become Executor work. A rework carries only `kind: "blocking"` findings, at
|
|
136
|
+
least one: the coordinator rejects a rework with an advisory finding or without
|
|
137
|
+
a blocking one, so submit `pass` with `inspected_paths` when nothing blocks. If the
|
|
114
138
|
checkpoint is `awaiting_user_successor_decision`, stop without dispatch; only
|
|
115
139
|
a literal user may invoke `--approve-successor`.
|
|
116
140
|
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
their own existing grant. An explicit approval already covering the same
|
|
16
16
|
operation, target, and impact — including a valid batch capability — stays
|
|
17
17
|
usable without another gate.
|
|
18
|
+
- A precondition commit is the one commit the runtime makes without a separate
|
|
19
|
+
grant. When an `imm-run` precondition would fail only because paths are
|
|
20
|
+
uncommitted, the runtime commits that precondition's committable set, an
|
|
21
|
+
explicit path list, silently and continues. It runs Git hooks normally and
|
|
22
|
+
grants no push, publication, history rewrite, or commit of any other path. A
|
|
23
|
+
model never makes this commit itself and never asks the user to make it.
|
|
18
24
|
- Before a new outbound effect, check the project's existing research channel and
|
|
19
25
|
data scope. A public article, a mock, a documentation example, or a local
|
|
20
26
|
read-only database gives no authority to call an arbitrary provider or send new
|
|
@@ -55,11 +61,19 @@ framing does not load that execution guidance.
|
|
|
55
61
|
|
|
56
62
|
Ordinary host input stays host-native and does not run natural-language Managed
|
|
57
63
|
routing. A new Managed workflow starts only from explicit `imm-brainstorm`,
|
|
58
|
-
`imm-planner`, or `imm-run` Skill entry.
|
|
64
|
+
`imm-planner`, or `imm-run` Skill entry. For `imm-planner` and `imm-brainstorm`
|
|
65
|
+
the user's request for that planning or clarification, by slash command or in
|
|
66
|
+
natural language, is such an entry; a request that asks for neither stays
|
|
67
|
+
host-native. `imm-run` starts only from a literal-user entry. A model-entered
|
|
68
|
+
Planner or Brainstorm leaves the session's run entry unchanged.
|
|
59
69
|
|
|
60
70
|
1. **Continue an existing Managed owner explicitly**: an active Assurance
|
|
61
71
|
projection, TaskIntent, TaskRecord, reviewer `follow_up`, or other nonterminal
|
|
62
72
|
Managed owner remains authoritative; the user resumes it with `imm-run`.
|
|
73
|
+
Once that entry binds this session, ordinary status questions and in-scope
|
|
74
|
+
clarification retain automatic response continuation. Explicit cancel, planning
|
|
75
|
+
entry or session replacement releases the binding; Host continuation metadata
|
|
76
|
+
remains non-authoritative.
|
|
63
77
|
2. **Start explicitly**: the selected Immune-Brain Skill owns its planning or
|
|
64
78
|
coordination work. It creates only requested artifacts and their required
|
|
65
79
|
parent directories. Explicit Planner entry also owns absent routing-policy
|
|
@@ -112,6 +126,8 @@ Routine Managed enrollment is the first step of an explicit `imm-run` entry and
|
|
|
112
126
|
|
|
113
127
|
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.
|
|
114
128
|
|
|
129
|
+
Before Enrollment prepares its binding, the Host commits the Enrollment committable set when an untracked TaskIntent would otherwise fail it. The set is the task's own TaskIntent and the Spec its binding names. In-scope changes that already exist are not committed: the Enrollment gate lists them and only the listed paths become task work on confirmation, so a dirty scope is never claimed without that list being shown. The commit is silent, needs no separate confirmation, and the native confirmation binds the HEAD that results. When no precondition fails, no commit is made. Dirty paths outside the set are left as they are and do not block Enrollment. With an unborn HEAD the empty root commit comes first and this commit follows it. A commit Git refuses fails Enrollment with Git's message and is never retried with hooks bypassed; if Enrollment is declined, cancelled or fails afterwards, report the remaining commit.
|
|
130
|
+
|
|
115
131
|
## Parallel Read-Only Dispatch
|
|
116
132
|
|
|
117
133
|
State mutations, step activations, QA decisions, and plan switches remain
|