immune-brain 4.6.0 → 5.1.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 +374 -46
- package/README.zh-CN.md +384 -47
- 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 +42 -44
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +143 -474
- package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +78 -87
- package/plugins/immune-brain/.pi-extension/pi-canary-assurance-progression.ts +7 -2
- package/plugins/immune-brain/.pi-extension/tsconfig.json +2 -2
- package/plugins/immune-brain/agents/immune-brain-advisory-reviewer.md +23 -0
- package/plugins/immune-brain/agents/immune-brain-qa.md +42 -0
- package/plugins/immune-brain/agents/immune-brain-reviewer.md +90 -1
- package/plugins/immune-brain/agents/immune-brain-ui-review.md +21 -0
- package/plugins/immune-brain/dist/BASELINE.md +4 -4
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +8601 -6975
- package/plugins/immune-brain/dist/docs/reference/immune-brain-config.md +27 -0
- package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +3 -3
- package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +30 -1
- package/plugins/immune-brain/dist/imm-brainstorm.md +1 -1
- package/plugins/immune-brain/dist/imm-doc-prune.md +2 -2
- package/plugins/immune-brain/dist/{imm-agent-doc-maintain.md → imm-doc-slim.md} +9 -9
- package/plugins/immune-brain/dist/imm-planner.md +23 -8
- package/plugins/immune-brain/dist/imm-pr-fix.md +1 -1
- package/plugins/immune-brain/dist/{imm-review-retro.md → imm-retro.md} +26 -13
- package/plugins/immune-brain/dist/{imm-loop.md → imm-run.md} +231 -10
- package/plugins/immune-brain/dist/registry.yaml +9 -9
- package/plugins/immune-brain/dist/role-prompts/code-review.md +7 -0
- package/plugins/immune-brain/dist/role-prompts/executor.md +35 -2
- package/plugins/immune-brain/dist/role-prompts/lane-steward.md +44 -0
- package/plugins/immune-brain/runtime/assurance/coordinator.ts +83 -67
- package/plugins/immune-brain/runtime/assurance/host_port.ts +20 -0
- package/plugins/immune-brain/runtime/assurance/qa.ts +2 -2
- package/plugins/immune-brain/runtime/assurance/verdict_authority.ts +347 -0
- package/plugins/immune-brain/runtime/claude/interaction.ts +6 -0
- package/plugins/immune-brain/runtime/claude/kernel_ports.ts +78 -373
- package/plugins/immune-brain/runtime/claude/mcp_server.ts +26 -2
- package/plugins/immune-brain/runtime/claude/review_host.ts +48 -16
- package/plugins/immune-brain/runtime/claude/role_agents.ts +84 -0
- package/plugins/immune-brain/runtime/commands/kernel.ts +3 -6
- package/plugins/immune-brain/runtime/kernel/application.ts +6 -3
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +15 -8
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +5 -3
- package/plugins/immune-brain/runtime/kernel/enrollment.ts +149 -5
- package/plugins/immune-brain/runtime/kernel/index.ts +3 -1
- package/plugins/immune-brain/runtime/kernel/storage.ts +170 -11
- package/plugins/immune-brain/runtime/kernel/storage_paths.ts +13 -0
- package/plugins/immune-brain/runtime/local_initiative.ts +94 -0
- package/plugins/immune-brain/runtime/loop_contract.ts +77 -24
- package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
- package/plugins/immune-brain/runtime/prompts/code-review.md +7 -0
- package/plugins/immune-brain/runtime/prompts/executor.md +35 -2
- package/plugins/immune-brain/runtime/prompts/lane-steward.md +44 -0
- package/plugins/immune-brain/runtime/role_prompt_bridge.ts +42 -2
- package/plugins/immune-brain/runtime/unattended/batch_git.ts +57 -27
- package/plugins/immune-brain/runtime/unattended/batch_integration.ts +387 -0
- package/plugins/immune-brain/runtime/unattended/batch_kernel_port.ts +106 -0
- package/plugins/immune-brain/runtime/unattended/batch_lanes.ts +909 -0
- package/plugins/immune-brain/runtime/unattended/batch_plan.ts +36 -11
- package/plugins/immune-brain/runtime/unattended/batch_preflight.ts +53 -36
- package/plugins/immune-brain/runtime/unattended/batch_reconfirmation.ts +34 -29
- package/plugins/immune-brain/runtime/unattended/batch_runner.ts +46 -91
- package/plugins/immune-brain/runtime/unattended/batch_schedule.ts +162 -0
- package/plugins/immune-brain/runtime/unattended/batch_state.ts +356 -8
- package/plugins/immune-brain/runtime/unattended/types.ts +21 -2
- package/plugins/immune-brain/runtime/workspace_scope.ts +34 -0
- package/plugins/immune-brain/skills/BASELINE.md +4 -4
- package/plugins/immune-brain/skills/imm-doc-prune/SKILL.md +1 -1
- package/plugins/immune-brain/skills/{imm-agent-doc-maintain → imm-doc-slim}/SKILL.md +9 -9
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +4 -2
- package/plugins/immune-brain/skills/imm-pr-fix/SKILL.md +1 -1
- package/plugins/immune-brain/skills/{imm-review-retro → imm-retro}/SKILL.md +7 -7
- package/plugins/immune-brain/skills/{imm-review-retro → imm-retro}/scripts/review_retro.ts +46 -21
- package/plugins/immune-brain/skills/{imm-loop → imm-run}/SKILL.md +11 -7
- package/plugins/immune-brain/skills/registry.yaml +9 -9
- package/plugins/immune-brain/.pi-extension/runtime-stub.ts +0 -581
|
@@ -15,8 +15,10 @@ import {
|
|
|
15
15
|
VerificationAbortedError,
|
|
16
16
|
} from "./verification";
|
|
17
17
|
import { createInvocationRegistry, type InvocationState, type InvocationToken } from "./invocations";
|
|
18
|
-
import type { ReviewBundle, ReviewManifestV5
|
|
19
|
-
import {
|
|
18
|
+
import type { ReviewBundle, ReviewManifestV5 } from "./review_evidence";
|
|
19
|
+
import { createVerdictAuthority, type VerdictAuthority } from "./verdict_authority";
|
|
20
|
+
import type { ConfirmationReferenceSource } from "./host_port";
|
|
21
|
+
import { buildRoleDelegationPacket, STATIC_REVIEW_RULES } from "../role_prompt_bridge";
|
|
20
22
|
import type { AssuranceProjectionResult } from "../kernel/assurance_projection";
|
|
21
23
|
import type { TaskIntentIdentityToken } from "../kernel/intent_token_registry";
|
|
22
24
|
import type { AssuranceHostPort, HostReviewReservation } from "./host_port";
|
|
@@ -346,7 +348,7 @@ export type AssuranceSubmitReviewResult = AssuranceRecoveryFields & (
|
|
|
346
348
|
| { state: "review_preparation_failed"; operation: "review"; operation_id: string; reason: string }
|
|
347
349
|
| { state: "completed" }
|
|
348
350
|
| { state: "settlement_unknown"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
349
|
-
| { state: "blocked"; reason: string; code?: "verdict_invalid" });
|
|
351
|
+
| { state: "blocked"; reason: string; code?: "verdict_invalid"; recovery_action?: string });
|
|
350
352
|
|
|
351
353
|
export type ActiveAssuranceState =
|
|
352
354
|
| { state: "running"; operation: "qa"; operation_id: string; deadline_seconds: number }
|
|
@@ -355,51 +357,17 @@ export type ActiveAssuranceState =
|
|
|
355
357
|
|
|
356
358
|
export interface AssuranceCoordinatorPorts {
|
|
357
359
|
host: AssuranceHostPort;
|
|
360
|
+
confirmationReference: ConfirmationReferenceSource;
|
|
361
|
+
onReworkApplied?: (ctx: HostContext, taskId: string, findingsCount: number) => void;
|
|
358
362
|
projectTask(root: string, taskId: string): Promise<AssuranceProjectionResult>;
|
|
359
363
|
readTaskRecord(root: string, taskId: string): Promise<TaskRecordRead>;
|
|
360
364
|
readTaskIntent(root: string, taskId: string): Promise<TaskIntentRead>;
|
|
361
|
-
buildAssurance(
|
|
362
|
-
root: string,
|
|
363
|
-
taskId: string,
|
|
364
|
-
role: AssuranceRole,
|
|
365
|
-
projection: AssuranceProjectionResult,
|
|
366
|
-
): Promise<{
|
|
367
|
-
snapshot: SnapshotDescriptor;
|
|
368
|
-
descriptors: Map<string, VerificationDescriptor>;
|
|
369
|
-
reviewBundle: ReviewBundle | null;
|
|
370
|
-
reviewManifest?: ReviewManifestV5 | null;
|
|
371
|
-
}>;
|
|
372
|
-
/**
|
|
373
|
-
* Publish and prove the deterministic task-scoped revision before QA runs, so
|
|
374
|
-
* a preparation failure can never consume or fake a settled QA attestation.
|
|
375
|
-
* Returns null for records still on the legacy v3 bundle path.
|
|
376
|
-
*/
|
|
377
|
-
ensureReviewRevision?(
|
|
378
|
-
root: string,
|
|
379
|
-
taskId: string,
|
|
380
|
-
projection: AssuranceProjectionResult,
|
|
381
|
-
): Promise<ReviewRevision | null>;
|
|
382
365
|
runQa(
|
|
383
366
|
snapshot: SnapshotDescriptor,
|
|
384
367
|
descriptors: Map<string, VerificationDescriptor>,
|
|
385
368
|
options: { signal?: AbortSignal; onProgress?: (progress: QaVerificationProgress) => void },
|
|
386
369
|
): Promise<AssuranceVerdict>;
|
|
387
370
|
writeReviewEvidence(input: { snapshot: SnapshotDescriptor; evidence: ReviewBundle | ReviewManifestV5 }): { path: string; remove(): void };
|
|
388
|
-
applyVerdict(
|
|
389
|
-
ctx: HostContext,
|
|
390
|
-
input: {
|
|
391
|
-
taskId: string;
|
|
392
|
-
snapshot: SnapshotDescriptor;
|
|
393
|
-
verdict: AssuranceVerdict;
|
|
394
|
-
invocation: InvocationToken;
|
|
395
|
-
actorId: string;
|
|
396
|
-
hooks?: {
|
|
397
|
-
beforeCommit?: () => Promise<void>;
|
|
398
|
-
onCommit?: () => void;
|
|
399
|
-
afterCommit?: () => Promise<void>;
|
|
400
|
-
};
|
|
401
|
-
},
|
|
402
|
-
): Promise<void>;
|
|
403
371
|
applyOrdinaryOperation(ctx: HostContext, input: { taskId: string; operation: { op: string; actor_id: string } }): Promise<unknown>;
|
|
404
372
|
advanceBeforeProjection?: () => Promise<void>;
|
|
405
373
|
qaBeforeProjection?: () => Promise<void>;
|
|
@@ -478,19 +446,19 @@ export function snapshotDigest(snapshot: SnapshotDescriptor): string {
|
|
|
478
446
|
return `sha256:${createHash("sha256").update(JSON.stringify(snapshot)).digest("hex")}`;
|
|
479
447
|
}
|
|
480
448
|
|
|
481
|
-
|
|
449
|
+
/**
|
|
450
|
+
* The per-dispatch Review facts: which evidence to read, which revision it pins,
|
|
451
|
+
* what the acceptances assert, and the exact verdict shapes.
|
|
452
|
+
*
|
|
453
|
+
* This is the whole prompt for a Host whose reviewer definition already carries
|
|
454
|
+
* the role contract and `STATIC_REVIEW_RULES` — the Claude Host. A Host whose
|
|
455
|
+
* dispatched agent carries no instructions (Pi) gets `buildReviewPrompt`, which
|
|
456
|
+
* prepends both.
|
|
457
|
+
*/
|
|
458
|
+
export function buildReviewSnapshotPrompt(snapshot: SnapshotDescriptor, evidencePath?: string): string {
|
|
482
459
|
if (snapshot.role !== "review") throw new Error("native review prompt requires review role");
|
|
483
460
|
const acceptance = snapshot.acceptance.map((item) => `- ${item.id}: ${item.assertion}`).join("\n");
|
|
484
461
|
const digest = snapshotDigest(snapshot);
|
|
485
|
-
const rolePacket = buildRoleDelegationPacket({
|
|
486
|
-
role: "code-review",
|
|
487
|
-
context: {
|
|
488
|
-
task_id: snapshot.task_id,
|
|
489
|
-
review_gate: "imm-code-review",
|
|
490
|
-
changed_files_signature: snapshot.diff_hash,
|
|
491
|
-
snapshot_digest: digest,
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
462
|
const revision = snapshot.review_revision;
|
|
495
463
|
const evidenceContract = revision
|
|
496
464
|
? [
|
|
@@ -506,25 +474,44 @@ export function buildReviewPrompt(snapshot: SnapshotDescriptor, evidencePath?: s
|
|
|
506
474
|
`The user-selected worktree may contain staged task changes that are absent from the isolated reviewer worktree. Review authority is bound only to the bundle dirty_files current_content bytes and committed HEAD provenance. Analyze code exclusively from those bundle bytes; repository file reads are permitted only for the provenance git commands above. A symbol present in current_content but absent from HEAD is the task change, not an absence.`,
|
|
507
475
|
];
|
|
508
476
|
return [
|
|
509
|
-
rolePacket.prompt,
|
|
510
477
|
...evidenceContract,
|
|
511
|
-
`Do not edit files, create files, run mutating commands, or change Git state. Focus on correctness, regressions, security, and missing tests.`,
|
|
512
|
-
`Execution outcomes for every acceptance were verified deterministically by the Kernel QA layer before this review and are embedded in this bundle under outcomes (the immutable evidence file, acceptance_id -> {status, summary}); do not re-execute descriptors and do not treat the absence of local test runs as a finding. Your review covers evidence provenance, code correctness, regressions, security, and missing tests against the embedded assertions and code.`,
|
|
513
478
|
`Snapshot digest: ${digest}`,
|
|
514
479
|
`TaskRecord revision: ${snapshot.record_revision}`,
|
|
515
480
|
revision
|
|
516
481
|
? `Intent revision ${snapshot.intent_revision} (hash ${snapshot.intent_content_hash}), diff ${snapshot.diff_hash}, review revision ${revision.review_commit} (base ${revision.base_head}, tree ${revision.review_tree}, manifest ${revision.manifest_digest}), state ${snapshot.lifecycle}:${snapshot.artifact_state}.`
|
|
517
482
|
: `Intent revision ${snapshot.intent_revision} (hash ${snapshot.intent_content_hash}), diff ${snapshot.diff_hash}, review bundle ${snapshot.review_bundle_digest}, state ${snapshot.lifecycle}:${snapshot.artifact_state}.`,
|
|
518
483
|
"Acceptance assertions:", acceptance,
|
|
519
|
-
|
|
520
|
-
`
|
|
521
|
-
`PASS shape: {"contract":"assurance_kernel/assurance_verdict/v2","role":"review","task_id":"${snapshot.task_id}","snapshot_digest":"${digest}","decision":"pass","approval":{"kind":"review","authority_role":"reviewer","summary":"<one line>"}}`,
|
|
522
|
-
`A pass verdict may carry non-blocking notes as findings, but every one of them must set kind "advisory"; a blocking finding is a rework verdict and must omit approval: {"contract":"assurance_kernel/assurance_verdict/v2","role":"review","task_id":"${snapshot.task_id}","snapshot_digest":"${digest}","decision":"pass","approval":{"kind":"review","authority_role":"reviewer","summary":"<one line>"},"findings":[{"id":"review-1","kind":"advisory","acceptance_id":"<id|null>","summary":"<one line>","evidence":{"trigger":"<concrete inputs or state>","caller_chain":["<path-or-symbol>"],"violated":{"kind":"acceptance|security_boundary","ref":"<acceptance id or boundary>"}}}]}`,
|
|
484
|
+
`PASS shape: {"contract":"assurance_kernel/assurance_verdict/v2","role":"review","task_id":"${snapshot.task_id}","snapshot_digest":"${digest}","decision":"pass","approval":{"kind":"review","authority_role":"reviewer","summary":"<one line>","inspected_paths":["<every changed path of the reviewed change set>"]}}`,
|
|
485
|
+
`A pass verdict may carry non-blocking notes as findings, but every one of them must set kind "advisory"; a blocking finding is a rework verdict and must omit approval: {"contract":"assurance_kernel/assurance_verdict/v2","role":"review","task_id":"${snapshot.task_id}","snapshot_digest":"${digest}","decision":"pass","approval":{"kind":"review","authority_role":"reviewer","summary":"<one line>","inspected_paths":["<every changed path of the reviewed change set>"]},"findings":[{"id":"review-1","kind":"advisory","acceptance_id":"<id|null>","summary":"<one line>","evidence":{"trigger":"<concrete inputs or state>","caller_chain":["<path-or-symbol>"],"violated":{"kind":"acceptance|security_boundary","ref":"<acceptance id or boundary>"}}}]}`,
|
|
523
486
|
`REWORK shape: {"contract":"assurance_kernel/assurance_verdict/v2","role":"review","task_id":"${snapshot.task_id}","snapshot_digest":"${digest}","decision":"rework","findings":[{"id":"review-1","kind":"blocking|advisory","acceptance_id":"<id|null>","summary":"<one line>","evidence":{"trigger":"<concrete inputs or state>","caller_chain":["<path-or-symbol>"],"violated":{"kind":"acceptance|security_boundary","ref":"<acceptance id or boundary>"}}}]}`,
|
|
524
487
|
`REWORK verdicts must omit the approval field entirely; do not emit "approval": null.`,
|
|
525
488
|
].join("\n");
|
|
526
489
|
}
|
|
527
490
|
|
|
491
|
+
/**
|
|
492
|
+
* The complete Review prompt: the internal role contract, the static review
|
|
493
|
+
* rules, and the per-dispatch facts. Dispatched by a Host whose agent carries no
|
|
494
|
+
* instructions of its own.
|
|
495
|
+
*/
|
|
496
|
+
export function buildReviewPrompt(snapshot: SnapshotDescriptor, evidencePath?: string): string {
|
|
497
|
+
if (snapshot.role !== "review") throw new Error("native review prompt requires review role");
|
|
498
|
+
const digest = snapshotDigest(snapshot);
|
|
499
|
+
const rolePacket = buildRoleDelegationPacket({
|
|
500
|
+
role: "code-review",
|
|
501
|
+
context: {
|
|
502
|
+
task_id: snapshot.task_id,
|
|
503
|
+
review_gate: "imm-code-review",
|
|
504
|
+
changed_files_signature: snapshot.diff_hash,
|
|
505
|
+
snapshot_digest: digest,
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
return [
|
|
509
|
+
rolePacket.prompt,
|
|
510
|
+
...STATIC_REVIEW_RULES,
|
|
511
|
+
buildReviewSnapshotPrompt(snapshot, evidencePath),
|
|
512
|
+
].join("\n");
|
|
513
|
+
}
|
|
514
|
+
|
|
528
515
|
function parseVerdictEvidence(value: unknown, index: number): FindingEvidence {
|
|
529
516
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
530
517
|
throw new Error(`finding ${index} evidence is required`);
|
|
@@ -593,7 +580,28 @@ export function parseAssuranceVerdict(input: unknown, snapshot: SnapshotDescript
|
|
|
593
580
|
const expectedKind = snapshot.role === "qa" ? "qa" : "review";
|
|
594
581
|
const expectedRole = snapshot.role === "qa" ? "qa" : "reviewer";
|
|
595
582
|
if (!approval || approval.kind !== expectedKind || approval.authority_role !== expectedRole || typeof approval.summary !== "string" || !approval.summary.trim()) throw new Error("pass verdict approval is invalid");
|
|
596
|
-
|
|
583
|
+
// A Review pass must claim the paths it inspected: BR-DEC-3 converts silent
|
|
584
|
+
// partial coverage into an explicit false statement. The list is checked
|
|
585
|
+
// here against the reviewed change set and removed before settlement, so
|
|
586
|
+
// the Kernel approval and TaskRecord never see it. QA verdicts treat the
|
|
587
|
+
// field as unknown, exactly as before.
|
|
588
|
+
if (snapshot.role === "review") {
|
|
589
|
+
const required = snapshot.dirty_files;
|
|
590
|
+
const listed = approval.inspected_paths;
|
|
591
|
+
if (listed === undefined) throw new Error(`review pass verdict approval.inspected_paths is required; it must list every reviewed changed path${required.length ? `: ${required.join(", ")}` : " (none for this change set)"}`);
|
|
592
|
+
if (!Array.isArray(listed) || listed.some((entry) => typeof entry !== "string" || !entry.trim())) throw new Error("review pass verdict approval.inspected_paths must be an array of repository-relative path strings");
|
|
593
|
+
const seen = new Set<string>();
|
|
594
|
+
for (const entry of listed as string[]) {
|
|
595
|
+
if (seen.has(entry)) throw new Error(`review pass verdict approval.inspected_paths lists a duplicate path: ${entry}`);
|
|
596
|
+
seen.add(entry);
|
|
597
|
+
}
|
|
598
|
+
const foreign = (listed as string[]).filter((entry) => !required.includes(entry));
|
|
599
|
+
if (foreign.length) throw new Error(`review pass verdict approval.inspected_paths lists a path outside the reviewed change set: ${foreign.join(", ")}`);
|
|
600
|
+
const missing = required.filter((entry) => !seen.has(entry));
|
|
601
|
+
if (missing.length) throw new Error(`review pass verdict approval.inspected_paths omits reviewed changed paths: ${missing.join(", ")}`);
|
|
602
|
+
}
|
|
603
|
+
const allowedApproval = snapshot.role === "review" ? ["kind", "authority_role", "summary", "inspected_paths"] : ["kind", "authority_role", "summary"];
|
|
604
|
+
const unknownApproval = Object.keys(approval).find((key) => !allowedApproval.includes(key));
|
|
597
605
|
if (unknownApproval) throw new Error(`pass verdict approval has unknown field: ${unknownApproval}`);
|
|
598
606
|
const passFindings = raw.findings === undefined ? [] : parseVerdictFindings(raw.findings as unknown[], snapshot);
|
|
599
607
|
if (passFindings.some((finding) => finding.kind !== "advisory")) throw new Error("pass verdict findings must all be advisory");
|
|
@@ -726,7 +734,14 @@ export class AssuranceCoordinator {
|
|
|
726
734
|
private sessionActive = true;
|
|
727
735
|
private sessionGeneration = 0;
|
|
728
736
|
|
|
729
|
-
|
|
737
|
+
readonly authority: VerdictAuthority;
|
|
738
|
+
constructor(private readonly ports: AssuranceCoordinatorPorts, authorityOverrides: Partial<VerdictAuthority> = {}) {
|
|
739
|
+
this.authority = { ...createVerdictAuthority({
|
|
740
|
+
confirmationReference: ports.confirmationReference,
|
|
741
|
+
commitInvocation: token => this.commitInvocation(token),
|
|
742
|
+
onReworkApplied: ports.onReworkApplied,
|
|
743
|
+
}), ...authorityOverrides };
|
|
744
|
+
}
|
|
730
745
|
|
|
731
746
|
onSessionStart(): void {
|
|
732
747
|
this.sessionActive = true;
|
|
@@ -973,9 +988,9 @@ export class AssuranceCoordinator {
|
|
|
973
988
|
progress("preparing_review_revision", "Proving the immutable Review revision before QA");
|
|
974
989
|
try {
|
|
975
990
|
if (parked.record?.contract === "assurance_kernel/task_record/v4") {
|
|
976
|
-
if (!this.
|
|
991
|
+
if (!this.authority.ensureReviewRevision)
|
|
977
992
|
throw new Error("v4 Review revision preparation is unavailable");
|
|
978
|
-
await this.
|
|
993
|
+
await this.authority.ensureReviewRevision(ctx.cwd, taskId, projection);
|
|
979
994
|
}
|
|
980
995
|
} catch (error) {
|
|
981
996
|
return { state: "blocked", reason: `review revision preparation failed: ${boundedAssuranceError(error)}` };
|
|
@@ -989,7 +1004,7 @@ export class AssuranceCoordinator {
|
|
|
989
1004
|
progress("capturing_snapshot", "Capturing the immutable QA snapshot");
|
|
990
1005
|
await this.ports.qaBeforeProjection?.();
|
|
991
1006
|
ensureOperationLive();
|
|
992
|
-
const assurance = await this.
|
|
1007
|
+
const assurance = await this.authority.buildAssurance(ctx.cwd, taskId, "qa", projection);
|
|
993
1008
|
ensureOperationLive();
|
|
994
1009
|
// Preparation counts against the same ceiling as the checks, so the
|
|
995
1010
|
// aggregate clock starts before the first prepared group runs. A host
|
|
@@ -1027,7 +1042,7 @@ export class AssuranceCoordinator {
|
|
|
1027
1042
|
try {
|
|
1028
1043
|
progress("settling_qa", "Settling deterministic QA through the Kernel revision boundary");
|
|
1029
1044
|
authorityBoundaryStarted = true;
|
|
1030
|
-
await this.
|
|
1045
|
+
await this.authority.applyVerdict(ctx, {
|
|
1031
1046
|
taskId,
|
|
1032
1047
|
snapshot: assurance.snapshot,
|
|
1033
1048
|
verdict: qaVerdict,
|
|
@@ -1090,10 +1105,10 @@ export class AssuranceCoordinator {
|
|
|
1090
1105
|
authorityBoundaryStarted = false;
|
|
1091
1106
|
if (aborted()) return this.reviewPreparationFailed(taskId, operationId, "host cancellation after QA authority settlement");
|
|
1092
1107
|
progress("preparing_review", "Preparing the reserved foreground Review evidence");
|
|
1093
|
-
let review: Awaited<ReturnType<
|
|
1108
|
+
let review: Awaited<ReturnType<VerdictAuthority["buildAssurance"]>>;
|
|
1094
1109
|
let evidence: { path: string; remove(): void };
|
|
1095
1110
|
try {
|
|
1096
|
-
review = await this.
|
|
1111
|
+
review = await this.authority.buildAssurance(ctx.cwd, taskId, "review", fresh);
|
|
1097
1112
|
const payload = review.reviewManifest ?? review.reviewBundle;
|
|
1098
1113
|
if (!payload) throw new Error("review evidence is missing after QA settlement");
|
|
1099
1114
|
evidence = this.ports.writeReviewEvidence({ snapshot: review.snapshot, evidence: payload });
|
|
@@ -1116,6 +1131,7 @@ export class AssuranceCoordinator {
|
|
|
1116
1131
|
taskId,
|
|
1117
1132
|
operationId,
|
|
1118
1133
|
prompt: buildReviewPrompt(review.snapshot, evidence.path),
|
|
1134
|
+
snapshotPrompt: buildReviewSnapshotPrompt(review.snapshot, evidence.path),
|
|
1119
1135
|
evidencePath: evidence.path,
|
|
1120
1136
|
maxTurns: reviewTurnBudget(workload),
|
|
1121
1137
|
});
|
|
@@ -1202,9 +1218,9 @@ export class AssuranceCoordinator {
|
|
|
1202
1218
|
}
|
|
1203
1219
|
if (reservation.snapshot.review_revision) {
|
|
1204
1220
|
try {
|
|
1205
|
-
if (!this.
|
|
1221
|
+
if (!this.authority.ensureReviewRevision)
|
|
1206
1222
|
throw new Error("v4 Review revision verification is unavailable");
|
|
1207
|
-
const revision = await this.
|
|
1223
|
+
const revision = await this.authority.ensureReviewRevision(ctx.cwd, taskId, fresh);
|
|
1208
1224
|
if (!revision || revision.base_head !== reservation.snapshot.review_revision.base_head || revision.review_commit !== reservation.snapshot.review_revision.review_commit || revision.review_tree !== reservation.snapshot.review_revision.review_tree || revision.manifest_digest !== reservation.snapshot.review_revision.manifest_digest)
|
|
1209
1225
|
throw new Error("Review revision changed before submission");
|
|
1210
1226
|
} catch (error) {
|
|
@@ -1220,7 +1236,7 @@ export class AssuranceCoordinator {
|
|
|
1220
1236
|
const invocation = this.openInvocation(taskId);
|
|
1221
1237
|
this.releaseReviewReservation(taskId, reservation);
|
|
1222
1238
|
try {
|
|
1223
|
-
await this.
|
|
1239
|
+
await this.authority.applyVerdict(ctx, {
|
|
1224
1240
|
taskId,
|
|
1225
1241
|
snapshot: reservation.snapshot,
|
|
1226
1242
|
verdict,
|
|
@@ -1,7 +1,27 @@
|
|
|
1
|
+
import type { SnapshotDescriptor } from "./coordinator";
|
|
2
|
+
|
|
3
|
+
export type ConfirmationReferenceSource = (input: {
|
|
4
|
+
snapshot: SnapshotDescriptor;
|
|
5
|
+
actorId: string;
|
|
6
|
+
now: string;
|
|
7
|
+
}) => string;
|
|
8
|
+
|
|
1
9
|
export interface ReviewRequest {
|
|
2
10
|
taskId: string;
|
|
3
11
|
operationId: string;
|
|
12
|
+
/**
|
|
13
|
+
* The complete Review prompt: internal role contract, static review rules and
|
|
14
|
+
* the per-dispatch facts. Dispatched by a Host whose reviewer agent carries no
|
|
15
|
+
* instructions of its own.
|
|
16
|
+
*/
|
|
4
17
|
prompt: string;
|
|
18
|
+
/**
|
|
19
|
+
* The per-dispatch facts alone, a strict suffix of `prompt`. A Host whose
|
|
20
|
+
* reviewer definition already carries the role contract and the static rules
|
|
21
|
+
* dispatches only this, so the static instructions stay generated from one
|
|
22
|
+
* source instead of being sent twice.
|
|
23
|
+
*/
|
|
24
|
+
snapshotPrompt: string;
|
|
5
25
|
evidencePath: string;
|
|
6
26
|
maxTurns: number;
|
|
7
27
|
}
|
|
@@ -8,14 +8,14 @@ import { tmpdir } from "node:os";
|
|
|
8
8
|
import { join, sep } from "node:path";
|
|
9
9
|
import { qaFindingId } from "./qa_findings";
|
|
10
10
|
import { captureGitTaskRevisionSnapshot } from "../workspace_scope";
|
|
11
|
-
import {
|
|
11
|
+
import { readTaskRecordRaw } from "../kernel/storage";
|
|
12
12
|
|
|
13
13
|
function deliveryTreeForSnapshot(
|
|
14
14
|
snapshot: SnapshotDescriptor,
|
|
15
15
|
writeTree: typeof writeDeliveryTree,
|
|
16
16
|
): string {
|
|
17
17
|
if (snapshot.review_revision?.review_tree) return snapshot.review_revision.review_tree;
|
|
18
|
-
const record =
|
|
18
|
+
const record = readTaskRecordRaw(snapshot.root, snapshot.task_id).record;
|
|
19
19
|
if (!record || record.contract !== "assurance_kernel/task_record/v4" || !record.git_base_head)
|
|
20
20
|
throw new Error("QA delivery requires a TaskRecord v4 git_base_head");
|
|
21
21
|
const captured = captureGitTaskRevisionSnapshot(
|