immune-brain 3.6.9 → 4.0.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/package.json +3 -2
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/.pi-extension/imm-canary-enroll.ts +18 -2
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +76 -121
- package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +106 -600
- package/plugins/immune-brain/.pi-extension/pi-canary-assurance-progression.ts +1 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-verification.ts +3 -3
- package/plugins/immune-brain/.pi-extension/runtime-stub.ts +17 -43
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +7587 -5049
- package/plugins/immune-brain/dist/docs/reference/planning-artifact-retention.md +11 -12
- package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +1 -1
- package/plugins/immune-brain/dist/imm-loop.md +27 -25
- package/plugins/immune-brain/dist/imm-planner.md +34 -27
- package/plugins/immune-brain/dist/imm-review-retro.md +2 -2
- package/plugins/immune-brain/dist/role-prompts/code-review.md +3 -1
- package/plugins/immune-brain/dist/role-prompts/executor.md +4 -4
- package/plugins/immune-brain/runtime/assurance/coordinator.ts +183 -40
- package/plugins/immune-brain/runtime/assurance/delivery_workspace.ts +240 -0
- package/plugins/immune-brain/runtime/assurance/qa.ts +132 -58
- package/plugins/immune-brain/runtime/assurance/review_evidence.ts +15 -7
- package/plugins/immune-brain/runtime/assurance/verification.ts +246 -206
- package/plugins/immune-brain/runtime/authorization_operation.ts +20 -0
- package/plugins/immune-brain/runtime/claude/kernel_ports.ts +288 -721
- package/plugins/immune-brain/runtime/commands/kernel.ts +158 -67
- package/plugins/immune-brain/runtime/github_issue_tracker.ts +1 -1
- package/plugins/immune-brain/runtime/kernel/actor_identity.ts +33 -0
- package/plugins/immune-brain/runtime/kernel/application.ts +22 -6
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +94 -5
- package/plugins/immune-brain/runtime/kernel/authority_port.ts +27 -6
- package/plugins/immune-brain/runtime/kernel/backend_claim.ts +43 -16
- package/plugins/immune-brain/runtime/kernel/batch_authority.ts +10 -6
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +50 -63
- package/plugins/immune-brain/runtime/kernel/canary_eligibility.ts +13 -4
- package/plugins/immune-brain/runtime/kernel/completion.ts +5 -14
- package/plugins/immune-brain/runtime/kernel/enrollment.ts +124 -34
- package/plugins/immune-brain/runtime/kernel/enrollment_authority.ts +13 -5
- package/plugins/immune-brain/runtime/kernel/index.ts +3 -1
- package/plugins/immune-brain/runtime/kernel/intent.ts +7 -11
- package/plugins/immune-brain/runtime/kernel/legacy_audit.ts +4 -1
- package/plugins/immune-brain/runtime/kernel/legacy_task_record.ts +323 -0
- package/plugins/immune-brain/runtime/kernel/pi_canary_prepare.ts +10 -1
- package/plugins/immune-brain/runtime/kernel/reducer.ts +32 -31
- package/plugins/immune-brain/runtime/kernel/run_identity.ts +121 -0
- package/plugins/immune-brain/runtime/kernel/spec_binding.ts +100 -0
- package/plugins/immune-brain/runtime/kernel/sqlite_migration.ts +950 -0
- package/plugins/immune-brain/runtime/kernel/sqlite_store.ts +1193 -0
- package/plugins/immune-brain/runtime/kernel/storage.ts +1254 -1206
- package/plugins/immune-brain/runtime/kernel/storage_layout_migration.ts +129 -755
- package/plugins/immune-brain/runtime/kernel/storage_paths.ts +419 -46
- package/plugins/immune-brain/runtime/kernel/types.ts +12 -43
- package/plugins/immune-brain/runtime/kernel/validation.ts +60 -274
- package/plugins/immune-brain/runtime/managed_task_routing_policy.ts +0 -1
- package/plugins/immune-brain/runtime/plan_core.ts +27 -65
- package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
- package/plugins/immune-brain/runtime/prompts/code-review.md +3 -1
- package/plugins/immune-brain/runtime/prompts/executor.md +4 -4
- package/plugins/immune-brain/runtime/staged_intent.ts +58 -0
- package/plugins/immune-brain/runtime/unattended/batch_git.ts +37 -7
- package/plugins/immune-brain/runtime/unattended/batch_plan.ts +42 -2
- package/plugins/immune-brain/runtime/unattended/batch_preflight.ts +771 -0
- package/plugins/immune-brain/runtime/unattended/batch_reasons.ts +189 -0
- package/plugins/immune-brain/runtime/unattended/batch_runner.ts +35 -0
- package/plugins/immune-brain/runtime/unattended/confirmation_deadline.ts +33 -0
- package/plugins/immune-brain/runtime/unattended/types.ts +14 -1
- package/plugins/immune-brain/runtime/v4_runtime.ts +19 -23
- package/plugins/immune-brain/runtime/verification_descriptor.ts +92 -136
- package/plugins/immune-brain/runtime/workspace_scope.ts +98 -13
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +3 -3
- package/plugins/immune-brain/bin/imm-retire-stale-wrapper +0 -4
- package/plugins/immune-brain/bin/imm-retired +0 -4
- package/plugins/immune-brain/runtime/authority_commit_receipts.ts +0 -716
- package/plugins/immune-brain/runtime/kernel/automatic_observations.ts +0 -451
- package/plugins/immune-brain/runtime/kernel/legacy.ts +0 -299
- package/plugins/immune-brain/runtime/kernel/observation.ts +0 -397
- package/plugins/immune-brain/runtime/kernel/readiness.ts +0 -282
- package/plugins/immune-brain/runtime/kernel/readiness_evidence.ts +0 -132
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "immune-brain",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Immune-Brain agent skill system",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -72,9 +72,10 @@
|
|
|
72
72
|
"plugins/immune-brain/runtime/plan_core.ts",
|
|
73
73
|
"plugins/immune-brain/runtime/commands/kernel.ts",
|
|
74
74
|
"plugins/immune-brain/runtime/kernel",
|
|
75
|
+
"plugins/immune-brain/runtime/authorization_operation.ts",
|
|
75
76
|
"plugins/immune-brain/runtime/canonical_json.ts",
|
|
77
|
+
"plugins/immune-brain/runtime/staged_intent.ts",
|
|
76
78
|
"plugins/immune-brain/runtime/workspace_scope.ts",
|
|
77
|
-
"plugins/immune-brain/runtime/authority_commit_receipts.ts",
|
|
78
79
|
"plugins/immune-brain/runtime/managed_task_routing_policy.ts",
|
|
79
80
|
"plugins/immune-brain/runtime/github_issue_tracker.ts",
|
|
80
81
|
"plugins/immune-brain/runtime/verification_descriptor.ts",
|
|
@@ -14,6 +14,7 @@ import { resolve } from "node:path";
|
|
|
14
14
|
// can resolve them at runtime), and the stub forwards to the real Kernel
|
|
15
15
|
// modules via dynamic import.
|
|
16
16
|
import {
|
|
17
|
+
LITERAL_USER_ACTOR_ID,
|
|
17
18
|
createEnrollmentAuthorityRegistry,
|
|
18
19
|
preparePiCanary,
|
|
19
20
|
revalidatePiCanary,
|
|
@@ -408,7 +409,7 @@ async function executeForegroundEnrollment(
|
|
|
408
409
|
`Terminal task ${authority.owner_task_id} retains an exactly proven stale backend claim`,
|
|
409
410
|
`invoke imm_kernel_canary repair_authority_state for ${authority.owner_task_id}`,
|
|
410
411
|
);
|
|
411
|
-
if (authority.state === "authority_conflict"
|
|
412
|
+
if (authority.state === "authority_conflict")
|
|
412
413
|
return terminal(
|
|
413
414
|
action,
|
|
414
415
|
taskId,
|
|
@@ -417,6 +418,21 @@ async function executeForegroundEnrollment(
|
|
|
417
418
|
authority.diagnostic ?? `Kernel authority state is ${authority.state}`,
|
|
418
419
|
"inspect authority state; do not retry enrollment",
|
|
419
420
|
);
|
|
421
|
+
// terminal_owner with a local run is this worktree's settled task and
|
|
422
|
+
// forbids re-enrollment. terminal_owner *without* one is historical audit
|
|
423
|
+
// evidence (a fresh clone, or another worktree's run reaching here through
|
|
424
|
+
// Git): evidence is not local authority, and this worktree may enroll the
|
|
425
|
+
// task for the first time. The Kernel's own precondition check enforces
|
|
426
|
+
// the same rule against the local run.
|
|
427
|
+
if (authority.state === "terminal_owner" && authority.owner_run_id !== null)
|
|
428
|
+
return terminal(
|
|
429
|
+
action,
|
|
430
|
+
taskId,
|
|
431
|
+
"authority_conflict",
|
|
432
|
+
stage,
|
|
433
|
+
`task ${taskId} is terminal in this worktree`,
|
|
434
|
+
"inspect authority state; do not retry enrollment",
|
|
435
|
+
);
|
|
420
436
|
const preparation = await preparePiCanary(root, { task_id: taskId, now });
|
|
421
437
|
signal.throwIfAborted();
|
|
422
438
|
if (!preparation.intent)
|
|
@@ -523,7 +539,7 @@ async function executeForegroundEnrollment(
|
|
|
523
539
|
intent_revision: preparation.intent.revision,
|
|
524
540
|
intent_content_hash: preparation.intent.content_hash,
|
|
525
541
|
preparation_digest: preparation.digest,
|
|
526
|
-
actor_id:
|
|
542
|
+
actor_id: LITERAL_USER_ACTOR_ID,
|
|
527
543
|
confirmation_ref: `pi-confirm-${createHash("sha256").update(`${taskId}\0${now}\0${nonce}`).digest("hex").slice(0, 16)}`,
|
|
528
544
|
expires_at: new Date(Date.now() + 10 * 60 * 1000).toISOString(),
|
|
529
545
|
nonce,
|
|
@@ -23,11 +23,6 @@ import { Type } from "typebox";
|
|
|
23
23
|
import { PLUGIN_VERSION } from "../runtime/plugin_version";
|
|
24
24
|
import {
|
|
25
25
|
parseVerificationDescriptor,
|
|
26
|
-
canonicalDescriptorBytes,
|
|
27
|
-
resolveBunRunner,
|
|
28
|
-
assertRunnerCompatible,
|
|
29
|
-
findingsDigest,
|
|
30
|
-
type FrozenRunner,
|
|
31
26
|
type VerificationDescriptor,
|
|
32
27
|
} from "./pi-canary-verification";
|
|
33
28
|
import {
|
|
@@ -43,9 +38,9 @@ import {
|
|
|
43
38
|
import type { InvocationToken } from "./pi-canary-invocations";
|
|
44
39
|
import { runDeterministicQa } from "../runtime/assurance/qa";
|
|
45
40
|
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} from "
|
|
41
|
+
captureStagedIntent,
|
|
42
|
+
restoreStagedIntent as restoreStagedIntentShared,
|
|
43
|
+
} from "../runtime/staged_intent";
|
|
49
44
|
import {
|
|
50
45
|
renderCanaryCall,
|
|
51
46
|
renderCanaryResult,
|
|
@@ -71,13 +66,13 @@ import {
|
|
|
71
66
|
} from "./pi-canary-interaction";
|
|
72
67
|
import { isToolFailureState, throwToolFailure, type ToolFailureV1 } from "./pi-canary-tool-failure";
|
|
73
68
|
import { taskDiffIdentity, taskRevisionIdentity, captureGitTaskSnapshot } from "../runtime/workspace_scope";
|
|
74
|
-
import { reviewReworkFindings } from "../runtime/assurance/coordinator";
|
|
69
|
+
import { reviewAdvisoryRecords, reviewReworkFindings } from "../runtime/assurance/coordinator";
|
|
75
70
|
import {
|
|
76
71
|
AssuranceProgression,
|
|
77
72
|
buildReviewPrompt,
|
|
78
73
|
classifyReviewWorkload,
|
|
79
74
|
deriveQaJobTimeoutMs,
|
|
80
|
-
|
|
75
|
+
projectTerminalTrackerState,
|
|
81
76
|
parseAssuranceVerdict,
|
|
82
77
|
snapshotDigest,
|
|
83
78
|
QA_JOB_TIMEOUT_SECONDS,
|
|
@@ -85,12 +80,9 @@ import {
|
|
|
85
80
|
REVIEW_PREPARATION_TIMEOUT_MS,
|
|
86
81
|
REVIEW_TIMING_PROFILES,
|
|
87
82
|
REVIEW_VERDICT_VALIDATION_TIMEOUT_MS,
|
|
88
|
-
type AssuranceAdvanceResult,
|
|
89
83
|
type AssuranceProgressionPorts,
|
|
90
|
-
type AssuranceSubmitReviewResult,
|
|
91
84
|
type AssuranceVerdict,
|
|
92
85
|
type HostContext,
|
|
93
|
-
type QaVerificationProgress,
|
|
94
86
|
type SnapshotDescriptor,
|
|
95
87
|
} from "./pi-canary-assurance-progression";
|
|
96
88
|
|
|
@@ -109,27 +101,25 @@ import {
|
|
|
109
101
|
reconcileKernelAuthority,
|
|
110
102
|
repairKernelAuthority,
|
|
111
103
|
readTaskRecord,
|
|
112
|
-
|
|
104
|
+
withKernelStoreLockForTask,
|
|
113
105
|
inspectStorageLayout,
|
|
114
106
|
migrateLegacyLayout,
|
|
115
107
|
readTaskIntent,
|
|
116
108
|
parseTaskIntentV1,
|
|
117
109
|
canonicalIntentHash,
|
|
118
110
|
projectAssurance,
|
|
119
|
-
deriveAssuranceAuthorization,
|
|
120
111
|
findingsDigestV2,
|
|
121
112
|
capabilityActionFor,
|
|
122
113
|
digestOfAction,
|
|
123
|
-
type AssuranceAuthorizationReadiness,
|
|
124
114
|
type AssuranceProjectionResult,
|
|
125
115
|
type TaskRecordRead,
|
|
126
116
|
type CanaryApplication,
|
|
127
117
|
type MutationAuthorityRegistry,
|
|
128
118
|
type CapabilityBindingV2,
|
|
119
|
+
LITERAL_USER_ACTOR_ID,
|
|
129
120
|
} from "./runtime-stub";
|
|
130
121
|
import { invocationRegistry } from "./pi-canary-assurance-progression";
|
|
131
122
|
|
|
132
|
-
const TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
133
123
|
const LOOP_OWNERS = ["plan", "kernel", "brainstorm", "planner", "loop"] as const;
|
|
134
124
|
const LOOP_TARGETS = [
|
|
135
125
|
"step",
|
|
@@ -186,6 +176,7 @@ export interface SnapshotDescriptorInput {
|
|
|
186
176
|
root: string;
|
|
187
177
|
task_id: string;
|
|
188
178
|
role: AssuranceRole;
|
|
179
|
+
run_id?: string | null;
|
|
189
180
|
record_revision: string;
|
|
190
181
|
workspace_revision: string;
|
|
191
182
|
intent_revision: number;
|
|
@@ -214,6 +205,7 @@ export function buildSnapshot(input: SnapshotDescriptorInput): SnapshotDescripto
|
|
|
214
205
|
return {
|
|
215
206
|
contract: "assurance_kernel/assurance_snapshot/v2",
|
|
216
207
|
task_id: input.task_id,
|
|
208
|
+
run_id: input.run_id ?? null,
|
|
217
209
|
role: input.role,
|
|
218
210
|
record_revision: input.record_revision,
|
|
219
211
|
workspace_revision: input.workspace_revision,
|
|
@@ -282,12 +274,11 @@ export function createPiAssuranceProgressionPorts(
|
|
|
282
274
|
projectTask: (root, taskId) => projectAssuranceState(root, taskId),
|
|
283
275
|
readTaskRecord: (root, taskId) => readTaskRecord(root, taskId),
|
|
284
276
|
readTaskIntent: (root, taskId) => readTaskIntent(root, taskId),
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
(dependencies.buildAssurance ?? buildAssuranceSnapshot)(root, taskId, role, projection, runner),
|
|
277
|
+
buildAssurance: (root, taskId, role, projection) =>
|
|
278
|
+
(dependencies.buildAssurance ?? buildAssuranceSnapshot)(root, taskId, role, projection),
|
|
288
279
|
ensureReviewRevision: (root, taskId, projection) => ensureTaskReviewRevision(root, taskId, projection),
|
|
289
|
-
runQa: (snapshot, descriptors,
|
|
290
|
-
(dependencies.runQa ?? runDeterministicQa)(snapshot, descriptors,
|
|
280
|
+
runQa: (snapshot, descriptors, options) =>
|
|
281
|
+
(dependencies.runQa ?? runDeterministicQa)(snapshot, descriptors, options),
|
|
291
282
|
writeReviewEvidence: (input) =>
|
|
292
283
|
(dependencies.writeReviewEvidence ?? writeNativeReviewEvidence)(input),
|
|
293
284
|
applyVerdict: (ctx, input) =>
|
|
@@ -491,7 +482,9 @@ export default function (
|
|
|
491
482
|
if (ctx.mode !== "tui")
|
|
492
483
|
return failCanaryTool(taskId, action.op, "blocked", "tui_required", "Kernel mutation is TUI-only", "invoke the TUI Tool");
|
|
493
484
|
try {
|
|
494
|
-
|
|
485
|
+
// Recovery also retires a leftover derived claim/owner file whose
|
|
486
|
+
// task the store already holds: that file is inert, not authority.
|
|
487
|
+
await withKernelStoreLockForTask(ctx.cwd, taskId, () => undefined);
|
|
495
488
|
} catch (error) {
|
|
496
489
|
return failCanaryTool(taskId, action.op, "blocked", "layout_recovery_failed", `Kernel transaction recovery failed: ${error instanceof Error ? error.message : String(error)}`, "resolve the pending marker and retry");
|
|
497
490
|
}
|
|
@@ -510,6 +503,25 @@ export default function (
|
|
|
510
503
|
if (action.op === "repair_authority_state") {
|
|
511
504
|
if (ctx.mode !== "tui") return failCanaryTool(taskId, action.op, "blocked", "tui_required", "imm_kernel_canary mutation is TUI-only", "invoke the TUI Tool");
|
|
512
505
|
const authority = await reconcileKernelAuthority(ctx.cwd, taskId);
|
|
506
|
+
if (authority.state === "terminal_owner" || authority.state === "unowned") {
|
|
507
|
+
// Repair is unrepresentable-by-construction removal: a leftover
|
|
508
|
+
// retired claim file is inert, and the store already holds the
|
|
509
|
+
// settled answer. Report the authority and write nothing.
|
|
510
|
+
try {
|
|
511
|
+
const repaired = await repairKernelAuthority(ctx.cwd, taskId, authority.revision);
|
|
512
|
+
const result = {
|
|
513
|
+
state: "recovered_retry",
|
|
514
|
+
operation: action.op,
|
|
515
|
+
authority: repaired,
|
|
516
|
+
result: `No stale authority claim exists for ${taskId}; the store owns authority`,
|
|
517
|
+
next_action: "retry the blocked managed request once",
|
|
518
|
+
};
|
|
519
|
+
return toolResult(JSON.stringify(result, null, 2), result);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
522
|
+
return failCanaryTool(taskId, action.op, "blocked", "authority_repair_failed", message, "inspect the kernel store authority state and retry");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
513
525
|
if (
|
|
514
526
|
authority.state !== "repairable_stale_claim" ||
|
|
515
527
|
authority.owner_task_id !== taskId
|
|
@@ -762,38 +774,11 @@ export default function (
|
|
|
762
774
|
const priorIntent = await readTaskIntent(ctx.cwd, taskId);
|
|
763
775
|
const sidecar = nextIntent ? join(ctx.cwd, priorIntent.intent_ref.path) : undefined;
|
|
764
776
|
const priorBytes = sidecar ? readFileSync(sidecar) : undefined;
|
|
765
|
-
const
|
|
766
|
-
? execFileSync("git", ["ls-files", "--stage", "-z", "--", priorIntent.intent_ref.path], {
|
|
767
|
-
cwd: ctx.cwd,
|
|
768
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
769
|
-
})
|
|
770
|
-
: undefined;
|
|
777
|
+
const stagedSnapshot = sidecar ? captureStagedIntent(ctx.cwd, priorIntent.intent_ref.path) : undefined;
|
|
771
778
|
let stagedNextDiffHash: string | undefined;
|
|
772
779
|
const restoreStagedIntent = (): void => {
|
|
773
|
-
if (!
|
|
774
|
-
|
|
775
|
-
execFileSync("git", ["update-index", "--force-remove", "--", priorIntent.intent_ref.path], {
|
|
776
|
-
cwd: ctx.cwd,
|
|
777
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
778
|
-
});
|
|
779
|
-
if (priorIndexState.length > 0) {
|
|
780
|
-
execFileSync("git", ["update-index", "-z", "--index-info"], {
|
|
781
|
-
cwd: ctx.cwd,
|
|
782
|
-
input: priorIndexState,
|
|
783
|
-
stdio: ["pipe", "ignore", "pipe"],
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
const restoredBytes = readFileSync(sidecar);
|
|
787
|
-
if (!restoredBytes.equals(priorBytes)) {
|
|
788
|
-
throw new Error("failed to restore prior intent bytes");
|
|
789
|
-
}
|
|
790
|
-
const restoredIndex = execFileSync("git", ["ls-files", "--stage", "-z", "--", priorIntent.intent_ref.path], {
|
|
791
|
-
cwd: ctx.cwd,
|
|
792
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
793
|
-
});
|
|
794
|
-
if (!restoredIndex.equals(priorIndexState)) {
|
|
795
|
-
throw new Error("failed to restore prior intent git index entry");
|
|
796
|
-
}
|
|
780
|
+
if (!stagedSnapshot) return;
|
|
781
|
+
restoreStagedIntentShared(ctx.cwd, stagedSnapshot);
|
|
797
782
|
};
|
|
798
783
|
if (nextIntent) {
|
|
799
784
|
try {
|
|
@@ -992,12 +977,13 @@ export default function (
|
|
|
992
977
|
const capability = await mintCapability(registry, {
|
|
993
978
|
authority_kind: "user",
|
|
994
979
|
task_id: taskId,
|
|
980
|
+
run_id: projection.projection.run_id,
|
|
995
981
|
action_kind: exactOperation.op,
|
|
996
982
|
expected_record_hash: projection.projection.record_revision,
|
|
997
983
|
intent_revision: nextIntent?.revision ?? projection.projection.intent_revision,
|
|
998
984
|
intent_content_hash: nextIntentHash ?? projection.projection.intent_content_hash,
|
|
999
985
|
diff_hash: operationDiffHash,
|
|
1000
|
-
actor_id:
|
|
986
|
+
actor_id: LITERAL_USER_ACTOR_ID,
|
|
1001
987
|
...(exactOperation.op === "approve_breaking_intent_revision"
|
|
1002
988
|
? { next_intent: exactOperation.next_intent, next_intent_ref: exactOperation.next_intent_ref }
|
|
1003
989
|
: {}),
|
|
@@ -1010,7 +996,7 @@ export default function (
|
|
|
1010
996
|
const result = (await app.execute({
|
|
1011
997
|
root: ctx.cwd,
|
|
1012
998
|
task_id: taskId,
|
|
1013
|
-
operation: { ...exactOperation, capability, actor_id:
|
|
999
|
+
operation: { ...exactOperation, capability, actor_id: LITERAL_USER_ACTOR_ID } as never,
|
|
1014
1000
|
prior_intent_token: priorIntent.token,
|
|
1015
1001
|
diffProvider: (root: string, record: NonNullable<TaskRecordRead["record"]>) => diffSnapshotOf(root, record),
|
|
1016
1002
|
now,
|
|
@@ -1023,7 +1009,7 @@ export default function (
|
|
|
1023
1009
|
) stagePlanningArtifactTransition(ctx.cwd, result.record);
|
|
1024
1010
|
return { state: "applied", operation, lifecycle: result.record.lifecycle };
|
|
1025
1011
|
} catch (error) {
|
|
1026
|
-
if (
|
|
1012
|
+
if (stagedSnapshot) {
|
|
1027
1013
|
const current = await readTaskRecord(ctx.cwd, taskId);
|
|
1028
1014
|
if (current.record?.intent_snapshot.revision === priorIntent.intent.revision) restoreStagedIntent();
|
|
1029
1015
|
}
|
|
@@ -1069,19 +1055,14 @@ export default function (
|
|
|
1069
1055
|
// Helpers (module scope; no workflow state)
|
|
1070
1056
|
// ---------------------------------------------------------------------------
|
|
1071
1057
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1058
|
+
// The derivation is shared with the Claude adapter so it cannot drift between
|
|
1059
|
+
// Hosts; this module re-exports it for its existing callers and tests.
|
|
1060
|
+
import {
|
|
1061
|
+
deriveAuthorizationOperation,
|
|
1062
|
+
type DerivedAuthorizationOperation,
|
|
1063
|
+
} from "../runtime/authorization_operation";
|
|
1075
1064
|
|
|
1076
|
-
|
|
1077
|
-
export function deriveAuthorizationOperation(input: {
|
|
1078
|
-
readiness: AssuranceAuthorizationReadiness;
|
|
1079
|
-
}): { operation: DerivedAuthorizationOperation } | { blocked: string } {
|
|
1080
|
-
if (input.readiness.state === "resolve_user_decision") return { operation: "resolve-user-decision" };
|
|
1081
|
-
if (input.readiness.state === "authorize_rework") return { operation: "authorize-rework" };
|
|
1082
|
-
if (input.readiness.blocked) return { blocked: input.readiness.blocked };
|
|
1083
|
-
return { blocked: "no unique host-derived authorization operation" };
|
|
1084
|
-
}
|
|
1065
|
+
export { deriveAuthorizationOperation, type DerivedAuthorizationOperation };
|
|
1085
1066
|
|
|
1086
1067
|
/**
|
|
1087
1068
|
* Ordinary Kernel operation mapping, exported so the conformance suite can
|
|
@@ -1142,7 +1123,7 @@ export async function recordCancelledUserDecision(
|
|
|
1142
1123
|
acceptance_id: null,
|
|
1143
1124
|
summary: `${operation} confirmation cancelled by literal user; snapshot ${snapshotDigestRef}`,
|
|
1144
1125
|
},
|
|
1145
|
-
actor_id:
|
|
1126
|
+
actor_id: LITERAL_USER_ACTOR_ID,
|
|
1146
1127
|
} as never,
|
|
1147
1128
|
prior_intent_token: (await readTaskIntent(ctx.cwd, taskId)).token,
|
|
1148
1129
|
diffProvider: (root: string, record: NonNullable<TaskRecordRead["record"]>) => diffSnapshotOf(root, record),
|
|
@@ -1178,9 +1159,9 @@ function diffSnapshotOf(root: string, record: NonNullable<TaskRecordRead["record
|
|
|
1178
1159
|
if (record.contract === "assurance_kernel/task_record/v4") {
|
|
1179
1160
|
if (!record.git_base_head)
|
|
1180
1161
|
throw new Error("TaskRecord v4 is missing git_base_head");
|
|
1181
|
-
return taskRevisionIdentity(root, record.intent_snapshot.scope_hint, record.git_base_head);
|
|
1162
|
+
return taskRevisionIdentity(root, record.intent_snapshot.scope_hint, record.git_base_head, record.task_id);
|
|
1182
1163
|
}
|
|
1183
|
-
return taskDiffIdentity(root, record.intent_snapshot.scope_hint);
|
|
1164
|
+
return taskDiffIdentity(root, record.intent_snapshot.scope_hint, record.task_id);
|
|
1184
1165
|
}
|
|
1185
1166
|
|
|
1186
1167
|
function diffHashOf(root: string, record: NonNullable<TaskRecordRead["record"]>): string {
|
|
@@ -1402,6 +1383,7 @@ async function applyAssuranceVerdict(
|
|
|
1402
1383
|
const capability = await mintCapability(registry, {
|
|
1403
1384
|
authority_kind: authorityKind,
|
|
1404
1385
|
task_id: snapshot.task_id,
|
|
1386
|
+
run_id: snapshot.run_id,
|
|
1405
1387
|
action_kind: "request_rework",
|
|
1406
1388
|
expected_record_hash: snapshot.record_revision,
|
|
1407
1389
|
intent_revision: snapshot.intent_revision,
|
|
@@ -1438,6 +1420,7 @@ async function applyAssuranceVerdict(
|
|
|
1438
1420
|
return;
|
|
1439
1421
|
}
|
|
1440
1422
|
const now = new Date().toISOString();
|
|
1423
|
+
const advisories = verdict.decision === "pass" ? reviewAdvisoryRecords(verdict) : [];
|
|
1441
1424
|
const approval = {
|
|
1442
1425
|
id: `approval-${snapshot.role}-${randomUUID().slice(0, 8)}`,
|
|
1443
1426
|
kind: snapshot.role === "qa" ? "qa" : "review",
|
|
@@ -1452,10 +1435,14 @@ async function applyAssuranceVerdict(
|
|
|
1452
1435
|
...(snapshot.role === "review" && snapshot.review_revision
|
|
1453
1436
|
? { review_revision: snapshot.review_revision }
|
|
1454
1437
|
: {}),
|
|
1438
|
+
...(snapshot.role === "review" && advisories.length > 0
|
|
1439
|
+
? { advisory_findings: advisories }
|
|
1440
|
+
: {}),
|
|
1455
1441
|
};
|
|
1456
1442
|
const capability = await mintCapability(registry, {
|
|
1457
1443
|
authority_kind: snapshot.role,
|
|
1458
1444
|
task_id: snapshot.task_id,
|
|
1445
|
+
run_id: snapshot.run_id,
|
|
1459
1446
|
action_kind: "record_approval",
|
|
1460
1447
|
expected_record_hash: snapshot.record_revision,
|
|
1461
1448
|
intent_revision: snapshot.intent_revision,
|
|
@@ -1481,7 +1468,6 @@ async function buildAssuranceSnapshot(
|
|
|
1481
1468
|
taskId: string,
|
|
1482
1469
|
role: AssuranceRole,
|
|
1483
1470
|
projection: AssuranceProjectionResult,
|
|
1484
|
-
runner: FrozenRunner,
|
|
1485
1471
|
): Promise<{
|
|
1486
1472
|
snapshot: SnapshotDescriptor;
|
|
1487
1473
|
descriptors: Map<string, VerificationDescriptor>;
|
|
@@ -1500,12 +1486,9 @@ async function buildAssuranceSnapshot(
|
|
|
1500
1486
|
const intent = record.record.intent_snapshot;
|
|
1501
1487
|
const acceptance = intent.acceptance;
|
|
1502
1488
|
const descriptors = new Map<string, VerificationDescriptor>();
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
// a free-form or version-mismatched string is ineligible.
|
|
1506
|
-
for (const item of acceptance) {
|
|
1489
|
+
// Review resumes from settled outcomes without resolving verification tools.
|
|
1490
|
+
for (const item of role === "qa" ? acceptance : []) {
|
|
1507
1491
|
const descriptor = parseVerificationDescriptor(item.verification);
|
|
1508
|
-
assertRunnerCompatible(descriptor, runner);
|
|
1509
1492
|
descriptors.set(item.id, descriptor);
|
|
1510
1493
|
}
|
|
1511
1494
|
// `git_base_head` is optional on the read shape because v3 records carry
|
|
@@ -1561,6 +1544,7 @@ async function buildAssuranceSnapshot(
|
|
|
1561
1544
|
root,
|
|
1562
1545
|
task_id: taskId,
|
|
1563
1546
|
role,
|
|
1547
|
+
run_id: projection.projection.run_id,
|
|
1564
1548
|
record_revision: projection.projection.record_revision,
|
|
1565
1549
|
workspace_revision: projection.projection.workspace_revision,
|
|
1566
1550
|
intent_revision: projection.projection.intent_revision,
|
|
@@ -1602,12 +1586,6 @@ function qaOutcomes(
|
|
|
1602
1586
|
);
|
|
1603
1587
|
}
|
|
1604
1588
|
|
|
1605
|
-
let frozenRunnerValue: FrozenRunner | undefined;
|
|
1606
|
-
async function frozenRunner(): Promise<FrozenRunner> {
|
|
1607
|
-
frozenRunnerValue ??= resolveBunRunner();
|
|
1608
|
-
return frozenRunnerValue;
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
1589
|
// The invocation registry is shared with the progression module's
|
|
1612
1590
|
// module-scoped registry (see the top-level import above); commit/cancel
|
|
1613
1591
|
// semantics are identical to the previous extension implementation.
|
|
@@ -1617,6 +1595,7 @@ async function mintCapability(
|
|
|
1617
1595
|
input: {
|
|
1618
1596
|
authority_kind: "review" | "qa" | "user";
|
|
1619
1597
|
task_id: string;
|
|
1598
|
+
run_id: string | null;
|
|
1620
1599
|
action_kind: string;
|
|
1621
1600
|
expected_record_hash: string;
|
|
1622
1601
|
intent_revision: number;
|
|
@@ -1654,6 +1633,7 @@ async function mintCapability(
|
|
|
1654
1633
|
const binding: CapabilityBindingV2 = {
|
|
1655
1634
|
authority_kind: input.authority_kind,
|
|
1656
1635
|
task_id: input.task_id,
|
|
1636
|
+
...(input.run_id ? { run_id: input.run_id } : {}),
|
|
1657
1637
|
action_digest: digest,
|
|
1658
1638
|
expected_record_hash: input.expected_record_hash,
|
|
1659
1639
|
intent_revision: input.intent_revision,
|
|
@@ -1698,13 +1678,8 @@ async function executeOrdinaryOperation(
|
|
|
1698
1678
|
// A content-changing revision writes the sidecar before the kernel's drift
|
|
1699
1679
|
// check runs; an unstaged write is itself scoped drift and deadlocks the
|
|
1700
1680
|
// revision. Mirror the breaking-revision path: stage the written sidecar
|
|
1701
|
-
// (worktree == index) and restore the exact prior
|
|
1702
|
-
const
|
|
1703
|
-
? execFileSync("git", ["ls-files", "--stage", "-z", "--", priorIntent.intent_ref.path], {
|
|
1704
|
-
cwd: ctx.cwd,
|
|
1705
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1706
|
-
})
|
|
1707
|
-
: null;
|
|
1681
|
+
// (worktree == index) and restore the exact prior state on failure.
|
|
1682
|
+
const priorStaged = priorBytes !== null ? captureStagedIntent(ctx.cwd, priorIntent.intent_ref.path) : null;
|
|
1708
1683
|
try {
|
|
1709
1684
|
if (priorBytes) {
|
|
1710
1685
|
writeFileSync(sidecar, `${JSON.stringify(operation.next_intent, null, 2)}\n`);
|
|
@@ -1730,19 +1705,8 @@ async function executeOrdinaryOperation(
|
|
|
1730
1705
|
} catch (error) {
|
|
1731
1706
|
if (priorBytes) {
|
|
1732
1707
|
const current = await readTaskRecord(ctx.cwd, input.taskId);
|
|
1733
|
-
if (current.record?.intent_snapshot.revision === priorIntent.intent.revision) {
|
|
1734
|
-
|
|
1735
|
-
execFileSync("git", ["update-index", "--force-remove", "--", priorIntent.intent_ref.path], {
|
|
1736
|
-
cwd: ctx.cwd,
|
|
1737
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1738
|
-
});
|
|
1739
|
-
if (priorIndexState && priorIndexState.length > 0) {
|
|
1740
|
-
execFileSync("git", ["update-index", "-z", "--index-info"], {
|
|
1741
|
-
cwd: ctx.cwd,
|
|
1742
|
-
input: priorIndexState,
|
|
1743
|
-
stdio: ["pipe", "ignore", "pipe"],
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1708
|
+
if (current.record?.intent_snapshot.revision === priorIntent.intent.revision && priorStaged) {
|
|
1709
|
+
restoreStagedIntentShared(ctx.cwd, priorStaged);
|
|
1746
1710
|
}
|
|
1747
1711
|
}
|
|
1748
1712
|
throw error;
|
|
@@ -1774,24 +1738,15 @@ async function enrichAssuranceResult(
|
|
|
1774
1738
|
: projection.projection;
|
|
1775
1739
|
let tracker: Awaited<ReturnType<typeof markGithubTaskTerminal>> | undefined;
|
|
1776
1740
|
await reconcileRefsQuietly(ctx.cwd);
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
tracker = {
|
|
1787
|
-
contract: "immune_brain/github_issue_tracker_result/v1",
|
|
1788
|
-
operation: "mark-terminal",
|
|
1789
|
-
status: "retryable_failure",
|
|
1790
|
-
association_found: false,
|
|
1791
|
-
message: "tracker observation failed after authoritative settlement",
|
|
1792
|
-
};
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1741
|
+
// One shared projection: the Claude Host runs the same step, so the opted-in
|
|
1742
|
+
// terminal tracker outcome does not depend on which Host settled the task.
|
|
1743
|
+
tracker = await projectTerminalTrackerState({
|
|
1744
|
+
root: ctx.cwd,
|
|
1745
|
+
task_id: taskId,
|
|
1746
|
+
projection,
|
|
1747
|
+
tombstone: await readTaskTombstone(ctx.cwd, taskId),
|
|
1748
|
+
markTerminal: (root, input) => markGithubTaskTerminal(root, input),
|
|
1749
|
+
});
|
|
1795
1750
|
return {
|
|
1796
1751
|
...result,
|
|
1797
1752
|
task_state: taskState,
|