immune-brain 3.2.1 → 3.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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +5 -3
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +19 -73
- package/plugins/immune-brain/.pi-extension/pi-canary-interaction.ts +1 -2
- package/plugins/immune-brain/.pi-extension/runtime-stub.ts +2 -2
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +27 -85
- package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +1 -1
- package/plugins/immune-brain/dist/imm-agent-doc-maintain.md +28 -13
- package/plugins/immune-brain/dist/imm-loop.md +25 -16
- package/plugins/immune-brain/dist/imm-planner.md +1 -1
- package/plugins/immune-brain/runtime/assurance/coordinator.ts +0 -8
- package/plugins/immune-brain/runtime/claude/interaction.ts +0 -1
- package/plugins/immune-brain/runtime/claude/kernel_ports.ts +5 -24
- package/plugins/immune-brain/runtime/claude/mcp_server.ts +12 -8
- package/plugins/immune-brain/runtime/kernel/application.ts +0 -1
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +1 -7
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +0 -7
- package/plugins/immune-brain/runtime/kernel/completion.ts +1 -3
- package/plugins/immune-brain/runtime/kernel/reducer.ts +0 -31
- package/plugins/immune-brain/runtime/kernel/types.ts +0 -2
- package/plugins/immune-brain/runtime/kernel/validation.ts +1 -3
- package/plugins/immune-brain/runtime/plugin_version.ts +2 -0
- package/plugins/immune-brain/skills/imm-loop/SKILL.md +3 -45
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +3 -226
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: imm-loop
|
|
3
|
-
description: Use
|
|
3
|
+
description: Use to run an enrolled TaskIntent to completion through Kernel-governed execution, QA, and Review.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Immune-Brain: Loop
|
|
@@ -52,7 +52,7 @@ Repeat this sequence; do not silently stop while a valid action remains:
|
|
|
52
52
|
1. Call `imm_loop_action` with `op: route` (or `dispatch_role` at a QA/review boundary) and follow the projected `next` authority.
|
|
53
53
|
2. Emit one progress line: `[target][phase] result | next: action`.
|
|
54
54
|
3. Execute exactly one allowed action:
|
|
55
|
-
- Kernel ownership: call `imm_kernel_canary` for that owned task. Freeze the completed artifacts, then call `advance_assurance`; when it returns `review_ready`, invoke the foreground reviewer and pass its structured verdict to `submit_review`. When the projection calls for `request_authorization
|
|
55
|
+
- Kernel ownership: call `imm_kernel_canary` for that owned task. Freeze the completed artifacts, then call `advance_assurance`; when it returns `review_ready`, invoke the foreground reviewer and pass its structured verdict to `submit_review`. When the projection calls for `request_authorization` or `approve_breaking_intent_revision`, invoke the exact Tool operation directly without asking the user for chat pre-confirmation; the native host interaction is the single authority decision. Invoke `repair_authority_state` directly for a proven stale claim; Kernel revalidation removes only the redundant claim without user interaction.
|
|
56
56
|
- Active Step / `rework_needed`: follow the returned `executor` context in the current conversation, implement only the active Step or pending same-boundary `follow_up`, verify, record structured execution evidence through the Loop runtime action, and continue. A bounded test-only repair may request internal `test-fixer` with `focus_delta.specific_changes`; PR review or CI repair may request internal `pr-fix` with the current `plan_id`, changed-file boundary, and verification. Both return child evidence to the Parent and cannot widen scope.
|
|
57
57
|
- `awaiting_qa_decision`: call `imm_loop_action` with `op: dispatch_role`, role `qa`, the current projection, Plan verification, recorded evidence, and current target identity. Invoke the returned foreground Agent envelope exactly. A `rework` or `replan` must carry validated `notes`.
|
|
58
58
|
- `review_required`: map the exact `pending_review_gate` (`imm-code-review` or `imm-ui-review`) to the internal `code-review` or `ui-review` role and call `imm_loop_action` with `op: dispatch_role`, passing `pending_review_gate`, `review_changed_files`, and `review_changed_files_signature`. Invoke the returned foreground Agent envelope exactly. Record a validated pass, or open a same-boundary follow-up through the Loop runtime action.
|
|
@@ -141,17 +141,26 @@ Default user-facing shape: checkpoint progress lines, then `Conclusion -> Eviden
|
|
|
141
141
|
When the Kernel projection reports an active/draining backend claim, keep
|
|
142
142
|
`imm-loop` as the user-facing entry and call the `imm_kernel_canary` Tool for
|
|
143
143
|
that owned task. Enrollment uses the `imm_canary_enrollment` Tool and Review
|
|
144
|
-
authorization remains a native TUI gate
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
Invalid or contradictory projections fail closed. After
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
144
|
+
authorization remains a native TUI gate. When the projection calls for
|
|
145
|
+
`request_authorization` or `approve_breaking_intent_revision`, invoke the exact
|
|
146
|
+
Tool operation directly without asking the user for chat pre-confirmation; the
|
|
147
|
+
native host interaction is the single authority decision. Invoke
|
|
148
|
+
`repair_authority_state` directly for a proven stale claim; Kernel revalidation
|
|
149
|
+
removes only the redundant claim without user interaction. Do not invoke the
|
|
150
|
+
removed `imm-canary-work` Skill as
|
|
151
|
+
a separate entry point. Invalid or contradictory projections fail closed. After
|
|
152
|
+
implementation and focused verification, freeze the artifacts and call
|
|
153
|
+
`advance_assurance`. If it returns `review_ready`, invoke the foreground
|
|
154
|
+
reviewer and pass its structured verdict to `submit_review`;
|
|
155
|
+
`request_authorization` is reserved for an unresolved user decision or an
|
|
156
|
+
explicit stop. Critical work completes after fresh QA and Review without a
|
|
157
|
+
second user confirmation.
|
|
158
|
+
Every QA/Review operation stays foreground and returns its next projected
|
|
159
|
+
obligation directly to the Parent. The host performs any opted-in GitHub Issue
|
|
160
|
+
projection only after the corresponding authority mutation: only a fresh
|
|
161
|
+
claimless `done`/`stopped` projection with its exact terminal tombstone projects
|
|
162
|
+
terminal closure (`completed`/`not planned`); Enrollment performs no GitHub
|
|
163
|
+
projection. Treat the attached tracker result as non-authoritative observation.
|
|
164
|
+
Report its failure separately, but never use it as evidence, a stop condition,
|
|
165
|
+
or a reason to repeat a Kernel mutation. A terminal tombstone alone never
|
|
166
|
+
blocks unrelated v3 routing.
|
|
@@ -133,7 +133,6 @@ export interface ForegroundToolUpdate {
|
|
|
133
133
|
|
|
134
134
|
export type AssuranceAdvanceResult =
|
|
135
135
|
| { state: "review_ready"; operation: "review"; operation_id: string; snapshot_digest: string; review_bundle_digest: string; agent_params: unknown }
|
|
136
|
-
| { state: "awaiting_user"; operation: "record-user-approval"; operation_id: string }
|
|
137
136
|
| { state: "rework"; operation: "qa"; operation_id: string; summary: string }
|
|
138
137
|
| { state: "cancelled"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
139
138
|
| { state: "failed"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
@@ -150,7 +149,6 @@ export type AssuranceAdvanceResult =
|
|
|
150
149
|
|
|
151
150
|
export type AssuranceSubmitReviewResult =
|
|
152
151
|
| { state: "rework"; operation: "review"; operation_id: string; summary: string }
|
|
153
|
-
| { state: "awaiting_user"; operation: "record-user-approval"; operation_id: string }
|
|
154
152
|
| { state: "review_preparation_failed"; operation: "review"; operation_id: string; reason: string }
|
|
155
153
|
| { state: "completed" }
|
|
156
154
|
| { state: "settlement_unknown"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
@@ -528,8 +526,6 @@ export class AssuranceCoordinator {
|
|
|
528
526
|
return this.unknownAfterCommit(taskId, "qa", operationId, boundedAssuranceError(error));
|
|
529
527
|
}
|
|
530
528
|
}
|
|
531
|
-
if (projection.projection.next_obligation === "authorize_user")
|
|
532
|
-
return { state: "awaiting_user", operation: "record-user-approval", operation_id: operationId };
|
|
533
529
|
if (projection.projection.next_obligation !== "run_qa" && projection.projection.next_obligation !== "run_review")
|
|
534
530
|
return { state: "blocked", reason: `Kernel requires ${projection.projection.next_obligation}` };
|
|
535
531
|
const qaAlreadySettled = projection.projection.next_obligation === "run_review";
|
|
@@ -623,8 +619,6 @@ export class AssuranceCoordinator {
|
|
|
623
619
|
return this.unknownAfterCommit(taskId, "qa", operationId, boundedAssuranceError(error));
|
|
624
620
|
}
|
|
625
621
|
}
|
|
626
|
-
if (fresh.projection.next_obligation === "authorize_user")
|
|
627
|
-
return { state: "awaiting_user", operation: "record-user-approval", operation_id: operationId };
|
|
628
622
|
if (fresh.projection.next_obligation !== "run_review") {
|
|
629
623
|
if (authorityCommitted && aborted()) return this.unknownAfterCommit(taskId, "qa", operationId, "QA settlement projection did not require Review after cancellation");
|
|
630
624
|
return { state: "blocked", reason: `Kernel requires ${fresh.projection.next_obligation} after QA` };
|
|
@@ -777,8 +771,6 @@ export class AssuranceCoordinator {
|
|
|
777
771
|
return this.unknownAfterCommit(taskId, "review", reservation.operationId, boundedAssuranceError(error));
|
|
778
772
|
}
|
|
779
773
|
}
|
|
780
|
-
if (settled.projection.next_obligation === "authorize_user")
|
|
781
|
-
return { state: "awaiting_user", operation: "record-user-approval", operation_id: reservation.operationId };
|
|
782
774
|
return { state: "blocked", reason: `Kernel requires ${settled.projection.next_obligation} after Review` };
|
|
783
775
|
}
|
|
784
776
|
|
|
@@ -493,21 +493,16 @@ export class ClaudeRuntime {
|
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
async authorize(taskId: string, operation: string, meta: ToolMeta, extra: Record<string, unknown> = {}) {
|
|
496
|
-
if (!isPrivilegedOperation(operation) && operation !== "request_authorization") throw new Error(`unsupported privileged operation ${operation}`);
|
|
497
|
-
this.rejectBeforePreparation(operation, { ...meta, taskId });
|
|
498
496
|
if (operation === "repair_authority_state") {
|
|
499
497
|
const authority = reconcileKernelAuthority(this.cwd, taskId);
|
|
500
498
|
if (authority.state !== "repairable_stale_claim" || authority.owner_task_id !== taskId) {
|
|
501
499
|
throw new Error(authority.diagnostic ?? "authority repair requires a repairable stale claim");
|
|
502
500
|
}
|
|
503
|
-
|
|
504
|
-
const current = reconcileKernelAuthority(this.cwd, taskId);
|
|
505
|
-
if (current.state !== authority.state || current.owner_task_id !== authority.owner_task_id || current.revision !== authority.revision) {
|
|
506
|
-
throw new Error("authority changed after native confirmation; repair aborted before capability issuance");
|
|
507
|
-
}
|
|
508
|
-
return repairKernelAuthority(this.cwd, taskId, current.revision);
|
|
501
|
+
return repairKernelAuthority(this.cwd, taskId, authority.revision);
|
|
509
502
|
}
|
|
510
|
-
|
|
503
|
+
if (!isPrivilegedOperation(operation) && operation !== "request_authorization") throw new Error(`unsupported privileged operation ${operation}`);
|
|
504
|
+
this.rejectBeforePreparation(operation, { ...meta, taskId });
|
|
505
|
+
let op = operation;
|
|
511
506
|
let decisionOp: { finding_id: string; resolution: string } | undefined;
|
|
512
507
|
const projection = await this.status(taskId);
|
|
513
508
|
if (projection.error || !projection.claim) throw new Error(projection.error ?? "no active backend claim");
|
|
@@ -524,7 +519,7 @@ export class ClaudeRuntime {
|
|
|
524
519
|
if (open.length !== 1) throw new Error(`resolve-user-decision requires exactly one open user decision; found ${open.length}`);
|
|
525
520
|
op = "resolve_user_decision";
|
|
526
521
|
decisionOp = { finding_id: open[0].id, resolution: `resume after literal-user decision: ${open[0].summary}` };
|
|
527
|
-
} else
|
|
522
|
+
} else {
|
|
528
523
|
throw new Error(readiness.blocked ?? "no unique host-derived authorization operation");
|
|
529
524
|
}
|
|
530
525
|
}
|
|
@@ -591,18 +586,6 @@ export class ClaudeRuntime {
|
|
|
591
586
|
}
|
|
592
587
|
const { registry, app } = this.authority();
|
|
593
588
|
const confirmation = gate.confirmation_ref;
|
|
594
|
-
const approval = op === "record_user_approval"
|
|
595
|
-
? {
|
|
596
|
-
id: `approval-user-${randomUUID().slice(0, 8)}`,
|
|
597
|
-
kind: "user" as const,
|
|
598
|
-
authority_role: "user" as const,
|
|
599
|
-
task_revision: projection.projection.intent_revision,
|
|
600
|
-
intent_content_hash: projection.projection.intent_content_hash,
|
|
601
|
-
diff_hash: projection.projection.diff_hash,
|
|
602
|
-
actor_id: actorId,
|
|
603
|
-
summary: "literal user approval",
|
|
604
|
-
}
|
|
605
|
-
: undefined;
|
|
606
589
|
try {
|
|
607
590
|
const capabilityProjection = await this.status(taskId);
|
|
608
591
|
assertProjectionBinding(projection, capabilityProjection, Boolean(nextIntent));
|
|
@@ -621,7 +604,6 @@ export class ClaudeRuntime {
|
|
|
621
604
|
actor_id: actorId,
|
|
622
605
|
now,
|
|
623
606
|
confirmation_ref: confirmation,
|
|
624
|
-
...(approval ? { approval } : {}),
|
|
625
607
|
...(op === "approve_breaking_intent_revision" ? { next_intent: nextIntent, next_intent_ref: nextIntentRef } : {}),
|
|
626
608
|
...(op === "resolve_user_decision" && decisionOp ? decisionOp : {}),
|
|
627
609
|
...(op === "stop" ? { reason: extra.reason ?? "user stop" } : {}),
|
|
@@ -633,7 +615,6 @@ export class ClaudeRuntime {
|
|
|
633
615
|
op,
|
|
634
616
|
capability,
|
|
635
617
|
actor_id: actorId,
|
|
636
|
-
...(approval ? { approval } : {}),
|
|
637
618
|
...(op === "approve_breaking_intent_revision" ? { next_intent: nextIntent, next_intent_ref: nextIntentRef } : {}),
|
|
638
619
|
...(op === "resolve_user_decision" && decisionOp ? decisionOp : {}),
|
|
639
620
|
...(op === "stop" ? { reason: extra.reason ?? "user stop" } : {}),
|
|
@@ -2,6 +2,7 @@ import { createInterface } from "node:readline";
|
|
|
2
2
|
import { stdin, stdout } from "node:process";
|
|
3
3
|
import type { Readable, Writable } from "node:stream";
|
|
4
4
|
import { CORE_CONTRACT, HOST_ID, MIN_CLAUDE_CODE_VERSION, probeHost } from "./capability";
|
|
5
|
+
import { PLUGIN_VERSION } from "../plugin_version";
|
|
5
6
|
import { isPrivilegedOperation, privilegedAnnotations, type NativeDecision } from "./interaction";
|
|
6
7
|
import { ClaudeReviewHost, FileHookEventLog, parseHookStdin } from "./review_host";
|
|
7
8
|
import { ClaudeRuntime, type ToolMeta } from "./kernel_ports";
|
|
@@ -15,7 +16,7 @@ export const TOOLS = [
|
|
|
15
16
|
{ name: "request_authorization", description: "Apply exact literal-user authorization.", privileged: true },
|
|
16
17
|
{ name: "approve_breaking_intent_revision", description: "Approve a breaking TaskIntent revision.", privileged: true },
|
|
17
18
|
{ name: "stop", description: "Stop the active task with literal-user authority.", privileged: true },
|
|
18
|
-
{ name: "repair_authority_state", description: "Repair a recoverable stale backend claim.", privileged:
|
|
19
|
+
{ name: "repair_authority_state", description: "Repair a proven recoverable stale backend claim.", privileged: false },
|
|
19
20
|
] as const;
|
|
20
21
|
|
|
21
22
|
export function listMcpTools() {
|
|
@@ -75,16 +76,14 @@ export function createMcpRuntime(options: McpRuntimeOptions = {}) {
|
|
|
75
76
|
if ("native_decision" in args) throw new Error("native_decision cannot be supplied in tool arguments");
|
|
76
77
|
// Read-only status must stay usable without trusted Host evidence, so it
|
|
77
78
|
// is dispatched before the capability probe rejects unversioned hosts.
|
|
78
|
-
if (name === "status") return runtime.status(taskId);
|
|
79
|
+
if (name === "status") return { plugin_version: PLUGIN_VERSION, ...(await runtime.status(taskId)) };
|
|
79
80
|
// The environment version fallback is disabled on this connection:
|
|
80
81
|
// only a version bound from the trusted initialize handshake may
|
|
81
82
|
// reach authority-mutating tools.
|
|
82
83
|
if (!negotiatedVersion) throw new Error("Claude Code version is unavailable");
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
// Kernel state without native interaction capability.
|
|
87
|
-
if (!negotiatedInteractive) throw new Error("non-interactive host session cannot execute authority tools");
|
|
84
|
+
// Automatic stale-claim repair is deterministic and needs no native
|
|
85
|
+
// interaction; other mutations retain the Host capability requirement.
|
|
86
|
+
if (!negotiatedInteractive && name !== "repair_authority_state") throw new Error("non-interactive host session cannot execute authority tools");
|
|
88
87
|
const probe = probeHost(options.env ?? process.env, process.platform, negotiatedVersion);
|
|
89
88
|
if (!probe.ok) throw new Error(probe.reason);
|
|
90
89
|
const toolMeta: ToolMeta = {
|
|
@@ -168,7 +167,12 @@ export async function handleJsonRpc(message: JsonRpc, mcp = createMcpRuntime()):
|
|
|
168
167
|
id: message.id ?? null,
|
|
169
168
|
result: {
|
|
170
169
|
protocolVersion: "2024-11-05",
|
|
171
|
-
serverInfo: {
|
|
170
|
+
serverInfo: {
|
|
171
|
+
name: HOST_ID,
|
|
172
|
+
version: PLUGIN_VERSION,
|
|
173
|
+
contract: CORE_CONTRACT,
|
|
174
|
+
minimumHostVersion: MIN_CLAUDE_CODE_VERSION,
|
|
175
|
+
},
|
|
172
176
|
capabilities: { tools: {} },
|
|
173
177
|
},
|
|
174
178
|
};
|
|
@@ -158,7 +158,6 @@ export function applyTaskAction(
|
|
|
158
158
|
|
|
159
159
|
const privileged =
|
|
160
160
|
action.type === "record_approval" ||
|
|
161
|
-
action.type === "record_user_approval" ||
|
|
162
161
|
action.type === "approve_breaking_intent_revision" ||
|
|
163
162
|
action.type === "request_rework" ||
|
|
164
163
|
action.type === "stop" ||
|
|
@@ -25,13 +25,9 @@ export interface AssuranceAuthorizationReadiness {
|
|
|
25
25
|
/**
|
|
26
26
|
* Kernel-decidable authorization readiness:
|
|
27
27
|
* - "resolve_user_decision": exactly one open unresolved-user-decision finding;
|
|
28
|
-
* - "record_user_approval": Kernel projects authorize_user after fresh QA
|
|
29
|
-
* and Review attestations for a critical task;
|
|
30
28
|
* - "none": nothing uniquely decidable from Kernel facts.
|
|
31
|
-
* A pending Pi native Review verdict is a session fact and is NOT visible
|
|
32
|
-
* here; the host composes it before this readiness.
|
|
33
29
|
*/
|
|
34
|
-
state: "resolve_user_decision" | "
|
|
30
|
+
state: "resolve_user_decision" | "none";
|
|
35
31
|
/** Non-null only when Kernel facts prove the authorization is blocked. */
|
|
36
32
|
blocked: string | null;
|
|
37
33
|
}
|
|
@@ -79,8 +75,6 @@ export function deriveAssuranceAuthorization(input: {
|
|
|
79
75
|
state: "none",
|
|
80
76
|
blocked: `resolve-user-decision requires exactly one open user decision; found ${input.open_user_decision_count}`,
|
|
81
77
|
};
|
|
82
|
-
if (input.next_obligation === "authorize_user")
|
|
83
|
-
return { state: "record_user_approval", blocked: null };
|
|
84
78
|
return { state: "none", blocked: null };
|
|
85
79
|
}
|
|
86
80
|
|
|
@@ -51,7 +51,6 @@ export type CanaryOperation =
|
|
|
51
51
|
| { op: "resolve_finding"; finding_id: string; actor_id: string }
|
|
52
52
|
| { op: "request_rework"; capability: object; findings: TaskFinding[]; actor_id: string }
|
|
53
53
|
| { op: "record_approval"; capability: object; approval: TaskApprovalV2; actor_id: string }
|
|
54
|
-
| { op: "record_user_approval"; capability: object; approval: TaskApprovalV2; actor_id: string }
|
|
55
54
|
| { op: "revise_intent"; next_intent: TaskIntentV1; actor_id: string }
|
|
56
55
|
| { op: "approve_breaking_intent_revision"; capability: object; next_intent: TaskIntentV1; actor_id: string }
|
|
57
56
|
| { op: "complete"; actor_id: string }
|
|
@@ -141,8 +140,6 @@ export function capabilityActionFor(input: {
|
|
|
141
140
|
switch (input.op) {
|
|
142
141
|
case "record_approval":
|
|
143
142
|
return { ...base, approval: input.approval } as TaskAction;
|
|
144
|
-
case "record_user_approval":
|
|
145
|
-
return { ...base, approval: input.approval } as TaskAction;
|
|
146
143
|
case "request_rework":
|
|
147
144
|
return { ...base, findings: input.findings } as TaskAction;
|
|
148
145
|
case "stop":
|
|
@@ -373,10 +370,6 @@ export function createCanaryApplication(
|
|
|
373
370
|
capability = operation.capability;
|
|
374
371
|
action = { ...base, type: "record_approval", approval: operation.approval };
|
|
375
372
|
break;
|
|
376
|
-
case "record_user_approval":
|
|
377
|
-
capability = operation.capability;
|
|
378
|
-
action = { ...base, type: "record_user_approval", approval: operation.approval };
|
|
379
|
-
break;
|
|
380
373
|
case "revise_intent":
|
|
381
374
|
action = {
|
|
382
375
|
...base,
|
|
@@ -12,7 +12,7 @@ import { assertKernelInvariantsV3, KernelInvariantError } from "./validation";
|
|
|
12
12
|
const REQUIRED_ATTESTATIONS: Record<TaskIntentV1["risk"], ApprovalKind[]> = {
|
|
13
13
|
routine: ["qa"],
|
|
14
14
|
material: ["qa", "review"],
|
|
15
|
-
critical: ["qa", "review"
|
|
15
|
+
critical: ["qa", "review"],
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
function archiveActivePlanningPath(path: string): string | null {
|
|
@@ -199,8 +199,6 @@ export function projectTask(
|
|
|
199
199
|
nextObligation = "run_qa";
|
|
200
200
|
} else if (decision.missing_approval_kinds.includes("review")) {
|
|
201
201
|
nextObligation = "run_review";
|
|
202
|
-
} else if (decision.missing_approval_kinds.includes("user")) {
|
|
203
|
-
nextObligation = "authorize_user";
|
|
204
202
|
} else if (decision.complete) {
|
|
205
203
|
nextObligation = "complete";
|
|
206
204
|
}
|
|
@@ -176,7 +176,6 @@ function intentRefMatches(intent: TaskIntentV1, ref: TaskIntentRefV3): boolean {
|
|
|
176
176
|
function hasPrivilegedKind(action: TaskAction): boolean {
|
|
177
177
|
return (
|
|
178
178
|
action.type === "record_approval" ||
|
|
179
|
-
action.type === "record_user_approval" ||
|
|
180
179
|
action.type === "approve_breaking_intent_revision" ||
|
|
181
180
|
action.type === "request_rework" ||
|
|
182
181
|
action.type === "stop" ||
|
|
@@ -374,36 +373,6 @@ export function reduceTask(
|
|
|
374
373
|
appendHistory(record, action, from, approval.id, authorityAudit);
|
|
375
374
|
break;
|
|
376
375
|
}
|
|
377
|
-
case "record_user_approval": {
|
|
378
|
-
if (record.lifecycle !== "active" || record.artifact_state !== "frozen")
|
|
379
|
-
throw new KernelInvariantError([
|
|
380
|
-
`cannot record user approval while state is ${stateOf(record)}`,
|
|
381
|
-
]);
|
|
382
|
-
const approval = action.approval;
|
|
383
|
-
if (approval.kind !== "user")
|
|
384
|
-
throw new KernelInvariantError([
|
|
385
|
-
"record_user_approval requires kind user",
|
|
386
|
-
]);
|
|
387
|
-
if (!authorityAudit || authorityAudit.authority_kind !== "user")
|
|
388
|
-
throw new KernelInvariantError([
|
|
389
|
-
"record_user_approval requires user authority",
|
|
390
|
-
]);
|
|
391
|
-
if (approval.task_revision !== record.intent_snapshot.revision)
|
|
392
|
-
throw new KernelInvariantError(["approval task_revision must equal the current intent revision"]);
|
|
393
|
-
if (approval.intent_content_hash !== record.intent_ref.content_hash)
|
|
394
|
-
throw new KernelInvariantError(["approval intent_content_hash must equal the current intent hash"]);
|
|
395
|
-
if (approval.diff_hash !== diffHash)
|
|
396
|
-
throw new KernelInvariantError(["approval diff_hash must equal the action diff hash"]);
|
|
397
|
-
if (record.attestations.some((item) => item.id === approval.id))
|
|
398
|
-
throw new KernelInvariantError([
|
|
399
|
-
`attestations contains duplicate id ${approval.id}`,
|
|
400
|
-
]);
|
|
401
|
-
if (approval.review_revision)
|
|
402
|
-
throw new KernelInvariantError(["review_revision is only valid on review approvals"]);
|
|
403
|
-
record.attestations.push({ ...approval, acceptance_results: [] });
|
|
404
|
-
appendHistory(record, action, from, approval.id, authorityAudit);
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
376
|
case "revise_intent":
|
|
408
377
|
case "approve_breaking_intent_revision": {
|
|
409
378
|
if (record.lifecycle !== "active")
|
|
@@ -57,7 +57,6 @@ export type AssuranceObligation =
|
|
|
57
57
|
| "submit_assurance"
|
|
58
58
|
| "run_qa"
|
|
59
59
|
| "run_review"
|
|
60
|
-
| "authorize_user"
|
|
61
60
|
| "complete"
|
|
62
61
|
| "none";
|
|
63
62
|
|
|
@@ -276,7 +275,6 @@ export type TaskAction =
|
|
|
276
275
|
| (TaskActionBase & { type: "record_finding"; finding: TaskFinding })
|
|
277
276
|
| (TaskActionBase & { type: "resolve_finding"; finding_id: string })
|
|
278
277
|
| (TaskActionBase & { type: "record_approval"; approval: TaskApprovalV2 })
|
|
279
|
-
| (TaskActionBase & { type: "record_user_approval"; approval: TaskApprovalV2 })
|
|
280
278
|
| (TaskActionBase & {
|
|
281
279
|
type: "revise_intent";
|
|
282
280
|
next_intent: TaskIntentV1;
|
|
@@ -730,7 +730,6 @@ const ACTION_V2_TYPES = [
|
|
|
730
730
|
"record_finding",
|
|
731
731
|
"resolve_finding",
|
|
732
732
|
"record_approval",
|
|
733
|
-
"record_user_approval",
|
|
734
733
|
"revise_intent",
|
|
735
734
|
"approve_breaking_intent_revision",
|
|
736
735
|
"request_rework",
|
|
@@ -831,8 +830,7 @@ export function parseTaskAction(raw: unknown): TaskAction {
|
|
|
831
830
|
};
|
|
832
831
|
break;
|
|
833
832
|
}
|
|
834
|
-
case "record_approval":
|
|
835
|
-
case "record_user_approval": {
|
|
833
|
+
case "record_approval": {
|
|
836
834
|
rejectUnknown(
|
|
837
835
|
value,
|
|
838
836
|
[...ACTION_BASE_FIELDS, "approval"],
|
|
@@ -1,51 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: imm-loop
|
|
3
|
-
description: Use to run
|
|
3
|
+
description: Use to run an enrolled TaskIntent to completion through Kernel-governed execution, QA, and Review.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Immune-Brain: Loop
|
|
7
7
|
|
|
8
|
-
Load [`../../dist/imm-loop.md`](../../dist/imm-loop.md), then
|
|
9
|
-
|
|
10
|
-
`route` for active Steps, bounded repair, architecture exploration, advisory
|
|
11
|
-
review, Compounder, Kernel ownership, or scope expansion. Use `dispatch_role`
|
|
12
|
-
for `qa`, `code-review`, and `ui-review`, then invoke the returned foreground
|
|
13
|
-
Agent envelope exactly. Brainstorm and Planner use the same Tool for bounded
|
|
14
|
-
`arch-explorer` and explicit-lens `advisory-reviewer` dispatches. Loop may
|
|
15
|
-
dispatch `compounder` only when a closed Step supplies structured evidence for
|
|
16
|
-
a reusable Learning; routine work without that evidence returns `next: none`
|
|
17
|
-
and creates no Learning. Do not discover or load a Pi Skill for these roles. The Managed Path public entries remain `imm-brainstorm`, `imm-planner`, and `imm-loop`; standalone `imm-pr-fix`, `imm-doc-prune`, and `imm-agent-doc-maintain` are host-native and are never dispatched as the Loop role.
|
|
18
|
-
Subagent Dispatch Protocol](../../dist/docs/reference/subagent-dispatch-protocol.md#authorization-authority).
|
|
19
|
-
All internal Agent dispatch envelopes use `run_in_background: false` and
|
|
20
|
-
return a direct result to the Parent before any workflow mutation.
|
|
21
|
-
|
|
22
|
-
At `terminal_plan_complete`, stop with no next skill, authority, or action. At `awaiting_user_successor_decision`, stop with `recommended_authority: user`. This boundary follows the explicit internal Compounder handoff and runtime terminal settlement; it must not dispatch Planner, Compounder, transition, or a new Pi session/subagent. Only a literal user may approve a successor through the native authority gate; the internal runtime token is `--approve-successor`, never a public Skill or user-facing entry.
|
|
23
|
-
|
|
24
|
-
Scope expansion always returns to `imm-planner`; Executor and repair roles must stop with the concrete missing scope and verification reason instead of widening execution.
|
|
25
|
-
|
|
26
|
-
The loop always enters through `imm_loop_action`: the projected action's `next`
|
|
27
|
-
authority is `executor`, `test-fixer`, `pr-fix`, `arch-explorer`,
|
|
28
|
-
`advisory-reviewer`, `compounder`, `imm_kernel_canary`, `imm-planner`, or
|
|
29
|
-
`none`.
|
|
30
|
-
|
|
31
|
-
When the Kernel projection reports an active/draining backend claim, keep
|
|
32
|
-
`imm-loop` as the user-facing entry and call the `imm_kernel_canary` Tool for
|
|
33
|
-
that owned task. Enrollment uses the `imm_canary_enrollment` Tool and Review
|
|
34
|
-
authorization remains a native TUI gate. When the projection calls for
|
|
35
|
-
`request_authorization`, `approve_breaking_intent_revision`, or
|
|
36
|
-
`repair_authority_state`, invoke the exact Tool operation directly without
|
|
37
|
-
asking the user for chat pre-confirmation; the native host interaction is the
|
|
38
|
-
single authority decision. Do not invoke the removed `imm-canary-work` Skill as
|
|
39
|
-
a separate entry point. Invalid or contradictory projections fail closed. After
|
|
40
|
-
implementation and focused verification, freeze the artifacts and call
|
|
41
|
-
`advance_assurance`. If it returns `review_ready`, invoke the foreground
|
|
42
|
-
reviewer and pass its structured verdict to `submit_review`; `request_authorization` remains the
|
|
43
|
-
critical-risk user authorization boundary. Every QA/Review operation stays
|
|
44
|
-
foreground and returns its next projected obligation directly to the Parent. The host performs any opted-in GitHub Issue projection only after the
|
|
45
|
-
corresponding authority mutation: only a fresh claimless `done`/`stopped`
|
|
46
|
-
projection with its exact terminal tombstone projects terminal closure
|
|
47
|
-
(`completed`/`not planned`); Enrollment performs no GitHub projection. Treat the
|
|
48
|
-
attached tracker result as non-authoritative observation. Report its failure
|
|
49
|
-
separately, but never use it as evidence, a stop condition, or a reason to
|
|
50
|
-
repeat a Kernel mutation. A terminal tombstone alone never blocks unrelated v3
|
|
51
|
-
routing.
|
|
8
|
+
Load [`../../dist/imm-loop.md`](../../dist/imm-loop.md), then follow that
|
|
9
|
+
canonical contract in the current host conversation.
|