gentle-pi 1.2.0 → 2.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/README.md +40 -9
- package/assets/agents/review-readability.md +0 -1
- package/assets/agents/review-reliability.md +0 -1
- package/assets/agents/review-resilience.md +0 -1
- package/assets/agents/review-risk.md +0 -1
- package/contracts/review-integration/v1/fixtures/binding-revision-conflict.fixture.json +25 -0
- package/contracts/review-integration/v1/fixtures/capabilities-v1.2.fixture.json +263 -0
- package/contracts/review-integration/v1/fixtures/capabilities-v1.3.fixture.json +301 -0
- package/contracts/review-integration/v1/fixtures/capabilities-v1.4.fixture.json +312 -0
- package/contracts/review-integration/v1/fixtures/capabilities-v1.5.fixture.json +321 -0
- package/contracts/review-integration/v1/fixtures/consent.fixture.json +36 -0
- package/contracts/review-integration/v1/fixtures/final-verification-incident.fixture.json +1 -0
- package/contracts/review-integration/v1/fixtures/operation.fixture.json +4 -0
- package/contracts/review-integration/v1/fixtures/repair-preflight.fixture.json +54 -0
- package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +97 -0
- package/contracts/review-integration/v1/fixtures/start.fixture.json +25 -1
- package/contracts/review-integration/v1/fixtures/status-ambiguous.fixture.json +4 -0
- package/contracts/review-integration/v1/fixtures/status-corrupted.fixture.json +4 -0
- package/contracts/review-integration/v1/fixtures/status-recover.fixture.json +4 -0
- package/contracts/review-integration/v1/fixtures/status-unrelated.fixture.json +4 -0
- package/contracts/review-integration/v1/fixtures/status-v2-ambiguous.fixture.json +110 -0
- package/contracts/review-integration/v1/fixtures/status-v2-corrupted.fixture.json +107 -0
- package/contracts/review-integration/v1/fixtures/status-v2-final-verification-retry.fixture.json +192 -0
- package/contracts/review-integration/v1/fixtures/status-v2-recover.fixture.json +130 -0
- package/contracts/review-integration/v1/fixtures/status-v2-repair.fixture.json +161 -0
- package/contracts/review-integration/v1/fixtures/status-v2-unrelated.fixture.json +103 -0
- package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +193 -0
- package/contracts/review-integration/v1/fixtures/status.fixture.json +19 -5
- package/contracts/review-integration/v1/fixtures/verification-evidence.fixture.json +1 -0
- package/contracts/review-integration/v1/schemas/admitted-result.schema.json +35 -0
- package/contracts/review-integration/v1/schemas/artifact-subject.schema.json +33 -0
- package/contracts/review-integration/v1/schemas/authority-repair-assessment.schema.json +247 -0
- package/contracts/review-integration/v1/schemas/capabilities-v1.2.schema.json +249 -0
- package/contracts/review-integration/v1/schemas/capabilities-v1.3.schema.json +429 -0
- package/contracts/review-integration/v1/schemas/capabilities-v1.4.schema.json +432 -0
- package/contracts/review-integration/v1/schemas/capabilities-v1.5.schema.json +154 -0
- package/contracts/review-integration/v1/schemas/consent.schema.json +86 -0
- package/contracts/review-integration/v1/schemas/failure.schema.json +54 -8
- package/contracts/review-integration/v1/schemas/final-verification-incident.schema.json +51 -0
- package/contracts/review-integration/v1/schemas/operation.schema.json +204 -31
- package/contracts/review-integration/v1/schemas/repair.schema.json +251 -0
- package/contracts/review-integration/v1/schemas/result-artifact-v2.schema.json +38 -0
- package/contracts/review-integration/v1/schemas/start-v2.schema.json +216 -0
- package/contracts/review-integration/v1/schemas/start.schema.json +79 -3
- package/contracts/review-integration/v1/schemas/status-v2.schema.json +313 -0
- package/contracts/review-integration/v1/schemas/status.schema.json +37 -10
- package/contracts/review-integration/v1/schemas/targeted-validation-request.schema.json +46 -0
- package/contracts/review-integration/v1/schemas/verification-evidence.schema.json +64 -0
- package/contracts/review-integration/v2/fixtures/capabilities.fixture.json +111 -0
- package/contracts/review-integration/v2/fixtures/consent.fixture.json +36 -0
- package/contracts/review-integration/v2/fixtures/start.fixture.json +93 -0
- package/contracts/review-integration/v2/fixtures/status.fixture.json +113 -0
- package/contracts/review-integration/v2/schemas/admitted-result.schema.json +14 -0
- package/contracts/review-integration/v2/schemas/artifact-subject.schema.json +36 -0
- package/contracts/review-integration/v2/schemas/capabilities.schema.json +111 -0
- package/contracts/review-integration/v2/schemas/consent.schema.json +70 -0
- package/contracts/review-integration/v2/schemas/failure.schema.json +35 -0
- package/contracts/review-integration/v2/schemas/operation.schema.json +46 -0
- package/contracts/review-integration/v2/schemas/repair.schema.json +22 -0
- package/contracts/review-integration/v2/schemas/start.schema.json +106 -0
- package/contracts/review-integration/v2/schemas/status.schema.json +104 -0
- package/docs/native-authority-architecture.md +18 -3
- package/docs/review-integration.md +175 -27
- package/extensions/gentle-ai.ts +414 -19
- package/lib/gentle-ai-binary.ts +1 -1
- package/lib/native-review-cli.ts +679 -57
- package/lib/review-candidate-view.ts +131 -1
- package/lib/review-compact-contract.ts +12 -3
- package/lib/review-consent-latch.ts +51 -0
- package/lib/review-correction-lifecycle.ts +170 -0
- package/lib/review-integration-v2.ts +1563 -0
- package/package.json +15 -11
- package/runtime/gentle-ai-binary.mjs +1 -1
- package/runtime/native-review-cli.mjs +663 -41
- package/runtime/review-integration-v2.mjs +1564 -0
- package/scripts/build-git-commit-transaction-runner.mjs +1 -1
- package/scripts/gentle-ai-installer.mjs +16 -11
- package/scripts/install-gentle-ai.mjs +7 -3
- package/scripts/test-packed-runner.mjs +2 -2
- package/scripts/verify-package-files.mjs +216 -64
- package/skills/release/SKILL.md +38 -11
- package/tests/devbinary/native-review-parity.devtest.ts +348 -0
- package/tests/gentle-ai-binary.test.ts +8 -1
- package/tests/gentle-ai-installer.test.ts +24 -21
- package/tests/native-binary-gate.test.ts +145 -0
- package/tests/native-review-capability-contract.test.ts +87 -0
- package/tests/native-review-cli.test.ts +44 -1
- package/tests/native-review-parity-runtime.test.ts +70 -23
- package/tests/native-review-parity.test.ts +673 -0
- package/tests/native-risk-evidence-parity.test.ts +91 -0
- package/tests/package-manifest.test.ts +72 -7
- package/tests/review-actor-tool-deny.test.ts +20 -5
- package/tests/review-candidate-view.test.ts +331 -1
- package/tests/review-compact-contract.test.ts +5 -0
- package/tests/review-consent-latch.test.ts +67 -0
- package/tests/review-controller-native-recovery.test.ts +180 -5
- package/tests/review-controller-native-routing.test.ts +385 -12
- package/tests/review-controller-workspace-root.test.ts +26 -6
- package/tests/review-correction-lifecycle.test.ts +174 -0
- package/tests/review-integration-v2.test.ts +539 -0
- package/tests/runtime-harness.mjs +187 -0
- package/tests/support/native-binary-gate.ts +86 -0
- package/tests/verify-package-files.test.ts +119 -0
- package/lib/review-integration-v1.ts +0 -795
- package/runtime/review-integration-v1.mjs +0 -796
- package/tests/native-review-integration-v1.test.ts +0 -102
- package/tests/review-integration-v1.test.ts +0 -530
|
@@ -19,11 +19,11 @@ class NativeReviewCliV214 extends NativeReviewCliV214Production {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
import { canonicalJsonV1, domainHashV1 } from "../lib/review-canonical.ts";
|
|
22
|
-
import { CandidateViewRegistry } from "../lib/review-candidate-view.ts";
|
|
22
|
+
import { CandidateViewRegistry, deriveChangedPathManifest } from "../lib/review-candidate-view.ts";
|
|
23
23
|
import { inspectLegacyReviewAuthorityV1 } from "../lib/review-legacy-detector.ts";
|
|
24
24
|
import { resolveRepositoryAuthorityV1 } from "../lib/review-repository.ts";
|
|
25
25
|
import { NATIVE_REVIEW_REMEDIATION, classifyNativeReviewRemediation } from "../lib/native-review-remediation.ts";
|
|
26
|
-
import type {
|
|
26
|
+
import type { AuthorityRepairAssessmentV1, ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
27
27
|
|
|
28
28
|
interface RegisteredTool {
|
|
29
29
|
execute: (
|
|
@@ -268,18 +268,26 @@ function fakeNative(overrides: Partial<NativeReviewCli> = {}): NativeReviewCli {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
const UNSUPPORTED_REPAIR_ASSESSMENT: AuthorityRepairAssessmentV1 = {
|
|
272
|
+
schema: "gentle-ai.review-authority-repair-assessment/v1",
|
|
273
|
+
status: "unsupported",
|
|
274
|
+
counts: { lineages: 0, compactLineages: 0, legacyLineages: 0, events: 0, bytes: 0, eligibleCandidates: 0, unsupportedLineages: 0, conflicts: 0 },
|
|
275
|
+
supportedOperations: ["review/complete-fix", "review/validate-fix"],
|
|
276
|
+
authorizationSchema: "gentle-ai.review-repair-authorization/v1",
|
|
277
|
+
};
|
|
278
|
+
|
|
271
279
|
function targetStatusFixture(options: {
|
|
272
280
|
applicability?: "current_target" | "unrelated" | "ambiguous" | "corrupted";
|
|
273
|
-
action?:
|
|
274
|
-
replayability?:
|
|
281
|
+
action?: ReviewStatusV3["action"];
|
|
282
|
+
replayability?: ReviewStatusV3["replayability"];
|
|
275
283
|
lineageId?: string;
|
|
276
284
|
authorityVersion?: "compact-v2" | "legacy-v1";
|
|
277
|
-
authorityState?: NonNullable<
|
|
278
|
-
receiptStatus?:
|
|
285
|
+
authorityState?: NonNullable<ReviewStatusV3["authority"]>["state"];
|
|
286
|
+
receiptStatus?: ReviewStatusV3["receipt"]["status"];
|
|
279
287
|
baseTree?: string;
|
|
280
288
|
currentCandidateTree?: string;
|
|
281
289
|
paths?: readonly string[];
|
|
282
|
-
} = {}):
|
|
290
|
+
} = {}): ReviewStatusV3 {
|
|
283
291
|
const applicability = options.applicability ?? "current_target";
|
|
284
292
|
const action = options.action ?? (applicability === "current_target" ? "finalize" : applicability === "unrelated" ? "start" : applicability === "ambiguous" ? "select_lineage" : "repair_authority");
|
|
285
293
|
const replayability = options.replayability ?? (action === "reconcile_finalize" ? "status_required" : applicability === "ambiguous" ? "status_required" : applicability === "corrupted" ? "manual_action_required" : "not_replayable");
|
|
@@ -305,15 +313,26 @@ function targetStatusFixture(options: {
|
|
|
305
313
|
initialSnapshotIdentity: sha,
|
|
306
314
|
currentSnapshotIdentity: sha,
|
|
307
315
|
};
|
|
316
|
+
const rawRepair = {
|
|
317
|
+
schema: UNSUPPORTED_REPAIR_ASSESSMENT.schema,
|
|
318
|
+
status: UNSUPPORTED_REPAIR_ASSESSMENT.status,
|
|
319
|
+
counts: {
|
|
320
|
+
lineages: 0, compact_lineages: 0, legacy_lineages: 0, events: 0, bytes: 0,
|
|
321
|
+
eligible_candidates: 0, unsupported_lineages: 0, conflicts: 0,
|
|
322
|
+
},
|
|
323
|
+
supported_operations: UNSUPPORTED_REPAIR_ASSESSMENT.supportedOperations,
|
|
324
|
+
authorization_schema: UNSUPPORTED_REPAIR_ASSESSMENT.authorizationSchema,
|
|
325
|
+
};
|
|
308
326
|
const raw: Record<string, unknown> = {
|
|
309
|
-
schema: "gentle-ai.review-integration.status/
|
|
310
|
-
contract: "gentle-ai.review-integration/
|
|
327
|
+
schema: "gentle-ai.review-integration.status/v3",
|
|
328
|
+
contract: "gentle-ai.review-integration/v2",
|
|
311
329
|
operation: "review.status",
|
|
312
330
|
applicability,
|
|
313
331
|
receipt: { status: receiptStatus },
|
|
314
332
|
action,
|
|
315
333
|
replayability,
|
|
316
334
|
target_identity: sha,
|
|
335
|
+
repair: rawRepair,
|
|
317
336
|
projection: {
|
|
318
337
|
schema: projection.schema,
|
|
319
338
|
kind: projection.kind,
|
|
@@ -336,7 +355,7 @@ function targetStatusFixture(options: {
|
|
|
336
355
|
}
|
|
337
356
|
if (action === "reconcile_finalize") raw.reconciliation = { required: true };
|
|
338
357
|
return {
|
|
339
|
-
contract: "gentle-ai.review-integration/
|
|
358
|
+
contract: "gentle-ai.review-integration/v2",
|
|
340
359
|
applicability,
|
|
341
360
|
...(applicability === "current_target" ? { authority: { version: authorityVersion, lineageId, state: authorityState, generation: 1, revision: sha } } : {}),
|
|
342
361
|
receipt: { status: receiptStatus },
|
|
@@ -346,11 +365,116 @@ function targetStatusFixture(options: {
|
|
|
346
365
|
...(action === "reconcile_finalize" ? { reconciliation: { required: true as const } } : {}),
|
|
347
366
|
targetIdentity: sha,
|
|
348
367
|
projection,
|
|
368
|
+
repair: UNSUPPORTED_REPAIR_ASSESSMENT,
|
|
349
369
|
candidates: applicability === "ambiguous" ? [lineageId, "other-lineage"] : [],
|
|
350
370
|
raw,
|
|
351
371
|
};
|
|
352
372
|
}
|
|
353
373
|
|
|
374
|
+
function bindReviewerManifest(status: ReviewStatusV3, cwd: string, manifestHash = `sha256:${"7".repeat(64)}`): ReviewStatusV3 {
|
|
375
|
+
const manifest = deriveChangedPathManifest(cwd, status.projection.baseTree, status.projection.currentCandidateTree).map((entry) => ({
|
|
376
|
+
...entry,
|
|
377
|
+
status: entry.status as "A" | "D" | "M" | "T",
|
|
378
|
+
intendedUntracked: status.projection.intendedUntracked.includes(entry.path),
|
|
379
|
+
}));
|
|
380
|
+
const subject = {
|
|
381
|
+
schema: "gentle-ai.review-artifact-subject/v2" as const,
|
|
382
|
+
subjectHash: `sha256:${"8".repeat(64)}`,
|
|
383
|
+
lineageId: status.authority!.lineageId,
|
|
384
|
+
authorityRevision: status.authority!.revision,
|
|
385
|
+
targetIdentity: status.targetIdentity,
|
|
386
|
+
baseTree: status.projection.baseTree,
|
|
387
|
+
candidateTree: status.projection.currentCandidateTree,
|
|
388
|
+
changedPathManifestSha256: manifestHash,
|
|
389
|
+
lens: "review-reliability" as const,
|
|
390
|
+
selectedOrder: 0,
|
|
391
|
+
};
|
|
392
|
+
status.nextTransition = {
|
|
393
|
+
kind: "collect",
|
|
394
|
+
reasonCode: "reviewer_results_required",
|
|
395
|
+
collect: { inputs: [{
|
|
396
|
+
name: "reviewer_result",
|
|
397
|
+
schema: "https://gentle-ai.dev/schema/review/reviewer/v1",
|
|
398
|
+
captureOperation: "review.capture-result",
|
|
399
|
+
arguments: [
|
|
400
|
+
{ name: "lineage", value: subject.lineageId, token: `--lineage=${subject.lineageId}` },
|
|
401
|
+
{ name: "expected-revision", value: subject.authorityRevision, token: `--expected-revision=${subject.authorityRevision}` },
|
|
402
|
+
{ name: "target", value: subject.targetIdentity, token: `--target=${subject.targetIdentity}` },
|
|
403
|
+
{ name: "lens", value: subject.lens, token: `--lens=${subject.lens}` },
|
|
404
|
+
{ name: "order", value: "0", token: "--order=0" },
|
|
405
|
+
{ name: "subject-hash", value: subject.subjectHash, token: `--subject-hash=${subject.subjectHash}` },
|
|
406
|
+
],
|
|
407
|
+
artifactSubject: subject,
|
|
408
|
+
baseTree: subject.baseTree,
|
|
409
|
+
candidateTree: subject.candidateTree,
|
|
410
|
+
changedPathManifest: manifest,
|
|
411
|
+
}] },
|
|
412
|
+
};
|
|
413
|
+
return status;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function bindCorrectionCollection(status: ReviewStatusV3): ReviewStatusV3 {
|
|
417
|
+
status.nextTransition = {
|
|
418
|
+
kind: "collect",
|
|
419
|
+
reasonCode: "verification_evidence_required",
|
|
420
|
+
collect: { inputs: [{
|
|
421
|
+
name: "verification_evidence",
|
|
422
|
+
schema: "gentle-ai.review-verification-evidence/v2",
|
|
423
|
+
captureOperation: "review.capture-evidence",
|
|
424
|
+
arguments: [{ name: "lineage", value: status.authority!.lineageId }],
|
|
425
|
+
}] },
|
|
426
|
+
};
|
|
427
|
+
delete status.validationRequest;
|
|
428
|
+
return status;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function bindTargetedValidation(status: ReviewStatusV3, requestHash = `sha256:${"9".repeat(64)}`): ReviewStatusV3 {
|
|
432
|
+
const request = {
|
|
433
|
+
schema: "gentle-ai.review-targeted-validation-request/v1" as const,
|
|
434
|
+
requestHash,
|
|
435
|
+
lineageId: status.authority!.lineageId,
|
|
436
|
+
expectedRevision: status.authority!.revision,
|
|
437
|
+
targetIdentity: status.targetIdentity,
|
|
438
|
+
fixFindingIds: [],
|
|
439
|
+
projection: "workspace" as const,
|
|
440
|
+
correctionCandidateTree: status.projection.currentCandidateTree,
|
|
441
|
+
correctionTargetIdentity: status.targetIdentity,
|
|
442
|
+
correctionPaths: status.projection.paths,
|
|
443
|
+
correctionPathsDigest: status.projection.pathsDigest,
|
|
444
|
+
};
|
|
445
|
+
status.validationRequest = request;
|
|
446
|
+
status.nextTransition = {
|
|
447
|
+
kind: "collect",
|
|
448
|
+
reasonCode: "targeted_validation_required",
|
|
449
|
+
collect: { inputs: [{
|
|
450
|
+
name: "targeted_validation",
|
|
451
|
+
schema: request.schema,
|
|
452
|
+
captureOperation: "external.run_targeted_validation",
|
|
453
|
+
arguments: [{ name: "lineage", value: request.lineageId }],
|
|
454
|
+
validationRequest: request,
|
|
455
|
+
}] },
|
|
456
|
+
};
|
|
457
|
+
return status;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function capturedCorrectionEvidence(status: ReviewStatusV3, outcome: "passed" | "verification_failed" | "procedural_tooling_failed", identityDigit: string) {
|
|
461
|
+
return {
|
|
462
|
+
schema: "gentle-ai.review-verification-evidence/v2" as const,
|
|
463
|
+
version: 2 as const,
|
|
464
|
+
lineageId: status.authority!.lineageId,
|
|
465
|
+
authorityRevision: status.authority!.revision,
|
|
466
|
+
targetIdentity: status.targetIdentity,
|
|
467
|
+
candidateTree: status.projection.currentCandidateTree,
|
|
468
|
+
pathsDigest: status.projection.pathsDigest,
|
|
469
|
+
paths: status.projection.paths,
|
|
470
|
+
ledgerIds: [],
|
|
471
|
+
rawPayloadSha256: `sha256:${identityDigit.repeat(64)}`,
|
|
472
|
+
rawPayloadBytes: 8,
|
|
473
|
+
outcome,
|
|
474
|
+
recordDigest: `sha256:${identityDigit.repeat(64)}`,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
354
478
|
function findResetRequests(value: unknown): unknown[] {
|
|
355
479
|
if (Array.isArray(value)) return value.flatMap(findResetRequests);
|
|
356
480
|
if (!value || typeof value !== "object") return [];
|
|
@@ -584,6 +708,7 @@ test("fresh negotiated registries reconstruct the frozen candidate before FINALI
|
|
|
584
708
|
status.projection.initialReviewTree = frozen.candidateTree;
|
|
585
709
|
status.projection.currentCandidateTree = frozen.candidateTree;
|
|
586
710
|
status.projection.paths = frozen.paths;
|
|
711
|
+
bindReviewerManifest(status, cwd);
|
|
587
712
|
frozen.cleanup();
|
|
588
713
|
let finalizedContent = "";
|
|
589
714
|
const { controller } = runtime(fakeNative({
|
|
@@ -602,6 +727,85 @@ test("fresh negotiated registries reconstruct the frozen candidate before FINALI
|
|
|
602
727
|
assert.equal((result.details as { result: { state: string } }).result.state, "approved");
|
|
603
728
|
});
|
|
604
729
|
|
|
730
|
+
test("fresh FINALIZE restores the provider manifest binding and rejects mode drift on the production path", async (t) => {
|
|
731
|
+
const cwd = repository(t);
|
|
732
|
+
writeFileSync(join(cwd, "app.ts"), "export const value = 2;\n");
|
|
733
|
+
const frozen = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
734
|
+
const status = targetStatusFixture({ lineageId: "manifest-lineage", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths });
|
|
735
|
+
bindReviewerManifest(status, cwd);
|
|
736
|
+
const collect = status.nextTransition!.collect!;
|
|
737
|
+
const input = collect.inputs[0]!;
|
|
738
|
+
const changed = input.changedPathManifest![0]!;
|
|
739
|
+
status.nextTransition = { ...status.nextTransition!, collect: { inputs: [{
|
|
740
|
+
...input,
|
|
741
|
+
changedPathManifest: [{
|
|
742
|
+
...changed,
|
|
743
|
+
newMode: "100755",
|
|
744
|
+
modeOnly: true,
|
|
745
|
+
}],
|
|
746
|
+
}] } };
|
|
747
|
+
frozen.cleanup();
|
|
748
|
+
let finalizes = 0;
|
|
749
|
+
const { controller } = runtime(fakeNative({
|
|
750
|
+
targetStatus: async () => status,
|
|
751
|
+
finalize: async () => {
|
|
752
|
+
finalizes += 1;
|
|
753
|
+
return { lineageId: "manifest-lineage", state: "approved", action: "approved", storeRevision: "r1" };
|
|
754
|
+
},
|
|
755
|
+
}), undefined, undefined, undefined, new CandidateViewRegistry());
|
|
756
|
+
const result = await controller.execute("manifest-mode-drift", {
|
|
757
|
+
operation: "finalize",
|
|
758
|
+
lineageId: "manifest-lineage",
|
|
759
|
+
input: JSON.stringify({ review_result: { lens_results: [{ lens: "review-reliability", findings: [], evidence: ["reviewed frozen candidate"] }] } }),
|
|
760
|
+
}, undefined, undefined, context(cwd));
|
|
761
|
+
assert.equal((result.details as { outcome?: string }).outcome, "native-operation-failed");
|
|
762
|
+
assert.equal(((result.details as { diagnostics?: { code?: string } }).diagnostics?.code), "manifest-mode-drift");
|
|
763
|
+
assert.equal(finalizes, 0, "manifest drift must stop before native FINALIZE");
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
test("fresh FINALIZE rejects inconsistent provider manifest hashes before production dispatch", async (t) => {
|
|
767
|
+
const cwd = repository(t);
|
|
768
|
+
writeFileSync(join(cwd, "app.ts"), "export const value = 2;\n");
|
|
769
|
+
const frozen = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
770
|
+
const status = bindReviewerManifest(targetStatusFixture({ lineageId: "manifest-hash-lineage", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths }), cwd);
|
|
771
|
+
const first = status.nextTransition!.collect!.inputs[0]!;
|
|
772
|
+
const secondSubjectHash = `sha256:${"a".repeat(64)}`;
|
|
773
|
+
status.nextTransition = { ...status.nextTransition!, collect: { inputs: [first, {
|
|
774
|
+
...first,
|
|
775
|
+
arguments: first.arguments.map((argument) => argument.name === "lens"
|
|
776
|
+
? { ...argument, value: "review-risk", token: "--lens=review-risk" }
|
|
777
|
+
: argument.name === "order"
|
|
778
|
+
? { ...argument, value: "1", token: "--order=1" }
|
|
779
|
+
: argument.name === "subject-hash"
|
|
780
|
+
? { ...argument, value: secondSubjectHash, token: `--subject-hash=${secondSubjectHash}` }
|
|
781
|
+
: argument),
|
|
782
|
+
artifactSubject: {
|
|
783
|
+
...first.artifactSubject!,
|
|
784
|
+
subjectHash: secondSubjectHash,
|
|
785
|
+
changedPathManifestSha256: `sha256:${"b".repeat(64)}`,
|
|
786
|
+
lens: "review-risk",
|
|
787
|
+
selectedOrder: 1,
|
|
788
|
+
},
|
|
789
|
+
}] } };
|
|
790
|
+
frozen.cleanup();
|
|
791
|
+
let finalizes = 0;
|
|
792
|
+
const { controller } = runtime(fakeNative({
|
|
793
|
+
targetStatus: async () => status,
|
|
794
|
+
finalize: async () => {
|
|
795
|
+
finalizes += 1;
|
|
796
|
+
return { lineageId: "manifest-hash-lineage", state: "approved", action: "approved", storeRevision: "r1" };
|
|
797
|
+
},
|
|
798
|
+
}), undefined, undefined, undefined, new CandidateViewRegistry());
|
|
799
|
+
const result = await controller.execute("manifest-hash-drift", {
|
|
800
|
+
operation: "finalize",
|
|
801
|
+
lineageId: "manifest-hash-lineage",
|
|
802
|
+
input: JSON.stringify({ review_result: { lens_results: [{ lens: "review-reliability", findings: [], evidence: ["reviewed frozen candidate"] }] } }),
|
|
803
|
+
}, undefined, undefined, context(cwd));
|
|
804
|
+
assert.equal((result.details as { outcome?: string }).outcome, "native-operation-failed");
|
|
805
|
+
assert.equal(((result.details as { diagnostics?: { code?: string } }).diagnostics?.code), "manifest-input-divergence");
|
|
806
|
+
assert.equal(finalizes, 0);
|
|
807
|
+
});
|
|
808
|
+
|
|
605
809
|
test("forecast-only FINALIZE reconstructs the frozen candidate after a fresh process (#176)", async (t) => {
|
|
606
810
|
const cwd = repository(t);
|
|
607
811
|
writeFileSync(join(cwd, "app.ts"), "export const value = 2;\n");
|
|
@@ -924,10 +1128,16 @@ test("fresh registry reload ignores raw correction state and follows the native
|
|
|
924
1128
|
const frozen = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
925
1129
|
mkdirSync(dirname(join(cwd, ".git", "gentle-ai", "review-transactions", "v2", "correction-lineage", "review-state.json")), { recursive: true });
|
|
926
1130
|
writeFileSync(join(cwd, ".git", "gentle-ai", "review-transactions", "v2", "correction-lineage", "review-state.json"), JSON.stringify({ schema: "gentle-ai.review-state-record/v2", state: { schema: "gentle-ai.review-state/v2", lineage_id: "correction-lineage", state: "correction_required", initial_snapshot: { kind: "current-changes", base_tree: frozen.baseTree, candidate_tree: frozen.candidateTree, paths: frozen.paths, paths_digest: "paths" }, current_snapshot: { kind: "current-changes", base_tree: frozen.baseTree, candidate_tree: frozen.candidateTree, paths: frozen.paths, paths_digest: "paths" }, fix_finding_ids: ["RELIABILITY-001"], findings: [{ id: "RELIABILITY-001", severity: "CRITICAL" }] } }));
|
|
927
|
-
const status = targetStatusFixture({ lineageId: "correction-lineage", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths });
|
|
1131
|
+
const status = bindCorrectionCollection(targetStatusFixture({ lineageId: "correction-lineage", authorityState: "correction_required", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths }));
|
|
1132
|
+
const validationStatus = bindTargetedValidation(targetStatusFixture({ lineageId: "correction-lineage", authorityState: "validating", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths }));
|
|
928
1133
|
frozen.cleanup();
|
|
929
1134
|
let finalizes = 0;
|
|
930
|
-
|
|
1135
|
+
let statuses = 0;
|
|
1136
|
+
const { controller } = runtime(fakeNative({
|
|
1137
|
+
finalize: async () => { finalizes += 1; return { lineageId: "correction-lineage", state: "approved", action: "approved", storeRevision: "r2" }; },
|
|
1138
|
+
targetStatus: async () => { statuses += 1; return statuses === 1 ? status : validationStatus; },
|
|
1139
|
+
captureEvidence: async () => capturedCorrectionEvidence(status, "passed", "6"),
|
|
1140
|
+
}), undefined, undefined, undefined, new CandidateViewRegistry());
|
|
931
1141
|
const required = await controller.execute("correction-validation-request", { operation: "finalize", lineageId: "correction-lineage", input: JSON.stringify({ final_evidence: "focused tests passed", final_verification_passed: true }) }, undefined, undefined, context(cwd));
|
|
932
1142
|
const request = required.details as { status: string; result: Record<string, unknown> };
|
|
933
1143
|
assert.equal(request.status, "in-progress");
|
|
@@ -938,6 +1148,140 @@ test("fresh registry reload ignores raw correction state and follows the native
|
|
|
938
1148
|
assert.equal(finalizes, 0);
|
|
939
1149
|
});
|
|
940
1150
|
|
|
1151
|
+
test("production correction routing captures evidence before validation and enforces all three outcomes", async (t) => {
|
|
1152
|
+
for (const [outcome, expectedKind] of [
|
|
1153
|
+
["passed", "run-targeted-validation"],
|
|
1154
|
+
["verification_failed", "recapture-required"],
|
|
1155
|
+
["procedural_tooling_failed", "terminal-escalation"],
|
|
1156
|
+
] as const) {
|
|
1157
|
+
await t.test(outcome, async (scenario) => {
|
|
1158
|
+
const cwd = repository(scenario);
|
|
1159
|
+
writeFileSync(join(cwd, "app.ts"), `export const outcome = ${JSON.stringify(outcome)};\n`);
|
|
1160
|
+
const frozen = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
1161
|
+
const beforeCapture = bindCorrectionCollection(targetStatusFixture({
|
|
1162
|
+
lineageId: `correction-${outcome.replaceAll("_", "-")}`,
|
|
1163
|
+
authorityState: "correction_required",
|
|
1164
|
+
baseTree: frozen.baseTree,
|
|
1165
|
+
currentCandidateTree: frozen.candidateTree,
|
|
1166
|
+
paths: frozen.paths,
|
|
1167
|
+
}));
|
|
1168
|
+
const afterCapture = targetStatusFixture({
|
|
1169
|
+
lineageId: beforeCapture.authority!.lineageId,
|
|
1170
|
+
authorityState: outcome === "procedural_tooling_failed" ? "escalated" : outcome === "passed" ? "validating" : "correction_required",
|
|
1171
|
+
action: outcome === "procedural_tooling_failed" ? "stop" : "finalize",
|
|
1172
|
+
baseTree: frozen.baseTree,
|
|
1173
|
+
currentCandidateTree: frozen.candidateTree,
|
|
1174
|
+
paths: frozen.paths,
|
|
1175
|
+
});
|
|
1176
|
+
if (outcome === "passed") bindTargetedValidation(afterCapture);
|
|
1177
|
+
else if (outcome === "verification_failed") bindCorrectionCollection(afterCapture);
|
|
1178
|
+
frozen.cleanup();
|
|
1179
|
+
const calls: string[] = [];
|
|
1180
|
+
let statuses = 0;
|
|
1181
|
+
let finalizes = 0;
|
|
1182
|
+
const { controller } = runtime(fakeNative({
|
|
1183
|
+
targetStatus: async () => {
|
|
1184
|
+
calls.push("status");
|
|
1185
|
+
statuses += 1;
|
|
1186
|
+
return statuses === 1 ? beforeCapture : afterCapture;
|
|
1187
|
+
},
|
|
1188
|
+
captureEvidence: async (request) => {
|
|
1189
|
+
calls.push("capture-evidence");
|
|
1190
|
+
assert.equal(request.outcome, outcome);
|
|
1191
|
+
return capturedCorrectionEvidence(beforeCapture, outcome, outcome === "passed" ? "1" : outcome === "verification_failed" ? "2" : "3");
|
|
1192
|
+
},
|
|
1193
|
+
finalize: async () => {
|
|
1194
|
+
calls.push("finalize");
|
|
1195
|
+
finalizes += 1;
|
|
1196
|
+
return { lineageId: beforeCapture.authority!.lineageId, state: "approved", action: "approved", storeRevision: "r-final" };
|
|
1197
|
+
},
|
|
1198
|
+
}), undefined, undefined, undefined, new CandidateViewRegistry());
|
|
1199
|
+
const validation = outcome === "passed" ? {
|
|
1200
|
+
request_hash: "9".repeat(64), correction_ids: [],
|
|
1201
|
+
original_criteria: { passed: true, evidence: ["acceptance passes"] },
|
|
1202
|
+
correction_regression: { passed: true, evidence: ["regression passes"] },
|
|
1203
|
+
fix_caused_findings: [], follow_ups: [],
|
|
1204
|
+
} : undefined;
|
|
1205
|
+
const result = await controller.execute(`correction-${outcome}`, {
|
|
1206
|
+
operation: "finalize",
|
|
1207
|
+
lineageId: beforeCapture.authority!.lineageId,
|
|
1208
|
+
input: JSON.stringify({ final_evidence: `evidence: ${outcome}`, final_verification_outcome: outcome, ...(validation === undefined ? {} : { validation }) }),
|
|
1209
|
+
}, undefined, undefined, context(cwd));
|
|
1210
|
+
const details = result.details as Record<string, unknown>;
|
|
1211
|
+
assert.deepEqual(calls.slice(0, 3), ["status", "capture-evidence", "status"], "STATUS must collect evidence before targeted validation can run");
|
|
1212
|
+
assert.equal((details.correction_step as { kind?: string } | undefined)?.kind, expectedKind);
|
|
1213
|
+
assert.equal(finalizes, outcome === "passed" ? 1 : 0);
|
|
1214
|
+
if (outcome === "passed") assert.deepEqual(calls, ["status", "capture-evidence", "status", "finalize"]);
|
|
1215
|
+
else assert.deepEqual(calls, ["status", "capture-evidence", "status"]);
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
test("production correction routing fails closed when targeted validation is offered before evidence capture", async (t) => {
|
|
1221
|
+
const cwd = repository(t);
|
|
1222
|
+
writeFileSync(join(cwd, "app.ts"), "export const corrected = true;\n");
|
|
1223
|
+
const frozen = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
1224
|
+
const premature = bindTargetedValidation(targetStatusFixture({ lineageId: "premature-validation", authorityState: "validating", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths }));
|
|
1225
|
+
frozen.cleanup();
|
|
1226
|
+
let captures = 0;
|
|
1227
|
+
let finalizes = 0;
|
|
1228
|
+
const { controller } = runtime(fakeNative({
|
|
1229
|
+
targetStatus: async () => premature,
|
|
1230
|
+
captureEvidence: async () => { captures += 1; return capturedCorrectionEvidence(premature, "passed", "4"); },
|
|
1231
|
+
finalize: async () => { finalizes += 1; return { lineageId: "premature-validation", state: "approved", action: "approved", storeRevision: "r1" }; },
|
|
1232
|
+
}), undefined, undefined, undefined, new CandidateViewRegistry());
|
|
1233
|
+
const result = await controller.execute("premature-targeted-validation", {
|
|
1234
|
+
operation: "finalize",
|
|
1235
|
+
lineageId: "premature-validation",
|
|
1236
|
+
input: JSON.stringify({
|
|
1237
|
+
final_evidence: "evidence first",
|
|
1238
|
+
final_verification_outcome: "passed",
|
|
1239
|
+
validation: { request_hash: "9".repeat(64), correction_ids: [], original_criteria: { passed: true, evidence: ["passes"] }, correction_regression: { passed: true, evidence: ["passes"] }, fix_caused_findings: [], follow_ups: [] },
|
|
1240
|
+
}),
|
|
1241
|
+
}, undefined, undefined, context(cwd));
|
|
1242
|
+
assert.equal((result.details as { outcome?: string }).outcome, "native-operation-failed");
|
|
1243
|
+
assert.equal(captures, 0);
|
|
1244
|
+
assert.equal(finalizes, 0);
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
test("production correction routing rejects a second capture that reuses failed evidence identity", async (t) => {
|
|
1248
|
+
const cwd = repository(t);
|
|
1249
|
+
writeFileSync(join(cwd, "app.ts"), "export const corrected = 1;\n");
|
|
1250
|
+
const frozen = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
1251
|
+
const status = bindCorrectionCollection(targetStatusFixture({ lineageId: "distinct-evidence", authorityState: "correction_required", baseTree: frozen.baseTree, currentCandidateTree: frozen.candidateTree, paths: frozen.paths }));
|
|
1252
|
+
frozen.cleanup();
|
|
1253
|
+
let captures = 0;
|
|
1254
|
+
let finalizes = 0;
|
|
1255
|
+
const candidateViews = new CandidateViewRegistry();
|
|
1256
|
+
const { controller } = runtime(fakeNative({
|
|
1257
|
+
targetStatus: async () => status,
|
|
1258
|
+
captureEvidence: async () => {
|
|
1259
|
+
captures += 1;
|
|
1260
|
+
return capturedCorrectionEvidence(status, "verification_failed", "5");
|
|
1261
|
+
},
|
|
1262
|
+
finalize: async () => { finalizes += 1; return { lineageId: "distinct-evidence", state: "approved", action: "approved", storeRevision: "r1" }; },
|
|
1263
|
+
}), undefined, undefined, undefined, candidateViews);
|
|
1264
|
+
const request = {
|
|
1265
|
+
operation: "finalize",
|
|
1266
|
+
lineageId: "distinct-evidence",
|
|
1267
|
+
input: JSON.stringify({ final_evidence: "verification failed", final_verification_outcome: "verification_failed" }),
|
|
1268
|
+
};
|
|
1269
|
+
const first = await controller.execute("distinct-first", request, undefined, undefined, context(cwd));
|
|
1270
|
+
assert.equal(((first.details as { correction_step?: { kind?: string } }).correction_step?.kind), "recapture-required");
|
|
1271
|
+
writeFileSync(join(cwd, "app.ts"), "export const corrected = 2;\n");
|
|
1272
|
+
const changed = new CandidateViewRegistry().create({ contributorRoot: cwd });
|
|
1273
|
+
status.projection.currentCandidateTree = changed.candidateTree;
|
|
1274
|
+
status.projection.paths = changed.paths;
|
|
1275
|
+
changed.cleanup();
|
|
1276
|
+
const second = await controller.execute("distinct-second", request, undefined, undefined, context(cwd));
|
|
1277
|
+
assert.equal((second.details as { outcome?: string }).outcome, "native-operation-failed");
|
|
1278
|
+
assert.match(JSON.stringify(second.details), /correction-evidence-replaced/);
|
|
1279
|
+
assert.equal(captures, 2);
|
|
1280
|
+
assert.equal(finalizes, 0);
|
|
1281
|
+
candidateViews.cleanupTerminal("distinct-evidence", "escalated");
|
|
1282
|
+
execFileSync("chmod", ["-R", "u+w", cwd]);
|
|
1283
|
+
});
|
|
1284
|
+
|
|
941
1285
|
test("native FINALIZE derives and requires the trusted refuter request before invoking native FINALIZE", async (t) => {
|
|
942
1286
|
const cwd = repository(t);
|
|
943
1287
|
let finalizes = 0;
|
|
@@ -1555,6 +1899,35 @@ test("native allow registers one authorization and bash-time revalidation consum
|
|
|
1555
1899
|
assert.equal(validates, 2);
|
|
1556
1900
|
});
|
|
1557
1901
|
|
|
1902
|
+
test("native VALIDATE delivery disabled/unmanaged renders as a successful skipped envelope before the maintainer-exception check, minting no authorization", async (t) => {
|
|
1903
|
+
const cwd = repository(t);
|
|
1904
|
+
let validations = 0;
|
|
1905
|
+
const { controller } = runtime(fakeNative({
|
|
1906
|
+
validate: async () => {
|
|
1907
|
+
validations += 1;
|
|
1908
|
+
return {
|
|
1909
|
+
allowed: false,
|
|
1910
|
+
result: "invalidated",
|
|
1911
|
+
action: "repository-policy",
|
|
1912
|
+
reason: "review-driven development is disabled and no receipt governs this candidate, so delivery follows ordinary repository policy",
|
|
1913
|
+
gateContext: nativeGateContext("native-lineage", "r1", git(cwd, "write-tree")),
|
|
1914
|
+
delivery: "disabled/unmanaged",
|
|
1915
|
+
};
|
|
1916
|
+
},
|
|
1917
|
+
targetStatus: async () => targetStatusFixture({ lineageId: "native-lineage", baseTree: git(cwd, "rev-parse", "HEAD^{tree}"), currentCandidateTree: git(cwd, "write-tree"), paths: [] }),
|
|
1918
|
+
}), undefined, undefined, undefined, new CandidateViewRegistry());
|
|
1919
|
+
const command = "git commit -m native";
|
|
1920
|
+
const validated = await controller.execute("disabled-delivery", { operation: "validate", lineageId: "native-lineage", idempotencyKey: "disabled-delivery", command, input: "{}" }, undefined, undefined, context(cwd));
|
|
1921
|
+
const details = validated.details as Record<string, unknown>;
|
|
1922
|
+
assert.equal(validations, 1);
|
|
1923
|
+
assert.equal(details.status, "skipped");
|
|
1924
|
+
assert.equal(details.outcome, "review-disabled-unmanaged-delivery");
|
|
1925
|
+
assert.equal((details.result as Record<string, unknown>).delivery, "disabled/unmanaged");
|
|
1926
|
+
assert.equal((details.result as Record<string, unknown>).allowed, false);
|
|
1927
|
+
assert.equal(details.maintainer_exception_request, undefined, "a repository-policy delivery skip must never mint a maintainer-exception request");
|
|
1928
|
+
assert.equal(details.authorization, undefined, "a repository-policy delivery skip must never mint an authorization");
|
|
1929
|
+
});
|
|
1930
|
+
|
|
1558
1931
|
test("fresh candidate registry binds a resumed zero-lens native START through FINALIZE and pre-commit", async (t) => {
|
|
1559
1932
|
const cwd = repository(t);
|
|
1560
1933
|
writeFileSync(join(cwd, "app.ts"), "export const value = 2;\n");
|
|
@@ -8,7 +8,7 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
|
|
|
8
8
|
import { createGentleAiExtension } from "../extensions/gentle-ai.ts";
|
|
9
9
|
import type { NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
10
10
|
import { CandidateViewRegistry } from "../lib/review-candidate-view.ts";
|
|
11
|
-
import type {
|
|
11
|
+
import type { AuthorityRepairAssessmentV1, ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
12
12
|
|
|
13
13
|
interface RegisteredTool {
|
|
14
14
|
execute: (
|
|
@@ -90,11 +90,19 @@ function fakeNative(overrides: Partial<NativeReviewCli> = {}): NativeReviewCli {
|
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
const UNSUPPORTED_REPAIR_ASSESSMENT: AuthorityRepairAssessmentV1 = {
|
|
94
|
+
schema: "gentle-ai.review-authority-repair-assessment/v1",
|
|
95
|
+
status: "unsupported",
|
|
96
|
+
counts: { lineages: 0, compactLineages: 0, legacyLineages: 0, events: 0, bytes: 0, eligibleCandidates: 0, unsupportedLineages: 0, conflicts: 0 },
|
|
97
|
+
supportedOperations: ["review/complete-fix", "review/validate-fix"],
|
|
98
|
+
authorizationSchema: "gentle-ai.review-repair-authorization/v1",
|
|
99
|
+
};
|
|
100
|
+
|
|
93
101
|
function targetStatusFixture(options: {
|
|
94
102
|
applicability?: "current_target" | "unrelated";
|
|
95
|
-
action?:
|
|
103
|
+
action?: ReviewStatusV3["action"];
|
|
96
104
|
lineageId?: string;
|
|
97
|
-
} = {}):
|
|
105
|
+
} = {}): ReviewStatusV3 {
|
|
98
106
|
const applicability = options.applicability ?? "current_target";
|
|
99
107
|
const action = options.action ?? (applicability === "current_target" ? "finalize" : "start");
|
|
100
108
|
const lineageId = options.lineageId ?? "native-lineage";
|
|
@@ -114,15 +122,26 @@ function targetStatusFixture(options: {
|
|
|
114
122
|
initialSnapshotIdentity: sha,
|
|
115
123
|
currentSnapshotIdentity: sha,
|
|
116
124
|
};
|
|
125
|
+
const rawRepair = {
|
|
126
|
+
schema: UNSUPPORTED_REPAIR_ASSESSMENT.schema,
|
|
127
|
+
status: UNSUPPORTED_REPAIR_ASSESSMENT.status,
|
|
128
|
+
counts: {
|
|
129
|
+
lineages: 0, compact_lineages: 0, legacy_lineages: 0, events: 0, bytes: 0,
|
|
130
|
+
eligible_candidates: 0, unsupported_lineages: 0, conflicts: 0,
|
|
131
|
+
},
|
|
132
|
+
supported_operations: UNSUPPORTED_REPAIR_ASSESSMENT.supportedOperations,
|
|
133
|
+
authorization_schema: UNSUPPORTED_REPAIR_ASSESSMENT.authorizationSchema,
|
|
134
|
+
};
|
|
117
135
|
const raw: Record<string, unknown> = {
|
|
118
|
-
schema: "gentle-ai.review-integration.status/
|
|
119
|
-
contract: "gentle-ai.review-integration/
|
|
136
|
+
schema: "gentle-ai.review-integration.status/v3",
|
|
137
|
+
contract: "gentle-ai.review-integration/v2",
|
|
120
138
|
operation: "review.status",
|
|
121
139
|
applicability,
|
|
122
140
|
receipt: { status: applicability === "current_target" ? "expected_missing" : "not_applicable" },
|
|
123
141
|
action,
|
|
124
142
|
replayability: "not_replayable",
|
|
125
143
|
target_identity: sha,
|
|
144
|
+
repair: rawRepair,
|
|
126
145
|
projection: {
|
|
127
146
|
schema: projection.schema,
|
|
128
147
|
kind: projection.kind,
|
|
@@ -144,7 +163,7 @@ function targetStatusFixture(options: {
|
|
|
144
163
|
raw.frozen = { tier: "medium", original_changed_lines: 2, correction_budget: 1 };
|
|
145
164
|
}
|
|
146
165
|
return {
|
|
147
|
-
contract: "gentle-ai.review-integration/
|
|
166
|
+
contract: "gentle-ai.review-integration/v2",
|
|
148
167
|
applicability,
|
|
149
168
|
...(applicability === "current_target" ? { authority: { version: "compact-v2" as const, lineageId, state: "reviewing", generation: 1, revision: sha } } : {}),
|
|
150
169
|
receipt: { status: applicability === "current_target" ? "expected_missing" : "not_applicable" },
|
|
@@ -153,6 +172,7 @@ function targetStatusFixture(options: {
|
|
|
153
172
|
...(applicability === "current_target" ? { frozen: { tier: "medium" as const, originalChangedLines: 2, correctionBudget: 1 } } : {}),
|
|
154
173
|
targetIdentity: sha,
|
|
155
174
|
projection,
|
|
175
|
+
repair: UNSUPPORTED_REPAIR_ASSESSMENT,
|
|
156
176
|
candidates: [],
|
|
157
177
|
raw,
|
|
158
178
|
};
|