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.
Files changed (107) hide show
  1. package/README.md +40 -9
  2. package/assets/agents/review-readability.md +0 -1
  3. package/assets/agents/review-reliability.md +0 -1
  4. package/assets/agents/review-resilience.md +0 -1
  5. package/assets/agents/review-risk.md +0 -1
  6. package/contracts/review-integration/v1/fixtures/binding-revision-conflict.fixture.json +25 -0
  7. package/contracts/review-integration/v1/fixtures/capabilities-v1.2.fixture.json +263 -0
  8. package/contracts/review-integration/v1/fixtures/capabilities-v1.3.fixture.json +301 -0
  9. package/contracts/review-integration/v1/fixtures/capabilities-v1.4.fixture.json +312 -0
  10. package/contracts/review-integration/v1/fixtures/capabilities-v1.5.fixture.json +321 -0
  11. package/contracts/review-integration/v1/fixtures/consent.fixture.json +36 -0
  12. package/contracts/review-integration/v1/fixtures/final-verification-incident.fixture.json +1 -0
  13. package/contracts/review-integration/v1/fixtures/operation.fixture.json +4 -0
  14. package/contracts/review-integration/v1/fixtures/repair-preflight.fixture.json +54 -0
  15. package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +97 -0
  16. package/contracts/review-integration/v1/fixtures/start.fixture.json +25 -1
  17. package/contracts/review-integration/v1/fixtures/status-ambiguous.fixture.json +4 -0
  18. package/contracts/review-integration/v1/fixtures/status-corrupted.fixture.json +4 -0
  19. package/contracts/review-integration/v1/fixtures/status-recover.fixture.json +4 -0
  20. package/contracts/review-integration/v1/fixtures/status-unrelated.fixture.json +4 -0
  21. package/contracts/review-integration/v1/fixtures/status-v2-ambiguous.fixture.json +110 -0
  22. package/contracts/review-integration/v1/fixtures/status-v2-corrupted.fixture.json +107 -0
  23. package/contracts/review-integration/v1/fixtures/status-v2-final-verification-retry.fixture.json +192 -0
  24. package/contracts/review-integration/v1/fixtures/status-v2-recover.fixture.json +130 -0
  25. package/contracts/review-integration/v1/fixtures/status-v2-repair.fixture.json +161 -0
  26. package/contracts/review-integration/v1/fixtures/status-v2-unrelated.fixture.json +103 -0
  27. package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +193 -0
  28. package/contracts/review-integration/v1/fixtures/status.fixture.json +19 -5
  29. package/contracts/review-integration/v1/fixtures/verification-evidence.fixture.json +1 -0
  30. package/contracts/review-integration/v1/schemas/admitted-result.schema.json +35 -0
  31. package/contracts/review-integration/v1/schemas/artifact-subject.schema.json +33 -0
  32. package/contracts/review-integration/v1/schemas/authority-repair-assessment.schema.json +247 -0
  33. package/contracts/review-integration/v1/schemas/capabilities-v1.2.schema.json +249 -0
  34. package/contracts/review-integration/v1/schemas/capabilities-v1.3.schema.json +429 -0
  35. package/contracts/review-integration/v1/schemas/capabilities-v1.4.schema.json +432 -0
  36. package/contracts/review-integration/v1/schemas/capabilities-v1.5.schema.json +154 -0
  37. package/contracts/review-integration/v1/schemas/consent.schema.json +86 -0
  38. package/contracts/review-integration/v1/schemas/failure.schema.json +54 -8
  39. package/contracts/review-integration/v1/schemas/final-verification-incident.schema.json +51 -0
  40. package/contracts/review-integration/v1/schemas/operation.schema.json +204 -31
  41. package/contracts/review-integration/v1/schemas/repair.schema.json +251 -0
  42. package/contracts/review-integration/v1/schemas/result-artifact-v2.schema.json +38 -0
  43. package/contracts/review-integration/v1/schemas/start-v2.schema.json +216 -0
  44. package/contracts/review-integration/v1/schemas/start.schema.json +79 -3
  45. package/contracts/review-integration/v1/schemas/status-v2.schema.json +313 -0
  46. package/contracts/review-integration/v1/schemas/status.schema.json +37 -10
  47. package/contracts/review-integration/v1/schemas/targeted-validation-request.schema.json +46 -0
  48. package/contracts/review-integration/v1/schemas/verification-evidence.schema.json +64 -0
  49. package/contracts/review-integration/v2/fixtures/capabilities.fixture.json +111 -0
  50. package/contracts/review-integration/v2/fixtures/consent.fixture.json +36 -0
  51. package/contracts/review-integration/v2/fixtures/start.fixture.json +93 -0
  52. package/contracts/review-integration/v2/fixtures/status.fixture.json +113 -0
  53. package/contracts/review-integration/v2/schemas/admitted-result.schema.json +14 -0
  54. package/contracts/review-integration/v2/schemas/artifact-subject.schema.json +36 -0
  55. package/contracts/review-integration/v2/schemas/capabilities.schema.json +111 -0
  56. package/contracts/review-integration/v2/schemas/consent.schema.json +70 -0
  57. package/contracts/review-integration/v2/schemas/failure.schema.json +35 -0
  58. package/contracts/review-integration/v2/schemas/operation.schema.json +46 -0
  59. package/contracts/review-integration/v2/schemas/repair.schema.json +22 -0
  60. package/contracts/review-integration/v2/schemas/start.schema.json +106 -0
  61. package/contracts/review-integration/v2/schemas/status.schema.json +104 -0
  62. package/docs/native-authority-architecture.md +18 -3
  63. package/docs/review-integration.md +175 -27
  64. package/extensions/gentle-ai.ts +414 -19
  65. package/lib/gentle-ai-binary.ts +1 -1
  66. package/lib/native-review-cli.ts +679 -57
  67. package/lib/review-candidate-view.ts +131 -1
  68. package/lib/review-compact-contract.ts +12 -3
  69. package/lib/review-consent-latch.ts +51 -0
  70. package/lib/review-correction-lifecycle.ts +170 -0
  71. package/lib/review-integration-v2.ts +1563 -0
  72. package/package.json +15 -11
  73. package/runtime/gentle-ai-binary.mjs +1 -1
  74. package/runtime/native-review-cli.mjs +663 -41
  75. package/runtime/review-integration-v2.mjs +1564 -0
  76. package/scripts/build-git-commit-transaction-runner.mjs +1 -1
  77. package/scripts/gentle-ai-installer.mjs +16 -11
  78. package/scripts/install-gentle-ai.mjs +7 -3
  79. package/scripts/test-packed-runner.mjs +2 -2
  80. package/scripts/verify-package-files.mjs +216 -64
  81. package/skills/release/SKILL.md +38 -11
  82. package/tests/devbinary/native-review-parity.devtest.ts +348 -0
  83. package/tests/gentle-ai-binary.test.ts +8 -1
  84. package/tests/gentle-ai-installer.test.ts +24 -21
  85. package/tests/native-binary-gate.test.ts +145 -0
  86. package/tests/native-review-capability-contract.test.ts +87 -0
  87. package/tests/native-review-cli.test.ts +44 -1
  88. package/tests/native-review-parity-runtime.test.ts +70 -23
  89. package/tests/native-review-parity.test.ts +673 -0
  90. package/tests/native-risk-evidence-parity.test.ts +91 -0
  91. package/tests/package-manifest.test.ts +72 -7
  92. package/tests/review-actor-tool-deny.test.ts +20 -5
  93. package/tests/review-candidate-view.test.ts +331 -1
  94. package/tests/review-compact-contract.test.ts +5 -0
  95. package/tests/review-consent-latch.test.ts +67 -0
  96. package/tests/review-controller-native-recovery.test.ts +180 -5
  97. package/tests/review-controller-native-routing.test.ts +385 -12
  98. package/tests/review-controller-workspace-root.test.ts +26 -6
  99. package/tests/review-correction-lifecycle.test.ts +174 -0
  100. package/tests/review-integration-v2.test.ts +539 -0
  101. package/tests/runtime-harness.mjs +187 -0
  102. package/tests/support/native-binary-gate.ts +86 -0
  103. package/tests/verify-package-files.test.ts +119 -0
  104. package/lib/review-integration-v1.ts +0 -795
  105. package/runtime/review-integration-v1.mjs +0 -796
  106. package/tests/native-review-integration-v1.test.ts +0 -102
  107. package/tests/review-integration-v1.test.ts +0 -530
