gentle-pi 2.1.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -79
- package/assets/agents/gentle-ai-worker.md +7 -3
- package/assets/agents/jd-fix-agent.md +1 -1
- package/assets/agents/jd-judge-a.md +3 -1
- package/assets/agents/jd-judge-b.md +3 -1
- package/assets/agents/review-readability.md +4 -3
- package/assets/agents/review-reliability.md +4 -3
- package/assets/agents/review-resilience.md +4 -3
- package/assets/agents/review-risk.md +4 -3
- package/assets/agents/sdd-apply.md +11 -4
- package/assets/agents/sdd-archive.md +6 -1
- package/assets/agents/sdd-design.md +6 -1
- package/assets/agents/sdd-explore.md +6 -2
- package/assets/agents/sdd-init.md +10 -2
- package/assets/agents/sdd-onboard.md +6 -1
- package/assets/agents/sdd-proposal.md +8 -1
- package/assets/agents/sdd-research.md +54 -0
- package/assets/agents/sdd-spec.md +6 -1
- package/assets/agents/sdd-status.md +10 -5
- package/assets/agents/sdd-sync.md +6 -1
- package/assets/agents/sdd-tasks.md +8 -4
- package/assets/agents/sdd-verify.md +27 -2
- package/assets/chains/4r-review.chain.md +2 -0
- package/assets/chains/sdd-full.chain.md +2 -2
- package/assets/chains/sdd-plan.chain.md +1 -1
- package/assets/chains/sdd-verify.chain.md +2 -2
- package/assets/orchestrator-delegation.md +145 -160
- package/assets/orchestrator-memory.md +2 -0
- package/assets/orchestrator.md +25 -48
- package/assets/sdd-orchestrator-workflow.md +163 -25
- package/assets/support/sdd-status-contract.md +24 -6
- package/contracts/review-integration/v1/fixtures/consent.fixture.json +3 -3
- package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +19 -28
- package/contracts/review-integration/v1/fixtures/start.fixture.json +1 -10
- package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +12 -21
- package/contracts/review-integration/v1/schemas/correction-plan-request.schema.json +49 -0
- package/contracts/review-integration/v1/schemas/operation.schema.json +76 -0
- package/contracts/review-integration/v1/schemas/repair.schema.json +39 -0
- package/contracts/review-integration/v1/schemas/status-v2.schema.json +4 -2
- package/contracts/review-integration/v1/schemas/status.schema.json +4 -2
- package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
- package/contracts/review-integration/v2/fixtures/consent.fixture.json +1 -1
- package/contracts/review-integration/v2/fixtures/start.fixture.json +1 -10
- package/contracts/review-integration/v2/fixtures/status.fixture.json +1 -10
- package/contracts/review-integration/v2/schemas/failure.schema.json +5 -1
- package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
- package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
- package/contracts/review-integration/v2/schemas/operation.schema.json +6 -1
- package/contracts/review-integration/v2/schemas/repair.schema.json +4 -2
- package/contracts/review-integration/v2/schemas/start.schema.json +5 -2
- package/contracts/review-integration/v2/schemas/status.schema.json +4 -2
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +30 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/README.md +12 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/manifest.json +65 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/lens.schema.json +16 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/refuter.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/targeted-validator.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/lens.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/refuter.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/targeted-validator.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-capabilities.baseline.json +15 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-roles.baseline.json +42 -0
- package/docs/native-authority-architecture.md +14 -16
- package/docs/review-integration.md +27 -353
- package/extensions/ask-user-choice.ts +151 -0
- package/extensions/gentle-ai.ts +2710 -3211
- package/extensions/quiet-tools.ts +515 -32
- package/extensions/sdd-init.ts +21 -12
- package/extensions/skill-registry.ts +10 -2
- package/extensions/startup-banner.ts +10 -4
- package/lib/gentle-ai-binary.ts +173 -2
- package/lib/gentle-ai-renderer.ts +70 -0
- package/lib/model-routing-authority.ts +133 -0
- package/lib/native-review-cli.ts +676 -781
- package/lib/opaque-pi-reviewer-adapter.ts +284 -0
- package/lib/provider-contract-bundle.ts +704 -0
- package/lib/review-candidate-view.ts +836 -118
- package/lib/review-compact-contract.ts +59 -248
- package/lib/review-host-relay.ts +578 -0
- package/lib/review-integration-v2.ts +1254 -221
- package/lib/review-last-event-controller.ts +35 -0
- package/lib/review-relay-contract.ts +16 -0
- package/lib/sdd-preflight.ts +230 -67
- package/lib/sdd-status.ts +66 -111
- package/lib/terminal-theme.ts +1 -1
- package/package.json +83 -79
- package/runtime/gentle-ai-binary.mjs +174 -3
- package/runtime/native-review-cli.mjs +610 -715
- package/runtime/review-integration-v2.mjs +1223 -190
- package/runtime/review-relay-contract.mjs +17 -0
- package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +5 -5
- package/scripts/check-provider-contract.mjs +138 -0
- package/scripts/gentle-ai-installer.mjs +85 -22
- package/scripts/maintainer/provider-relay-matrix.mjs +403 -0
- package/scripts/mirror-provider-contract.mjs +143 -0
- package/scripts/test-packed-runner.mjs +20 -9
- package/scripts/verify-package-files.mjs +119 -43
- package/skills/_shared/review-ledger-contract.md +11 -19
- package/skills/chained-pr/SKILL.md +3 -0
- package/skills/cognitive-doc-design/SKILL.md +1 -1
- package/skills/comment-writer/SKILL.md +1 -1
- package/skills/gentle-ai/SKILL.md +7 -74
- package/skills/issue-creation/SKILL.md +94 -168
- package/skills/judgment-day/SKILL.md +9 -5
- package/skills/judgment-day/references/prompts-and-formats.md +2 -0
- package/skills/rdd-defect-workflow/SKILL.md +54 -0
- package/skills/release/SKILL.md +3 -3
- package/skills/skill-registry/SKILL.md +1 -1
- package/skills/work-unit-commits/SKILL.md +3 -1
- package/tests/artifact-language.test.ts +24 -11
- package/tests/ask-user-choice.test.ts +264 -0
- package/tests/background-subagents.test.ts +771 -0
- package/tests/codegraph-tools.test.ts +3 -3
- package/tests/crosslane/cross-lane.mjs +16 -0
- package/tests/delegated-key-learnings-contract.test.ts +240 -0
- package/tests/devbinary/native-review-parity.devtest.ts +167 -250
- package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
- package/tests/fixtures/devbinary/capabilities-v2.1.derived.json +331 -0
- package/tests/fixtures/devbinary/capabilities-v2.2.captured.json +340 -0
- package/tests/fixtures/devbinary/consent-v3.captured.json +37 -0
- package/tests/fixtures/devbinary/failure-v2-capture-evidence.captured.json +16 -0
- package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
- package/tests/fixtures/devbinary/result-artifact-v2-path.captured.json +12 -0
- package/tests/fixtures/devbinary/result-artifact-v2.captured.json +12 -0
- package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
- package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
- package/tests/fixtures/devbinary/start-v3-consent-declined.captured.json +19 -0
- package/tests/fixtures/devbinary/start-v3-consent-granted.captured.json +109 -0
- package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
- package/tests/fixtures/devbinary/status-v5-capture-result-submission.captured.json +184 -0
- package/tests/fixtures/devbinary/status-v5-repository-context.captured.json +138 -0
- package/tests/fixtures/devbinary/status-v5.captured.json +88 -0
- package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/README.md +12 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/manifest.json +65 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/targeted-validator.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/targeted-validator.json +1 -0
- package/tests/gentle-ai-binary.test.ts +83 -13
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +195 -0
- package/tests/gentle-ai-dev-binary.test.ts +159 -0
- package/tests/gentle-ai-installer.test.ts +138 -48
- package/tests/gentle-ai.test.ts +820 -131
- package/tests/gentle-theme.test.ts +133 -0
- package/tests/maintainer/provider-relay.maintest.ts +601 -0
- package/tests/model-routing-authority.test.ts +257 -0
- package/tests/native-review-capability-contract.test.ts +75 -2
- package/tests/native-review-cli.test.ts +588 -908
- package/tests/native-review-consent.test.ts +283 -38
- package/tests/native-review-parity-runtime.test.ts +108 -350
- package/tests/native-review-parity.test.ts +684 -732
- package/tests/native-sdd-attempt-authority.test.ts +235 -0
- package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
- package/tests/orchestrator-budget.test.ts +158 -89
- package/tests/orchestrator-rdd-ownership.test.ts +103 -0
- package/tests/package-manifest.test.ts +128 -100
- package/tests/provider-contract-bundle.test.ts +385 -0
- package/tests/provider-contract-mirror.test.ts +206 -0
- package/tests/provider-defect-handoff.test.ts +252 -0
- package/tests/quiet-tool-rendering.test.ts +1055 -28
- package/tests/review-actor-tool-deny.test.ts +12 -13
- package/tests/review-authority-recovery-docs.test.ts +1 -2
- package/tests/review-candidate-view.test.ts +898 -12
- package/tests/review-compact-contract.test.ts +29 -122
- package/tests/review-controller-lock-status.test.ts +2 -2
- package/tests/review-controller-native-recovery.test.ts +366 -857
- package/tests/review-controller-native-routing.test.ts +1253 -4199
- package/tests/review-controller-retired-ops.test.ts +1 -1
- package/tests/review-controller-workspace-root.test.ts +236 -70
- package/tests/review-controller.test.ts +26 -816
- package/tests/review-corrected-finalize-binding.test.ts +134 -0
- package/tests/review-dispatch-hydration-gap.test.ts +145 -0
- package/tests/review-gate.test.ts +0 -45
- package/tests/review-host-relay-restart-parity.test.ts +360 -0
- package/tests/review-host-relay-routing.test.ts +352 -0
- package/tests/review-host-relay.test.ts +754 -0
- package/tests/review-integration-v2-forward.test.ts +654 -0
- package/tests/review-integration-v2.test.ts +317 -129
- package/tests/review-last-event-closure.test.ts +408 -0
- package/tests/review-ledger-contract.test.ts +106 -60
- package/tests/review-recovered-lineage-routing.test.ts +199 -0
- package/tests/review-relay-transport-agent.test.ts +322 -0
- package/tests/review-snapshot.test.ts +3 -2
- package/tests/runtime-harness.mjs +573 -180
- package/tests/sdd-agent-tools.test.ts +53 -32
- package/tests/sdd-preflight.test.ts +81 -15
- package/tests/sdd-status.test.ts +109 -110
- package/tests/skill-collision-prefixes.test.ts +6 -8
- package/tests/skill-registry.test.ts +50 -1
- package/tests/verify-package-files.test.ts +62 -0
- package/tests/writer-edit-surface-scope.test.ts +230 -0
- package/themes/Gentleman-Cute.json +94 -0
- package/themes/Gentleman-Sexy.json +92 -0
- package/assets/agents/review-refuter.md +0 -40
- package/assets/agents/review-validator.md +0 -23
- package/lib/git-commit-transaction.ts +0 -801
- package/lib/native-review-remediation.ts +0 -49
- package/lib/review-compact.ts +0 -947
- package/lib/review-refuter-adapter.ts +0 -129
- package/lib/review-runtime-contract.ts +0 -68
- package/prompts/gcl.md +0 -54
- package/prompts/gis.md +0 -25
- package/prompts/gpr.md +0 -41
- package/prompts/gwr.md +0 -31
- package/runtime/git-commit-transaction.mjs +0 -802
- package/scripts/run-git-commit-transaction.mjs +0 -35
- package/tests/fixtures/native-review-cli/v2.1.2/bind-sdd.json +0 -25
- package/tests/fixtures/native-review-cli/v2.1.2/finalize.json +0 -8
- package/tests/fixtures/native-review-cli/v2.1.2/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.2/sdd-status.json +0 -200
- package/tests/fixtures/native-review-cli/v2.1.2/start.json +0 -12
- package/tests/fixtures/native-review-cli/v2.1.2/validate-allow.json +0 -24
- package/tests/fixtures/native-review-cli/v2.1.2/validate-deny-empty-context.json +0 -20
- package/tests/fixtures/native-review-cli/v2.1.2/validate-deny.json +0 -28
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
- package/tests/git-commit-transaction.test.ts +0 -302
- package/tests/review-compact.test.ts +0 -243
- package/tests/review-refuter-adapter.test.ts +0 -89
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
decodeReviewConsentV2,
|
|
12
12
|
decodeReviewFailureV2,
|
|
13
13
|
decodeReviewNextTransitionV3,
|
|
14
|
-
decodeReviewOperationV2,
|
|
15
14
|
decodeReviewProjectionV1,
|
|
16
15
|
decodeReviewRepairV2,
|
|
17
16
|
decodeReviewStartV3,
|
|
@@ -19,7 +18,9 @@ import {
|
|
|
19
18
|
} from "../lib/review-integration-v2.ts";
|
|
20
19
|
|
|
21
20
|
const fixtureRoot = join(process.cwd(), "contracts", "review-integration", "v2", "fixtures");
|
|
21
|
+
const devFixtureRoot = join(process.cwd(), "tests", "fixtures", "devbinary");
|
|
22
22
|
const fixture = <T = unknown>(name: string): T => JSON.parse(readFileSync(join(fixtureRoot, name), "utf8")) as T;
|
|
23
|
+
const devFixture = <T = unknown>(name: string): T => JSON.parse(readFileSync(join(devFixtureRoot, name), "utf8")) as T;
|
|
23
24
|
const executableDigest = "dcc846103b16d365eaeeb9d7f289c23fc4f2897f23def1cb3fe7f05557b64705";
|
|
24
25
|
const digest = `sha256:${"a".repeat(64)}`;
|
|
25
26
|
|
|
@@ -56,10 +57,9 @@ function assertAdditionalProperty(decoder: Decoder, source: JsonObject, path: re
|
|
|
56
57
|
assert.throws(() => decoder(candidate), /not allowed/, path.length === 0 ? "top-level" : path.join("."));
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
test("
|
|
60
|
+
test("current review integration fixtures decode", () => {
|
|
60
61
|
assert.equal(decodeReviewCapabilitiesV2(fixture("capabilities.fixture.json"), executableDigest).contract, REVIEW_INTEGRATION_CONTRACT);
|
|
61
62
|
assert.equal(decodeReviewStartV3(fixture("start.fixture.json")).riskLevel, "high");
|
|
62
|
-
assert.equal(decodeReviewStatusV3(fixture("status.fixture.json")).contract, REVIEW_INTEGRATION_CONTRACT);
|
|
63
63
|
assert.equal(decodeReviewConsentV2(fixture("consent.fixture.json")).action, "consent_required");
|
|
64
64
|
});
|
|
65
65
|
|
|
@@ -107,10 +107,52 @@ test("START enforces required, exact, and enum-bounded payloads", () => {
|
|
|
107
107
|
}
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
test("
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
test("start/v3 transports the published selected_lenses wire contract without Pi-owned risk policy", () => {
|
|
111
|
+
const captured = devFixture<JsonObject>("start-v3-zero-lens-closed.captured.json");
|
|
112
|
+
const decoded = decodeReviewStartV3(captured);
|
|
113
|
+
assert.equal(decoded.action, "closed");
|
|
114
|
+
assert.equal(decoded.lensesRequired, false);
|
|
115
|
+
assert.deepEqual(decoded.selectedLenses, []);
|
|
116
|
+
|
|
117
|
+
for (const riskLevel of ["medium", "high"] as const) {
|
|
118
|
+
const noLens = clone(captured);
|
|
119
|
+
noLens.risk_level = riskLevel;
|
|
120
|
+
assert.doesNotThrow(() => decodeReviewStartV3(noLens));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const nullLenses = clone(captured);
|
|
124
|
+
nullLenses.selected_lenses = null;
|
|
125
|
+
assert.throws(() => decodeReviewStartV3(nullLenses), /selected_lenses.*invalid length/);
|
|
126
|
+
|
|
127
|
+
const unknownLens = clone(captured);
|
|
128
|
+
unknownLens.selected_lenses = ["review-future"];
|
|
129
|
+
assert.throws(() => decodeReviewStartV3(unknownLens), /selected_lenses.*unsupported/);
|
|
130
|
+
|
|
131
|
+
const tooManyLenses = clone(captured);
|
|
132
|
+
tooManyLenses.selected_lenses = ["review-risk", "review-resilience", "review-readability", "review-reliability", "review-future"];
|
|
133
|
+
assert.throws(() => decodeReviewStartV3(tooManyLenses), /selected_lenses.*invalid length/);
|
|
134
|
+
|
|
135
|
+
const duplicateLenses = clone(captured);
|
|
136
|
+
duplicateLenses.selected_lenses = ["review-risk", "review-risk"];
|
|
137
|
+
assert.throws(() => decodeReviewStartV3(duplicateLenses), /selected_lenses.*duplicates/);
|
|
138
|
+
|
|
139
|
+
const wrongSchema = clone(captured);
|
|
140
|
+
wrongSchema.schema = "gentle-ai.review-integration.start/v2";
|
|
141
|
+
assert.throws(() => decodeReviewStartV3(wrongSchema), /schema/);
|
|
142
|
+
|
|
143
|
+
const invalidAction = clone(captured);
|
|
144
|
+
invalidAction.action = "dispatch-locally";
|
|
145
|
+
assert.throws(() => decodeReviewStartV3(invalidAction), /action.*unsupported/);
|
|
146
|
+
|
|
147
|
+
const invalidLensesRequired = clone(captured);
|
|
148
|
+
invalidLensesRequired.lenses_required = "false";
|
|
149
|
+
assert.throws(() => decodeReviewStartV3(invalidLensesRequired), /lenses_required/);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("historical status fixture with a receipt is rejected after receipt retirement", () => {
|
|
153
|
+
// This archived fixture is pinned for schema-rejection coverage only. Its
|
|
154
|
+
// bytes retain the retired receipt surface and must never regain authority.
|
|
155
|
+
assert.throws(() => decodeReviewStatusV3(fixture("status.fixture.json")), /receipt.*not allowed/);
|
|
114
156
|
});
|
|
115
157
|
|
|
116
158
|
test("projection enforces every required property and rejects additional keys", () => {
|
|
@@ -123,7 +165,7 @@ test("failure enforces exact keys, enums, and identifiers", () => {
|
|
|
123
165
|
const source: JsonObject = {
|
|
124
166
|
schema: "gentle-ai.review-integration.failure/v2",
|
|
125
167
|
contract: REVIEW_INTEGRATION_CONTRACT,
|
|
126
|
-
operation: "review.
|
|
168
|
+
operation: "review.capture-result",
|
|
127
169
|
phase: "pre_native",
|
|
128
170
|
code: "gate_scope_changed",
|
|
129
171
|
message: "the target scope changed since START",
|
|
@@ -142,28 +184,6 @@ test("failure enforces exact keys, enums, and identifiers", () => {
|
|
|
142
184
|
assert.throws(() => decodeReviewFailureV2(badCode), /code/);
|
|
143
185
|
});
|
|
144
186
|
|
|
145
|
-
function finalizeEnvelope(): JsonObject {
|
|
146
|
-
return {
|
|
147
|
-
schema: "gentle-ai.review-integration.operation/v2",
|
|
148
|
-
contract: REVIEW_INTEGRATION_CONTRACT,
|
|
149
|
-
operation: "review.finalize",
|
|
150
|
-
result: {
|
|
151
|
-
operation: "review/finalize",
|
|
152
|
-
lineage_id: "review-fixture",
|
|
153
|
-
state: "approved",
|
|
154
|
-
action: "publish",
|
|
155
|
-
store_revision: digest,
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
test("operation envelopes strictly bind the outer operation to one exact result variant", () => {
|
|
161
|
-
const envelope = finalizeEnvelope();
|
|
162
|
-
assert.equal(decodeReviewOperationV2(envelope).operation, "review.finalize");
|
|
163
|
-
assertAdditionalProperty(decodeReviewOperationV2, envelope);
|
|
164
|
-
assertNestedRequired(decodeReviewOperationV2, envelope, ["result"], ["operation", "lineage_id", "state", "action", "store_revision"]);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
187
|
test("net-new decoders: consent, next-transition, and artifact-subject reject malformed payloads", () => {
|
|
168
188
|
const consentSource = fixture<JsonObject>("consent.fixture.json");
|
|
169
189
|
assertRequired(decodeReviewConsentV2, consentSource, ["schema", "contract", "operation", "action", "blocking", "target_identity", "projection", "risk_level", "changed_files", "changed_lines", "headline", "reason", "value", "risk_evidence", "choices", "off_path"]);
|
|
@@ -245,6 +265,50 @@ test("capabilities enforce the exact mandatory feature set while accepting a sup
|
|
|
245
265
|
const extraOperation = clone(source);
|
|
246
266
|
(extraOperation.operations as string[]).push("review.future_operation");
|
|
247
267
|
assert.doesNotThrow(() => decode(extraOperation));
|
|
268
|
+
|
|
269
|
+
// mandatory features must stay an exact match: a known-but-optional feature
|
|
270
|
+
// name added to the mandatory list must still be rejected. This is the
|
|
271
|
+
// contract boundary (unknown_mandatory: "reject") and must not regress when
|
|
272
|
+
// gates/projections become additive-tolerant below.
|
|
273
|
+
const extraMandatoryUnknownAddition = clone(source);
|
|
274
|
+
((extraMandatoryUnknownAddition.features as JsonObject).mandatory as JsonObject[]).push({ name: "bounded_process_waits", supported: true, requires: [] });
|
|
275
|
+
assert.throws(() => decode(extraMandatoryUnknownAddition), /mandatory/);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("capabilities gates and projections accept an additive superset beyond the required floor", () => {
|
|
279
|
+
const source = fixture<JsonObject>("capabilities.fixture.json");
|
|
280
|
+
const decode = (value: unknown) => decodeReviewCapabilitiesV2(value, executableDigest);
|
|
281
|
+
|
|
282
|
+
const extraGate = clone(source);
|
|
283
|
+
(extraGate.gates as string[]).push("future-gate");
|
|
284
|
+
const decodedExtraGate = decode(extraGate) as { gates: ReadonlySet<string> };
|
|
285
|
+
assert.equal(decodedExtraGate.gates.has("future-gate"), false, "an unknown advertised gate must not leak into internal use");
|
|
286
|
+
for (const gate of ["post-apply", "pre-commit", "pre-push", "pre-pr", "release"]) {
|
|
287
|
+
assert.equal(decodedExtraGate.gates.has(gate), true, gate);
|
|
288
|
+
}
|
|
289
|
+
assert.equal(decodedExtraGate.gates.size, 5);
|
|
290
|
+
|
|
291
|
+
const extraProjection = clone(source);
|
|
292
|
+
(extraProjection.projections as string[]).push("future-projection");
|
|
293
|
+
const decodedExtraProjection = decode(extraProjection) as { projections: ReadonlySet<string> };
|
|
294
|
+
assert.equal(decodedExtraProjection.projections.has("future-projection"), false, "an unknown advertised projection must not leak into internal use");
|
|
295
|
+
for (const projection of ["staged", "workspace"]) {
|
|
296
|
+
assert.equal(decodedExtraProjection.projections.has(projection), true, projection);
|
|
297
|
+
}
|
|
298
|
+
assert.equal(decodedExtraProjection.projections.size, 2);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("capabilities gates and projections still enforce the required floor", () => {
|
|
302
|
+
const source = fixture<JsonObject>("capabilities.fixture.json");
|
|
303
|
+
const decode = (value: unknown) => decodeReviewCapabilitiesV2(value, executableDigest);
|
|
304
|
+
|
|
305
|
+
const missingGate = clone(source);
|
|
306
|
+
missingGate.gates = (missingGate.gates as string[]).filter((gate) => gate !== "release");
|
|
307
|
+
assert.throws(() => decode(missingGate), /gates/);
|
|
308
|
+
|
|
309
|
+
const missingProjection = clone(source);
|
|
310
|
+
missingProjection.projections = (missingProjection.projections as string[]).filter((projection) => projection !== "workspace");
|
|
311
|
+
assert.throws(() => decode(missingProjection), /projections/);
|
|
248
312
|
});
|
|
249
313
|
|
|
250
314
|
test("START independently binds base/candidate tree and the target-mode overlay pair", () => {
|
|
@@ -271,97 +335,11 @@ test("START independently binds base/candidate tree and the target-mode overlay
|
|
|
271
335
|
assert.throws(() => decodeReviewStartV3(manifestWithoutTrees), /changed_path_manifest/);
|
|
272
336
|
});
|
|
273
337
|
|
|
274
|
-
test("status enforces authority/frozen/receipt conditionals and decodes the required repair field", () => {
|
|
275
|
-
const current = fixture<JsonObject>("status.fixture.json");
|
|
276
|
-
const decoded = decodeReviewStatusV3(current);
|
|
277
|
-
assert.equal(decoded.repair.status, "unsupported");
|
|
278
|
-
assert.equal(decoded.repair.counts.eligibleCandidates, 0);
|
|
279
|
-
|
|
280
|
-
const missingAuthority = clone(current);
|
|
281
|
-
delete missingAuthority.authority;
|
|
282
|
-
assert.throws(() => decodeReviewStatusV3(missingAuthority), /requires authority/);
|
|
283
|
-
|
|
284
|
-
const compactMissingFrozen = clone(current);
|
|
285
|
-
delete compactMissingFrozen.frozen;
|
|
286
|
-
assert.throws(() => decodeReviewStatusV3(compactMissingFrozen), /requires frozen/);
|
|
287
|
-
|
|
288
|
-
const legacyWithFrozen = clone(current);
|
|
289
|
-
(legacyWithFrozen.authority as JsonObject).version = "legacy-v1";
|
|
290
|
-
(legacyWithFrozen.receipt as JsonObject).status = "expected_missing";
|
|
291
|
-
assert.throws(() => decodeReviewStatusV3(legacyWithFrozen), /legacy status cannot expose frozen/);
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test("status validation_request and retry_final_verification require their exact authority preconditions", () => {
|
|
295
|
-
const current = fixture<JsonObject>("status.fixture.json");
|
|
296
|
-
const withValidationRequest = clone(current);
|
|
297
|
-
withValidationRequest.validation_request = {
|
|
298
|
-
schema: "gentle-ai.review-targeted-validation-request/v1",
|
|
299
|
-
request_hash: digest,
|
|
300
|
-
lineage_id: "review-status-fixture",
|
|
301
|
-
expected_revision: digest,
|
|
302
|
-
target_identity: (current.target_identity as string),
|
|
303
|
-
fix_finding_ids: ["finding-1"],
|
|
304
|
-
projection: "workspace",
|
|
305
|
-
correction_candidate_tree: "a".repeat(40),
|
|
306
|
-
correction_target_identity: digest,
|
|
307
|
-
correction_paths: ["tracked.txt"],
|
|
308
|
-
correction_paths_digest: digest,
|
|
309
|
-
};
|
|
310
|
-
// authority.state is "reviewing" in the fixture, not correction_required.
|
|
311
|
-
assert.throws(() => decodeReviewStatusV3(withValidationRequest), /correction_required/);
|
|
312
|
-
|
|
313
|
-
const retryWithoutIncident = clone(current);
|
|
314
|
-
retryWithoutIncident.action = "retry_final_verification";
|
|
315
|
-
retryWithoutIncident.action_disposition = "final_verification_retry";
|
|
316
|
-
(retryWithoutIncident.authority as JsonObject).state = "escalated";
|
|
317
|
-
assert.throws(() => decodeReviewStatusV3(retryWithoutIncident), /requires final_verification_retry/);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
test("operation envelopes decode validate, bind_sdd, and retry_final_verification result variants", () => {
|
|
321
|
-
const validateEnvelope: JsonObject = {
|
|
322
|
-
schema: "gentle-ai.review-integration.operation/v2",
|
|
323
|
-
contract: REVIEW_INTEGRATION_CONTRACT,
|
|
324
|
-
operation: "review.validate",
|
|
325
|
-
result: { schema: "gentle-ai.review-gate-result/v1", result: "allow", allowed: true, action: "publish", reason: "receipt matches", context: { gate: "pre-pr" } },
|
|
326
|
-
};
|
|
327
|
-
assert.equal(decodeReviewOperationV2(validateEnvelope).operation, "review.validate");
|
|
328
|
-
|
|
329
|
-
const bindSddEnvelope: JsonObject = {
|
|
330
|
-
schema: "gentle-ai.review-integration.operation/v2",
|
|
331
|
-
contract: REVIEW_INTEGRATION_CONTRACT,
|
|
332
|
-
operation: "review.bind_sdd",
|
|
333
|
-
result: { schema: "gentle-ai.sdd-review-binding/v1", revision: digest, change: "migrate-review-integration-v2", lineage: "review-fixture", authority_revision: digest, receipt_hash: digest, gate_context: { gate: "post-apply" } },
|
|
334
|
-
};
|
|
335
|
-
assert.equal(decodeReviewOperationV2(bindSddEnvelope).operation, "review.bind_sdd");
|
|
336
|
-
|
|
337
|
-
const retryEnvelope: JsonObject = {
|
|
338
|
-
schema: "gentle-ai.review-integration.operation/v2",
|
|
339
|
-
contract: REVIEW_INTEGRATION_CONTRACT,
|
|
340
|
-
operation: "review.retry_final_verification",
|
|
341
|
-
result: {
|
|
342
|
-
operation: "review.retry_final_verification",
|
|
343
|
-
predecessor_lineage_id: "review-predecessor",
|
|
344
|
-
predecessor_revision: digest,
|
|
345
|
-
lineage_id: "review-successor",
|
|
346
|
-
state: "validating",
|
|
347
|
-
store_revision: digest,
|
|
348
|
-
target_identity: digest,
|
|
349
|
-
incident_digest: digest,
|
|
350
|
-
recovery_disposition: "final_verification_retry",
|
|
351
|
-
},
|
|
352
|
-
};
|
|
353
|
-
assert.equal(decodeReviewOperationV2(retryEnvelope).operation, "review.retry_final_verification");
|
|
354
|
-
|
|
355
|
-
const crossedResult = clone(validateEnvelope);
|
|
356
|
-
crossedResult.result = bindSddEnvelope.result;
|
|
357
|
-
assert.throws(() => decodeReviewOperationV2(crossedResult), /does not match|not allowed|required/);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
338
|
test("failure context accepts scope_change or binding_revision but rejects both or neither", () => {
|
|
361
339
|
const base: JsonObject = {
|
|
362
340
|
schema: "gentle-ai.review-integration.failure/v2",
|
|
363
341
|
contract: REVIEW_INTEGRATION_CONTRACT,
|
|
364
|
-
operation: "review.
|
|
342
|
+
operation: "review.capture-result",
|
|
365
343
|
phase: "pre_native",
|
|
366
344
|
code: "gate_scope_changed",
|
|
367
345
|
message: "the target scope changed since START",
|
|
@@ -427,6 +405,44 @@ test("next_transition decodes an execute variant and rejects a stop that carries
|
|
|
427
405
|
assert.throws(() => decodeReviewNextTransitionV3(stopWithExecute), /stop cannot carry/);
|
|
428
406
|
});
|
|
429
407
|
|
|
408
|
+
function approvedAcknowledgementTransition(): JsonObject {
|
|
409
|
+
return {
|
|
410
|
+
kind: "execute",
|
|
411
|
+
reason_code: "approved_acknowledgement_required",
|
|
412
|
+
execute: {
|
|
413
|
+
operation: "review.acknowledge-approved",
|
|
414
|
+
command: "gentle-ai review acknowledge-approved --provider-vector",
|
|
415
|
+
arguments: [
|
|
416
|
+
{ name: "cwd", value: "/provider/repository", token: "--cwd=/provider/repository" },
|
|
417
|
+
{ name: "lineage", value: "review-fixture", token: "--lineage=review-fixture" },
|
|
418
|
+
{ name: "target", value: digest, token: `--target=${digest}` },
|
|
419
|
+
{ name: "expected-revision", value: digest, token: `--expected-revision=${digest}` },
|
|
420
|
+
{ name: "token", value: "provider-issued-once", token: "--token=provider-issued-once" },
|
|
421
|
+
],
|
|
422
|
+
preconditions: [{ name: "state", value: "approved", token: "--state=approved" }],
|
|
423
|
+
binding: { lineage_id: "review-fixture", target_identity: digest, revision: digest },
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
test("acknowledgement execute rejects decoy bindings, closed-vector drift, and malformed approval", () => {
|
|
429
|
+
const valid = approvedAcknowledgementTransition();
|
|
430
|
+
assert.equal(decodeReviewNextTransitionV3(valid).execute?.operation, "review.acknowledge-approved");
|
|
431
|
+
const cases: Array<[string, (candidate: JsonObject) => void]> = [
|
|
432
|
+
["decoy target", (candidate) => { const argument = ((candidate.execute as JsonObject).arguments as JsonObject[])[2]!; argument.value = `sha256:${"b".repeat(64)}`; argument.token = `--target=${argument.value}`; }],
|
|
433
|
+
["mismatched expected revision", (candidate) => { const argument = ((candidate.execute as JsonObject).arguments as JsonObject[])[3]!; argument.value = `sha256:${"c".repeat(64)}`; argument.token = `--expected-revision=${argument.value}`; }],
|
|
434
|
+
["wrong argument order", (candidate) => { ((candidate.execute as JsonObject).arguments as JsonObject[]).reverse(); }],
|
|
435
|
+
["wrong argument name", (candidate) => { const argument = ((candidate.execute as JsonObject).arguments as JsonObject[])[0]!; argument.name = "repository"; argument.token = `--repository=${argument.value}`; }],
|
|
436
|
+
["malformed approved precondition", (candidate) => { ((candidate.execute as JsonObject).preconditions as JsonObject[])[0]!.value = "burned"; }],
|
|
437
|
+
["token value mismatch", (candidate) => { ((candidate.execute as JsonObject).arguments as JsonObject[])[4]!.token = "--token=other"; }],
|
|
438
|
+
];
|
|
439
|
+
for (const [name, mutate] of cases) {
|
|
440
|
+
const candidate = clone(valid);
|
|
441
|
+
mutate(candidate);
|
|
442
|
+
assert.throws(() => decodeReviewNextTransitionV3(candidate), /acknowledgement|arguments|preconditions|binding/, name);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
430
446
|
test("repair decodes a committed execute result and rejects a non-eligible preflight carrying provider_inputs", () => {
|
|
431
447
|
const executed: JsonObject = {
|
|
432
448
|
schema: "gentle-ai.review-integration.repair/v2",
|
|
@@ -515,10 +531,10 @@ test("repair eligible preflight with wrong required_inputs order is rejected", (
|
|
|
515
531
|
test("next_transition.execute accepts the optional selector_arguments and artifacts the schema declares", () => {
|
|
516
532
|
const base = {
|
|
517
533
|
kind: "execute" as const,
|
|
518
|
-
reason_code: "
|
|
534
|
+
reason_code: "fresh_target_ready",
|
|
519
535
|
execute: {
|
|
520
|
-
operation: "review.
|
|
521
|
-
command: "gentle-ai review
|
|
536
|
+
operation: "review.start",
|
|
537
|
+
command: "gentle-ai review start --lineage=review-96f29cbd865e77a9",
|
|
522
538
|
arguments: [{ name: "lineage", value: "review-96f29cbd865e77a9", token: "--lineage=review-96f29cbd865e77a9" }],
|
|
523
539
|
preconditions: [{ name: "target_identity", value: "sha256:" + "9".repeat(64) }],
|
|
524
540
|
binding: { target_identity: "sha256:" + "9".repeat(64) },
|
|
@@ -526,23 +542,195 @@ test("next_transition.execute accepts the optional selector_arguments and artifa
|
|
|
526
542
|
};
|
|
527
543
|
|
|
528
544
|
assert.doesNotThrow(() => decodeReviewNextTransitionV3(base));
|
|
529
|
-
assert.doesNotThrow(() => decodeReviewNextTransitionV3({ ...base, execute: { ...base.execute, artifacts: [{ name: "
|
|
545
|
+
assert.doesNotThrow(() => decodeReviewNextTransitionV3({ ...base, execute: { ...base.execute, artifacts: [{ name: "start-record", path: "review-start.json" }] } }));
|
|
530
546
|
assert.doesNotThrow(() => decodeReviewNextTransitionV3({ ...base, execute: { ...base.execute, selector_arguments: [{ name: "projection", value: "workspace", token: "--projection=workspace" }] } }));
|
|
531
547
|
});
|
|
532
548
|
|
|
549
|
+
// gentle-pi#311 P4-roles: the two Go-owned non-lens provider role capture
|
|
550
|
+
// operations render SELF-CONTAINED vectors (binding tokens + --agent=pi
|
|
551
|
+
// --execute=true). Their schemas are pinned, and a submission descriptor on
|
|
552
|
+
// either one is a contract violation because it would hand the caller a way
|
|
553
|
+
// to author the verdict.
|
|
554
|
+
test("next_transition decodes the self-contained provider role capture vectors strictly", () => {
|
|
555
|
+
const roleInput = (name: string, captureOperation: string, schema: string, extra: Record<string, unknown> = {}) => ({
|
|
556
|
+
kind: "collect",
|
|
557
|
+
reason_code: "provider_refuter_required",
|
|
558
|
+
collect: { inputs: [{
|
|
559
|
+
name,
|
|
560
|
+
schema,
|
|
561
|
+
capture_operation: captureOperation,
|
|
562
|
+
arguments: [
|
|
563
|
+
{ name: "lineage", value: "review-fixture", token: "--lineage=review-fixture" },
|
|
564
|
+
{ name: "expected-revision", value: digest, token: `--expected-revision=${digest}` },
|
|
565
|
+
{ name: "target", value: digest, token: `--target=${digest}` },
|
|
566
|
+
{ name: "repository-context", value: `rctx1_${"c".repeat(64)}`, token: `--repository-context=rctx1_${"c".repeat(64)}` },
|
|
567
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
568
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
569
|
+
],
|
|
570
|
+
...extra,
|
|
571
|
+
}] },
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
const decodeRoleTransition = (value: unknown) => decodeReviewNextTransitionV3(value, { v5: true });
|
|
575
|
+
const refuter = roleInput("provider_refuter", "review.capture-refuter", "https://gentle-ai.dev/schema/review/refuter/v1");
|
|
576
|
+
const decodedRefuter = decodeRoleTransition(refuter);
|
|
577
|
+
assert.equal(decodedRefuter.collect?.inputs[0]?.captureOperation, "review.capture-refuter");
|
|
578
|
+
assert.equal(decodedRefuter.collect?.inputs[0]?.arguments.at(-1)?.token, "--execute=true");
|
|
579
|
+
|
|
580
|
+
assert.throws(
|
|
581
|
+
() => decodeRoleTransition(roleInput("provider_refuter", "review.capture-refuter", "https://gentle-ai.dev/schema/review/reviewer/v1")),
|
|
582
|
+
/schema must be https:\/\/gentle-ai\.dev\/schema\/review\/refuter\/v1/,
|
|
583
|
+
);
|
|
584
|
+
assert.throws(
|
|
585
|
+
() => decodeRoleTransition(roleInput("provider_refuter", "review.capture-refuter", "https://gentle-ai.dev/schema/review/refuter/v1", {
|
|
586
|
+
submission: { operation_token: "capture-refuter", argument_tokens: ["--input={{value}}"], values: [{ slot: "{{value}}", domain: "artifact-path", substitution_location: 0 }] },
|
|
587
|
+
})),
|
|
588
|
+
/submission is not allowed on the self-contained/,
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
const validator = roleInput("provider_targeted_validator", "review.capture-validation", "https://gentle-ai.dev/schema/review/validator/v1");
|
|
592
|
+
assert.throws(
|
|
593
|
+
() => decodeRoleTransition(validator),
|
|
594
|
+
/validation_request is required/,
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
assert.throws(
|
|
598
|
+
() => decodeRoleTransition(roleInput("provider_targeted_validator", "review.capture-validation", "https://gentle-ai.dev/schema/review/refuter/v1")),
|
|
599
|
+
/schema must be https:\/\/gentle-ai\.dev\/schema\/review\/validator\/v1/,
|
|
600
|
+
);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("v5 targeted-validator collect inputs carry the exact provider-owned validation request", () => {
|
|
604
|
+
const requestHash = `sha256:${"a".repeat(64)}`;
|
|
605
|
+
const expectedRevision = `sha256:${"b".repeat(64)}`;
|
|
606
|
+
const targetIdentity = `sha256:${"c".repeat(64)}`;
|
|
607
|
+
const correctionTargetIdentity = `sha256:${"d".repeat(64)}`;
|
|
608
|
+
const correctionPathsDigest = `sha256:${"e".repeat(64)}`;
|
|
609
|
+
const validationRequest = {
|
|
610
|
+
schema: "gentle-ai.review-targeted-validation-request/v1",
|
|
611
|
+
request_hash: requestHash,
|
|
612
|
+
lineage_id: "review-fixture",
|
|
613
|
+
expected_revision: expectedRevision,
|
|
614
|
+
target_identity: targetIdentity,
|
|
615
|
+
fix_finding_ids: ["R1-001"],
|
|
616
|
+
policy_content: "Gentle AI native bounded review policy.",
|
|
617
|
+
fix_findings: [{
|
|
618
|
+
id: "R1-001",
|
|
619
|
+
lens: "risk",
|
|
620
|
+
location: "selected.txt:1",
|
|
621
|
+
severity: "BLOCKER",
|
|
622
|
+
claim: "the selected relay input must be corrected before delivery",
|
|
623
|
+
proof_refs: ["selected.txt:1"],
|
|
624
|
+
evidence_class: "deterministic",
|
|
625
|
+
causal_disposition: "introduced",
|
|
626
|
+
}],
|
|
627
|
+
fix_classifications: [{
|
|
628
|
+
finding_id: "R1-001",
|
|
629
|
+
class: "deterministic",
|
|
630
|
+
causal_disposition: "introduced",
|
|
631
|
+
proof: "selected.txt:1",
|
|
632
|
+
}],
|
|
633
|
+
projection: "workspace",
|
|
634
|
+
correction_candidate_tree: "f".repeat(40),
|
|
635
|
+
correction_target_identity: correctionTargetIdentity,
|
|
636
|
+
correction_paths: ["selected.txt"],
|
|
637
|
+
correction_paths_digest: correctionPathsDigest,
|
|
638
|
+
};
|
|
639
|
+
const input = {
|
|
640
|
+
name: "provider_targeted_validator",
|
|
641
|
+
schema: "https://gentle-ai.dev/schema/review/validator/v1",
|
|
642
|
+
capture_operation: "review.capture-validation",
|
|
643
|
+
arguments: [
|
|
644
|
+
{ name: "lineage", value: "review-fixture", token: "--lineage=review-fixture" },
|
|
645
|
+
{ name: "expected-revision", value: expectedRevision, token: `--expected-revision=${expectedRevision}` },
|
|
646
|
+
{ name: "target", value: correctionTargetIdentity, token: `--target=${correctionTargetIdentity}` },
|
|
647
|
+
{ name: "repository-context", value: `rctx1_${"f".repeat(64)}`, token: `--repository-context=rctx1_${"f".repeat(64)}` },
|
|
648
|
+
{ name: "request-hash", value: requestHash, token: `--request-hash=${requestHash}` },
|
|
649
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
650
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
651
|
+
],
|
|
652
|
+
validation_request: validationRequest,
|
|
653
|
+
};
|
|
654
|
+
const transition = { kind: "collect" as const, reason_code: "targeted_validation_required", collect: { inputs: [input] } };
|
|
655
|
+
const decoded = decodeReviewNextTransitionV3(transition, { v5: true });
|
|
656
|
+
const decodedRequest = decoded.collect?.inputs[0]?.validationRequest;
|
|
657
|
+
assert.equal(decodedRequest?.requestHash, requestHash);
|
|
658
|
+
assert.equal(decodedRequest?.correctionTargetIdentity, correctionTargetIdentity);
|
|
659
|
+
assert.deepEqual(decodedRequest?.correctionPaths, ["selected.txt"]);
|
|
660
|
+
assert.equal(decodedRequest?.policyContent, validationRequest.policy_content);
|
|
661
|
+
assert.deepEqual(decodedRequest?.fixFindings, [{
|
|
662
|
+
id: "R1-001",
|
|
663
|
+
lens: "risk",
|
|
664
|
+
location: "selected.txt:1",
|
|
665
|
+
severity: "BLOCKER",
|
|
666
|
+
claim: "the selected relay input must be corrected before delivery",
|
|
667
|
+
proofRefs: ["selected.txt:1"],
|
|
668
|
+
evidenceClass: "deterministic",
|
|
669
|
+
causalDisposition: "introduced",
|
|
670
|
+
}]);
|
|
671
|
+
assert.deepEqual(decodedRequest?.fixClassifications, [{
|
|
672
|
+
findingId: "R1-001",
|
|
673
|
+
class: "deterministic",
|
|
674
|
+
causalDisposition: "introduced",
|
|
675
|
+
proof: "selected.txt:1",
|
|
676
|
+
}]);
|
|
677
|
+
assert.deepEqual(decoded.collect?.inputs[0]?.arguments, input.arguments, "provider-rendered arguments must remain unchanged");
|
|
678
|
+
|
|
679
|
+
const missingRequest = clone(transition);
|
|
680
|
+
delete (missingRequest.collect.inputs[0] as JsonObject).validation_request;
|
|
681
|
+
assert.throws(() => decodeReviewNextTransitionV3(missingRequest, { v5: true }), /validation_request is required/);
|
|
682
|
+
|
|
683
|
+
const mismatchedRequestHash = clone(transition);
|
|
684
|
+
((mismatchedRequestHash.collect.inputs[0] as JsonObject).validation_request as JsonObject).request_hash = `sha256:${"0".repeat(64)}`;
|
|
685
|
+
assert.throws(() => decodeReviewNextTransitionV3(mismatchedRequestHash, { v5: true }), /request-hash/);
|
|
686
|
+
|
|
687
|
+
const refuter = clone(transition);
|
|
688
|
+
const refuterInput = refuter.collect.inputs[0] as JsonObject;
|
|
689
|
+
refuterInput.name = "provider_refuter";
|
|
690
|
+
refuterInput.schema = "https://gentle-ai.dev/schema/review/refuter/v1";
|
|
691
|
+
refuterInput.capture_operation = "review.capture-refuter";
|
|
692
|
+
assert.throws(() => decodeReviewNextTransitionV3(refuter, { v5: true }), /validation_request.*targeted-validator/);
|
|
693
|
+
|
|
694
|
+
const malformedRequest = clone(transition);
|
|
695
|
+
((malformedRequest.collect.inputs[0] as JsonObject).validation_request as JsonObject).unadvertised = true;
|
|
696
|
+
assert.throws(() => decodeReviewNextTransitionV3(malformedRequest, { v5: true }), /not allowed/);
|
|
697
|
+
|
|
698
|
+
const weakenedRequest = clone(transition);
|
|
699
|
+
((weakenedRequest.collect.inputs[0] as JsonObject).validation_request as JsonObject).policy_content = "";
|
|
700
|
+
assert.throws(() => decodeReviewNextTransitionV3(weakenedRequest, { v5: true }), /policy_content/);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
test("status enforces authority/frozen/receipt conditionals and decodes the required repair field", () => {
|
|
704
|
+
const current = devFixture<JsonObject>("status-v5-repository-context.captured.json");
|
|
705
|
+
delete current.receipt;
|
|
706
|
+
if (current.action === "finalize") {
|
|
707
|
+
current.action = "stop";
|
|
708
|
+
delete current.next_transition;
|
|
709
|
+
delete current.forecast;
|
|
710
|
+
}
|
|
711
|
+
const decoded = decodeReviewStatusV3(current);
|
|
712
|
+
assert.equal(decoded.repair.status, "unsupported");
|
|
713
|
+
assert.equal(decoded.authority?.state, "reviewing");
|
|
714
|
+
|
|
715
|
+
const missingAuthority = clone(current);
|
|
716
|
+
delete missingAuthority.authority;
|
|
717
|
+
assert.throws(() => decodeReviewStatusV3(missingAuthority), /requires authority/);
|
|
718
|
+
|
|
719
|
+
const compactMissingFrozen = clone(current);
|
|
720
|
+
delete compactMissingFrozen.frozen;
|
|
721
|
+
assert.throws(() => decodeReviewStatusV3(compactMissingFrozen), /requires frozen/);
|
|
722
|
+
});
|
|
723
|
+
|
|
533
724
|
test("next_transition.execute.binding stays open, as its schema declares no properties", () => {
|
|
534
725
|
const withContext = {
|
|
535
726
|
kind: "execute" as const,
|
|
536
|
-
reason_code: "
|
|
727
|
+
reason_code: "fresh_target_ready",
|
|
537
728
|
execute: {
|
|
538
|
-
operation: "review.
|
|
539
|
-
arguments: [{ name: "
|
|
540
|
-
preconditions: [{ name: "target_identity", value:
|
|
541
|
-
|
|
542
|
-
// closing the object was stricter than the contract.
|
|
543
|
-
binding: { target_identity: "sha256:" + "8".repeat(64), repository_context: "rctx1_" + "c".repeat(64) },
|
|
729
|
+
operation: "review.start",
|
|
730
|
+
arguments: [{ name: "lineage", value: "review-fixture", token: "--lineage=review-fixture" }],
|
|
731
|
+
preconditions: [{ name: "target_identity", value: `sha256:${"8".repeat(64)}` }],
|
|
732
|
+
binding: { target_identity: `sha256:${"8".repeat(64)}`, repository_context: `rctx1_${"c".repeat(64)}` },
|
|
544
733
|
},
|
|
545
734
|
};
|
|
546
|
-
|
|
547
735
|
assert.doesNotThrow(() => decodeReviewNextTransitionV3(withContext));
|
|
548
736
|
});
|