@@ -0,0 +1,174 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import {
4
+ CORRECTION_OUTCOMES,
5
+ CorrectionEvidenceReplacedError,
6
+ CorrectionOutcomeError,
7
+ assertDistinctCorrectionEvidence,
8
+ resolveCorrectionStep,
9
+ } from "../lib/review-correction-lifecycle.ts";
10
+
11
+ // The provider owns the evidence directory and the budget ledger. Pi's job is
12
+ // narrower and entirely decidable from data: given a status and one captured
13
+ // evidence record, which step comes next, and is it ever allowed to reuse a
14
+ // prior evidence identity. Keeping that a pure function is what makes these
15
+ // branches testable without spawning the binary — the alternative the design
16
+ // rejected was inlining them beside the existing conditionals, where the
17
+ // single-correction invariant would be enforced by control flow instead.
18
+
19
+ const STATUS = Object.freeze({
20
+ lineageId: "review-b39d803b68a90767",
21
+ targetIdentity: "sha256:b39d803b68a90767c822c6bac906c2ff512034df2a2432d4c2d8e48f45c11b0a",
22
+ authorityRevision: "sha256:1298b2f5dd1ab7208bd7dfbf2e5802b21380617050f7ae3d7786bffcf0c6bb29",
23
+ correctionBudget: 56,
24
+ changedLinesCharged: 0,
25
+ });
26
+
27
+ function evidence(outcome: string, overrides: Record<string, unknown> = {}) {
28
+ return {
29
+ outcome,
30
+ evidenceIdentity: "sha256:a077148ccb9f1c6e8b65fbfa984309676eee3e35311caaca31d6d61f6351f9fa",
31
+ recordDigest: "sha256:a077148ccb9f1c6e8b65fbfa984309676eee3e35311caaca31d6d61f6351f9fa",
32
+ candidateTree: "0542f90ab7b6af3fe79340509ec6420f0cc9ef68",
33
+ rawPayloadSha256: "sha256:9013edfc64295a20c3f4ed41a7cf61d81a6617bb7180fd95b129cb77ff53fa00",
34
+ ...overrides,
35
+ };
36
+ }
37
+
38
+ test("the outcome domain is exactly the three closed provider values", () => {
39
+ assert.deepEqual([...CORRECTION_OUTCOMES], ["passed", "verification_failed", "procedural_tooling_failed"]);
40
+ });
41
+
42
+ test("an outcome outside the closed domain is rejected with no step and no charge", () => {
43
+ for (const rejected of ["failed", "PASSED", "pass", "ok", "", "verification-failed", "skipped"]) {
44
+ assert.throws(
45
+ () => resolveCorrectionStep(STATUS, evidence(rejected)),
46
+ (error: unknown) => error instanceof CorrectionOutcomeError && /outcome/.test((error as Error).message),
47
+ `outcome ${JSON.stringify(rejected)} must be refused`,
48
+ );
49
+ }
50
+ });
51
+
52
+ test("a missing or non-string outcome is refused rather than defaulted", () => {
53
+ for (const rejected of [undefined, null, 1, true, {}, []]) {
54
+ assert.throws(() => resolveCorrectionStep(STATUS, evidence(rejected as unknown as string)), CorrectionOutcomeError);
55
+ }
56
+ });
57
+
58
+ test("passed is the only outcome that unlocks targeted validation", () => {
59
+ const step = resolveCorrectionStep(STATUS, evidence("passed"));
60
+
61
+ assert.equal(step.kind, "run-targeted-validation");
62
+ assert.equal(step.unlocksTargetedValidation, true);
63
+ assert.equal(step.transactionOpen, false);
64
+ assert.equal(step.escalation, undefined);
65
+ // The provider issues the request; Pi must re-query STATUS for it rather
66
+ // than inventing one, which is why the step names the operation instead of
67
+ // carrying a fabricated payload.
68
+ assert.equal(step.expectCaptureOperation, "external.run_targeted_validation");
69
+ });
70
+
71
+ test("verification_failed keeps the transaction open and charges nothing", () => {
72
+ const step = resolveCorrectionStep(STATUS, evidence("verification_failed"));
73
+
74
+ assert.equal(step.kind, "recapture-required");
75
+ assert.equal(step.transactionOpen, true);
76
+ assert.equal(step.unlocksTargetedValidation, false);
77
+ assert.equal(step.attemptConsumed, false);
78
+ assert.equal(step.changedLinesCharged, 0);
79
+ assert.equal(step.budgetConsumed, 0);
80
+ assert.equal(step.autoRetry, false);
81
+ });
82
+
83
+ test("verification_failed carries the prior identity as supersedes and demands a NEW capture", () => {
84
+ const first = evidence("verification_failed");
85
+ const step = resolveCorrectionStep(STATUS, first);
86
+
87
+ assert.equal(step.kind, "recapture-required");
88
+ assert.equal(step.supersedes, first.evidenceIdentity);
89
+ assert.equal(step.requiresNewCapture, true);
90
+ // It must never hand back a step that resubmits the same candidate bytes.
91
+ assert.notEqual(step.supersedes, undefined);
92
+ assert.equal(Object.hasOwn(step, "resubmitCandidate"), false);
93
+ });
94
+
95
+ test("verification_failed never consumes budget even when lines were already charged", () => {
96
+ const charged = { ...STATUS, changedLinesCharged: 40 };
97
+ const step = resolveCorrectionStep(charged, evidence("verification_failed"));
98
+
99
+ // The invariant is about what THIS outcome adds, not about resetting prior
100
+ // accounting: a failed verification must not move the needle at all.
101
+ assert.equal(step.budgetConsumed, 0);
102
+ assert.equal(step.changedLinesCharged, 40);
103
+ assert.equal(step.attemptConsumed, false);
104
+ });
105
+
106
+ test("procedural_tooling_failed escalates terminally before any retry is eligible", () => {
107
+ const step = resolveCorrectionStep(STATUS, evidence("procedural_tooling_failed"));
108
+
109
+ assert.equal(step.kind, "terminal-escalation");
110
+ assert.equal(step.retryEligible, false);
111
+ assert.equal(step.transactionOpen, false);
112
+ assert.equal(step.unlocksTargetedValidation, false);
113
+ assert.equal(typeof step.escalation, "string");
114
+ assert.match(step.escalation as string, /\S/);
115
+ // No further actor may run after a terminal escalation.
116
+ assert.equal(step.launchesReviewer, false);
117
+ assert.equal(step.launchesValidator, false);
118
+ assert.equal(step.launchesCorrection, false);
119
+ });
120
+
121
+ test("every closed outcome resolves to exactly one distinct step kind", () => {
122
+ const kinds = CORRECTION_OUTCOMES.map((outcome) => resolveCorrectionStep(STATUS, evidence(outcome)).kind);
123
+
124
+ assert.equal(new Set(kinds).size, kinds.length, "each outcome must map to its own step kind");
125
+ assert.deepEqual(kinds, ["run-targeted-validation", "recapture-required", "terminal-escalation"]);
126
+ });
127
+
128
+ test("a second capture with a distinct identity and an unchanged prior record is accepted", () => {
129
+ const prior = evidence("verification_failed");
130
+ const next = evidence("passed", { evidenceIdentity: "sha256:" + "b".repeat(64), recordDigest: "sha256:" + "b".repeat(64) });
131
+
132
+ assert.doesNotThrow(() => assertDistinctCorrectionEvidence({ prior, next, priorStillResolvable: true, priorRecordDigestNow: prior.recordDigest }));
133
+ });
134
+
135
+ test("a second capture reusing the prior evidence identity is correction-evidence-replaced", () => {
136
+ const prior = evidence("verification_failed");
137
+ const next = evidence("passed");
138
+
139
+ assert.throws(
140
+ () => assertDistinctCorrectionEvidence({ prior, next, priorStillResolvable: true, priorRecordDigestNow: prior.recordDigest }),
141
+ (error: unknown) => error instanceof CorrectionEvidenceReplacedError && /correction-evidence-replaced/.test((error as Error).message),
142
+ );
143
+ });
144
+
145
+ test("a prior evidence record whose bytes changed is correction-evidence-replaced", () => {
146
+ const prior = evidence("verification_failed");
147
+ const next = evidence("passed", { evidenceIdentity: "sha256:" + "c".repeat(64), recordDigest: "sha256:" + "c".repeat(64) });
148
+
149
+ assert.throws(
150
+ () => assertDistinctCorrectionEvidence({ prior, next, priorStillResolvable: true, priorRecordDigestNow: "sha256:" + "d".repeat(64) }),
151
+ CorrectionEvidenceReplacedError,
152
+ );
153
+ });
154
+
155
+ test("a prior evidence record that no longer resolves is correction-evidence-replaced", () => {
156
+ const prior = evidence("verification_failed");
157
+ const next = evidence("passed", { evidenceIdentity: "sha256:" + "e".repeat(64), recordDigest: "sha256:" + "e".repeat(64) });
158
+
159
+ assert.throws(
160
+ () => assertDistinctCorrectionEvidence({ prior, next, priorStillResolvable: false, priorRecordDigestNow: prior.recordDigest }),
161
+ CorrectionEvidenceReplacedError,
162
+ );
163
+ });
164
+
165
+ test("the step machine is pure: repeated calls on frozen input return equal results and mutate nothing", () => {
166
+ const input = Object.freeze(evidence("verification_failed"));
167
+ const snapshot = JSON.stringify({ status: STATUS, input });
168
+
169
+ const first = resolveCorrectionStep(STATUS, input);
170
+ const second = resolveCorrectionStep(STATUS, input);
171
+
172
+ assert.deepEqual(first, second);
173
+ assert.equal(JSON.stringify({ status: STATUS, input }), snapshot, "inputs must not be mutated");
174
+ });