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
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import { __testing } from "../extensions/gentle-ai.ts";
|
|
6
|
+
import { reconcileUnknownReviewLastEventCapture } from "../lib/review-last-event-controller.ts";
|
|
7
|
+
import * as nativeReviewCliModule from "../lib/native-review-cli.ts";
|
|
8
|
+
import type { NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
9
|
+
import {
|
|
10
|
+
decodeReviewLastEventClosureV1,
|
|
11
|
+
decodeReviewStartV3,
|
|
12
|
+
type ReviewCollectInputV3,
|
|
13
|
+
type ReviewStatusV3,
|
|
14
|
+
} from "../lib/review-integration-v2.ts";
|
|
15
|
+
|
|
16
|
+
const SHA = `sha256:${"a".repeat(64)}`;
|
|
17
|
+
const TREE = "b".repeat(40);
|
|
18
|
+
const CAPTURED_FIXTURES = join(process.cwd(), "tests", "fixtures", "devbinary");
|
|
19
|
+
|
|
20
|
+
function captured(name: string): unknown {
|
|
21
|
+
return JSON.parse(readFileSync(join(CAPTURED_FIXTURES, name), "utf8"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function closure(operation: string, lineageId: string): Record<string, unknown> {
|
|
25
|
+
return {
|
|
26
|
+
schema: "gentle-ai.review-last-event-closure/v1",
|
|
27
|
+
operation,
|
|
28
|
+
lineage_id: lineageId,
|
|
29
|
+
state: operation === "review.capture-correction-plan" ? "correction_required" : "approved",
|
|
30
|
+
...(operation === "review.capture-correction-plan"
|
|
31
|
+
? { target_identity: SHA, request_hash: SHA, correction_lines: 2 }
|
|
32
|
+
: { action: "native last event closed the review" }),
|
|
33
|
+
store_revision: SHA,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function bindingArguments(lineageId: string, suffix = "0"): ReviewCollectInputV3["arguments"] {
|
|
38
|
+
return [
|
|
39
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
40
|
+
{ name: "expected-revision", value: SHA, token: `--expected-revision=${SHA}` },
|
|
41
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
42
|
+
{ name: "repository-context", value: `rctx1_${"c".repeat(64)}`, token: `--repository-context=rctx1_${"c".repeat(64)}` },
|
|
43
|
+
{ name: "lens", value: `review-risk-${suffix}`, token: `--lens=review-risk-${suffix}` },
|
|
44
|
+
{ name: "order", value: suffix, token: `--order=${suffix}` },
|
|
45
|
+
{ name: "subject-hash", value: SHA, token: `--subject-hash=${SHA}` },
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function materializeInput(lineageId: string, suffix = "0"): ReviewCollectInputV3 {
|
|
50
|
+
const arguments_ = [
|
|
51
|
+
...bindingArguments(lineageId, suffix),
|
|
52
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
53
|
+
{ name: "materialize", value: "true", token: "--materialize=true" },
|
|
54
|
+
];
|
|
55
|
+
return {
|
|
56
|
+
name: "reviewer_result",
|
|
57
|
+
schema: "https://gentle-ai.dev/schema/review/reviewer/v1",
|
|
58
|
+
captureOperation: "review.capture-result",
|
|
59
|
+
arguments: arguments_,
|
|
60
|
+
artifactSubject: {
|
|
61
|
+
schema: "gentle-ai.review-artifact-subject/v2",
|
|
62
|
+
subjectHash: SHA,
|
|
63
|
+
lineageId,
|
|
64
|
+
authorityRevision: SHA,
|
|
65
|
+
targetIdentity: SHA,
|
|
66
|
+
baseTree: TREE,
|
|
67
|
+
candidateTree: TREE,
|
|
68
|
+
changedPathManifestSha256: SHA,
|
|
69
|
+
lens: `review-risk-${suffix}`,
|
|
70
|
+
selectedOrder: Number(suffix),
|
|
71
|
+
},
|
|
72
|
+
submission: {
|
|
73
|
+
operationToken: "capture-result",
|
|
74
|
+
argumentTokens: [...bindingArguments(lineageId, suffix).map((argument) => argument.token!), "--input={{value}}"],
|
|
75
|
+
values: [{ slot: "reviewer_result", domain: "artifact_path_or_stdin", substitutionLocation: 7 }],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function correctionPlanInput(lineageId: string): ReviewCollectInputV3 {
|
|
81
|
+
return {
|
|
82
|
+
name: "correction_plan",
|
|
83
|
+
schema: "gentle-ai.review-correction-plan/v1",
|
|
84
|
+
captureOperation: "review.capture-correction-plan",
|
|
85
|
+
arguments: bindingArguments(lineageId),
|
|
86
|
+
submission: {
|
|
87
|
+
operationToken: "capture-correction-plan",
|
|
88
|
+
argumentTokens: [...bindingArguments(lineageId).map((argument) => argument.token!), "--correction-lines={{value}}"],
|
|
89
|
+
values: [{ slot: "correction_lines", domain: "positive_integer", substitutionLocation: 7, minimum: 2, maximum: 3 }],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function roleInput(lineageId: string, operation: "review.capture-refuter" | "review.capture-validation"): ReviewCollectInputV3 {
|
|
95
|
+
return {
|
|
96
|
+
name: operation === "review.capture-refuter" ? "provider_refuter" : "targeted_validator",
|
|
97
|
+
schema: "gentle-ai.review-provider-role/v1",
|
|
98
|
+
captureOperation: operation,
|
|
99
|
+
arguments: [
|
|
100
|
+
...bindingArguments(lineageId),
|
|
101
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
102
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function status(lineageId: string, inputs: readonly ReviewCollectInputV3[]): ReviewStatusV3 {
|
|
108
|
+
return {
|
|
109
|
+
contract: "gentle-ai.review-integration/v2",
|
|
110
|
+
applicability: "current_target",
|
|
111
|
+
authority: { version: "compact-v2", lineageId, state: "reviewing", generation: 1, revision: SHA },
|
|
112
|
+
action: "stop",
|
|
113
|
+
replayability: "not_replayable",
|
|
114
|
+
targetIdentity: SHA,
|
|
115
|
+
projection: {
|
|
116
|
+
schema: "gentle-ai.review-candidate-projection/v1",
|
|
117
|
+
kind: "current-changes",
|
|
118
|
+
projection: "workspace",
|
|
119
|
+
baseTree: TREE,
|
|
120
|
+
initialReviewTree: TREE,
|
|
121
|
+
currentCandidateTree: TREE,
|
|
122
|
+
pathsDigest: SHA,
|
|
123
|
+
paths: ["app.ts"],
|
|
124
|
+
intendedUntracked: [],
|
|
125
|
+
intendedUntrackedProof: SHA,
|
|
126
|
+
initialSnapshotIdentity: SHA,
|
|
127
|
+
currentSnapshotIdentity: SHA,
|
|
128
|
+
},
|
|
129
|
+
candidates: [],
|
|
130
|
+
nextTransition: { kind: "collect", reasonCode: "capture_required", collect: { inputs: [...inputs] } },
|
|
131
|
+
raw: { schema: "gentle-ai.review-integration.status/v5" },
|
|
132
|
+
} as unknown as ReviewStatusV3;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function capture(lineageId: string, input: ReviewCollectInputV3, native: NativeReviewCli, extras: Record<string, unknown> = {}): Promise<Record<string, unknown>> {
|
|
136
|
+
return __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(input), ...extras }, process.cwd(), native);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
test("strictly decodes the real zero-lens closed START and every last-event closure", () => {
|
|
140
|
+
const start = decodeReviewStartV3(captured("start-v3-zero-lens-closed.captured.json"));
|
|
141
|
+
assert.equal(start.action, "closed");
|
|
142
|
+
assert.equal(start.state, "approved");
|
|
143
|
+
assert.equal(start.lensesRequired, false);
|
|
144
|
+
assert.deepEqual(start.selectedLenses, []);
|
|
145
|
+
for (const [name, operation, state] of [
|
|
146
|
+
["last-event-capture-result-approved.captured.json", "review/capture-result", "approved"],
|
|
147
|
+
["last-event-capture-result-correction-required.captured.json", "review/capture-result", "correction_required"],
|
|
148
|
+
["last-event-capture-correction-plan.captured.json", "review.capture-correction-plan", "correction_required"],
|
|
149
|
+
["last-event-capture-refuter-correction-required.captured.json", "review.capture-refuter", "correction_required"],
|
|
150
|
+
["last-event-capture-refuter-approved.captured.json", "review.capture-refuter", "approved"],
|
|
151
|
+
["last-event-capture-validation-approved.captured.json", "review/capture-validation", "approved"],
|
|
152
|
+
] as const) {
|
|
153
|
+
const decoded = decodeReviewLastEventClosureV1(captured(name));
|
|
154
|
+
assert.equal(decoded.operation, operation, name);
|
|
155
|
+
assert.equal(decoded.state, state, name);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("retired legacy client no longer exposes a FINALIZE route", () => {
|
|
160
|
+
assert.equal("NativeReviewCliV214" in nativeReviewCliModule, false);
|
|
161
|
+
assert.equal("NativeReviewCliV213" in nativeReviewCliModule, false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("one exact offered materialize binding executes only its selected slot and never follows STATUS", async (t) => {
|
|
165
|
+
t.after(() => __testing.setReviewHostRelayRunnerForTesting());
|
|
166
|
+
const lineageId = "one-slot-lineage";
|
|
167
|
+
const selected = materializeInput(lineageId, "0");
|
|
168
|
+
const ignored = materializeInput(lineageId, "1");
|
|
169
|
+
let statusCalls = 0;
|
|
170
|
+
const native = {
|
|
171
|
+
targetStatus: async () => {
|
|
172
|
+
statusCalls += 1;
|
|
173
|
+
return status(lineageId, [selected, ignored]);
|
|
174
|
+
},
|
|
175
|
+
} as unknown as NativeReviewCli;
|
|
176
|
+
const relayed: unknown[] = [];
|
|
177
|
+
__testing.setReviewHostRelayRunnerForTesting(async (request) => {
|
|
178
|
+
relayed.push(request);
|
|
179
|
+
return { promptByteLength: 12, resultByteLength: 8, submission: "{\"admission_decision\":\"completed\"}" };
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const result = await capture(lineageId, selected, native, { reviewerRunAcknowledged: true });
|
|
183
|
+
assert.equal(statusCalls, 1);
|
|
184
|
+
assert.equal(relayed.length, 1);
|
|
185
|
+
assert.equal(result.status, "captured");
|
|
186
|
+
assert.equal(result.outcome, "native-reviewer-result-captured");
|
|
187
|
+
assert.deepEqual((relayed[0] as { captureArgumentTokens: readonly string[] }).captureArgumentTokens, selected.arguments.map((argument) => argument.token));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("malformed, stale, duplicate, and incomplete collect bindings reject before capture mutation", async (t) => {
|
|
191
|
+
t.after(() => __testing.setReviewHostRelayRunnerForTesting());
|
|
192
|
+
const lineageId = "rejected-binding";
|
|
193
|
+
const input = materializeInput(lineageId);
|
|
194
|
+
let statusCalls = 0;
|
|
195
|
+
let launches = 0;
|
|
196
|
+
const native = { targetStatus: async () => { statusCalls += 1; return status(lineageId, [input]); } } as unknown as NativeReviewCli;
|
|
197
|
+
__testing.setReviewHostRelayRunnerForTesting(async () => { launches += 1; return { promptByteLength: 1, resultByteLength: 1, submission: "{}" }; });
|
|
198
|
+
|
|
199
|
+
await assert.rejects(() => __testing.executeReviewCaptureOperation({ lineageId, collectBinding: "{" }, process.cwd(), native), /collectBinding is not valid JSON/);
|
|
200
|
+
const stale = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(materializeInput("other")) }, process.cwd(), native);
|
|
201
|
+
const duplicate = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(input) }, process.cwd(), {
|
|
202
|
+
targetStatus: async () => { statusCalls += 1; return status(lineageId, [input, input]); },
|
|
203
|
+
} as unknown as NativeReviewCli);
|
|
204
|
+
assert.equal(stale.outcome, "capture-binding-rejected");
|
|
205
|
+
assert.equal(duplicate.outcome, "capture-binding-rejected");
|
|
206
|
+
|
|
207
|
+
const missingLineageToken: ReviewCollectInputV3 = { ...input, arguments: input.arguments.filter((argument) => argument.name !== "lineage") };
|
|
208
|
+
const missingTargetToken: ReviewCollectInputV3 = { ...input, arguments: input.arguments.filter((argument) => argument.name !== "target") };
|
|
209
|
+
const mismatchedLineageToken: ReviewCollectInputV3 = {
|
|
210
|
+
...input,
|
|
211
|
+
arguments: input.arguments.map((argument) => argument.name === "lineage" ? { ...argument, value: "other-lineage" } : argument),
|
|
212
|
+
};
|
|
213
|
+
const mismatchedTargetToken: ReviewCollectInputV3 = {
|
|
214
|
+
...input,
|
|
215
|
+
arguments: input.arguments.map((argument) => argument.name === "target" ? { ...argument, value: `sha256:${"d".repeat(64)}` } : argument),
|
|
216
|
+
};
|
|
217
|
+
const missingAuthorityLineage = { ...status(lineageId, [input]), authority: undefined } as ReviewStatusV3;
|
|
218
|
+
const emptyAuthorityLineage = {
|
|
219
|
+
...status(lineageId, [input]),
|
|
220
|
+
authority: { ...status(lineageId, [input]).authority!, lineageId: "" },
|
|
221
|
+
} as unknown as ReviewStatusV3;
|
|
222
|
+
const missingStatusTarget = { ...status(lineageId, [missingTargetToken]), targetIdentity: undefined } as unknown as ReviewStatusV3;
|
|
223
|
+
const emptyStatusTarget = { ...status(lineageId, [input]), targetIdentity: "" } as unknown as ReviewStatusV3;
|
|
224
|
+
for (const [offeredInput, currentStatus] of [
|
|
225
|
+
[missingLineageToken, status(lineageId, [missingLineageToken])],
|
|
226
|
+
[missingTargetToken, status(lineageId, [missingTargetToken])],
|
|
227
|
+
[mismatchedLineageToken, status(lineageId, [mismatchedLineageToken])],
|
|
228
|
+
[mismatchedTargetToken, status(lineageId, [mismatchedTargetToken])],
|
|
229
|
+
[input, missingAuthorityLineage],
|
|
230
|
+
[input, emptyAuthorityLineage],
|
|
231
|
+
[missingTargetToken, missingStatusTarget],
|
|
232
|
+
[input, emptyStatusTarget],
|
|
233
|
+
] as const) {
|
|
234
|
+
const rejected = await __testing.executeReviewCaptureOperation(
|
|
235
|
+
{ lineageId, collectBinding: JSON.stringify(offeredInput), reviewerRunAcknowledged: true },
|
|
236
|
+
process.cwd(),
|
|
237
|
+
{ targetStatus: async () => currentStatus } as unknown as NativeReviewCli,
|
|
238
|
+
);
|
|
239
|
+
assert.equal(rejected.outcome, "capture-binding-rejected");
|
|
240
|
+
}
|
|
241
|
+
assert.equal(launches, 0);
|
|
242
|
+
assert.equal(statusCalls, 2);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("forecast spends zero and acknowledgement runs exactly one materialize reviewer", async (t) => {
|
|
246
|
+
t.after(() => __testing.setReviewHostRelayRunnerForTesting());
|
|
247
|
+
const lineageId = "forecast-lineage";
|
|
248
|
+
const input = materializeInput(lineageId);
|
|
249
|
+
let statusCalls = 0;
|
|
250
|
+
let launches = 0;
|
|
251
|
+
const native = { targetStatus: async () => { statusCalls += 1; return status(lineageId, [input]); } } as unknown as NativeReviewCli;
|
|
252
|
+
__testing.setReviewHostRelayRunnerForTesting(async () => { launches += 1; return { promptByteLength: 1, resultByteLength: 1, submission: "{}" }; });
|
|
253
|
+
|
|
254
|
+
const forecast = await capture(lineageId, input, native);
|
|
255
|
+
const acknowledged = await capture(lineageId, input, native, { reviewerRunAcknowledged: true });
|
|
256
|
+
assert.equal(forecast.outcome, "reviewer-model-run-forecast");
|
|
257
|
+
assert.equal((forecast.cost_forecast as { model_runs: number }).model_runs, 1);
|
|
258
|
+
assert.equal(acknowledged.status, "captured");
|
|
259
|
+
assert.equal(launches, 1);
|
|
260
|
+
assert.equal(statusCalls, 2);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("correction-plan preserves exact provider tokens and bounds without a follow-up lifecycle call", async () => {
|
|
264
|
+
const lineageId = "correction-plan";
|
|
265
|
+
const input = correctionPlanInput(lineageId);
|
|
266
|
+
let statusCalls = 0;
|
|
267
|
+
const requests: Array<{ argumentTokens: readonly string[]; correctionLines: number }> = [];
|
|
268
|
+
const native = {
|
|
269
|
+
targetStatus: async () => { statusCalls += 1; return status(lineageId, [input]); },
|
|
270
|
+
captureCorrectionPlan: async (request: { argumentTokens: readonly string[]; correctionLines: number }) => {
|
|
271
|
+
requests.push(request);
|
|
272
|
+
return decodeReviewLastEventClosureV1(closure("review.capture-correction-plan", lineageId));
|
|
273
|
+
},
|
|
274
|
+
} as unknown as NativeReviewCli;
|
|
275
|
+
const missing = await capture(lineageId, input, native);
|
|
276
|
+
const outOfBounds = await capture(lineageId, input, native, { correctionLines: 4 });
|
|
277
|
+
const completed = await capture(lineageId, input, native, { correctionLines: 2 });
|
|
278
|
+
assert.equal(missing.outcome, "correction-lines-required");
|
|
279
|
+
assert.equal(outOfBounds.outcome, "capture-binding-rejected");
|
|
280
|
+
assert.equal(completed.status, "closed");
|
|
281
|
+
assert.equal(requests.length, 1);
|
|
282
|
+
assert.deepEqual(requests[0]!.argumentTokens, input.submission!.argumentTokens);
|
|
283
|
+
assert.equal(requests[0]!.correctionLines, 2);
|
|
284
|
+
assert.equal(statusCalls, 3);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("refuter and validation each execute one self-contained provider vector", async () => {
|
|
288
|
+
for (const operation of ["review.capture-refuter", "review.capture-validation"] as const) {
|
|
289
|
+
const lineageId = `role-${operation}`;
|
|
290
|
+
const input = roleInput(lineageId, operation);
|
|
291
|
+
const requests: Array<{ captureOperation: string; argumentTokens: readonly string[] }> = [];
|
|
292
|
+
const native = {
|
|
293
|
+
targetStatus: async () => status(lineageId, [input]),
|
|
294
|
+
captureProviderRole: async (request: { captureOperation: string; argumentTokens: readonly string[] }) => {
|
|
295
|
+
requests.push(request);
|
|
296
|
+
return { schema: "gentle-ai.review-provider-role-capture/v1", lineageId, targetIdentity: SHA, role: operation, captured: true };
|
|
297
|
+
},
|
|
298
|
+
} as unknown as NativeReviewCli;
|
|
299
|
+
const result = await capture(lineageId, input, native);
|
|
300
|
+
assert.equal(result.status, "captured");
|
|
301
|
+
assert.equal(requests.length, 1);
|
|
302
|
+
assert.equal(requests[0]!.captureOperation, operation);
|
|
303
|
+
assert.deepEqual(requests[0]!.argumentTokens, input.arguments.map((argument) => argument.token!));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("terminal capture closes directly, nonterminal capture does not auto-follow, and unknown mutation reconciles once", async (t) => {
|
|
308
|
+
t.after(() => __testing.setReviewHostRelayRunnerForTesting());
|
|
309
|
+
const lineageId = "closure-behavior";
|
|
310
|
+
const input = materializeInput(lineageId);
|
|
311
|
+
let statusCalls = 0;
|
|
312
|
+
const native = { targetStatus: async () => { statusCalls += 1; return status(lineageId, [input]); } } as unknown as NativeReviewCli;
|
|
313
|
+
__testing.setReviewHostRelayRunnerForTesting(async () => ({ promptByteLength: 1, resultByteLength: 1, submission: JSON.stringify(closure("review/capture-result", lineageId)) }));
|
|
314
|
+
const terminal = await capture(lineageId, input, native, { reviewerRunAcknowledged: true });
|
|
315
|
+
assert.equal(terminal.status, "closed");
|
|
316
|
+
assert.equal(statusCalls, 1, "terminal closure performs no post-success STATUS");
|
|
317
|
+
|
|
318
|
+
statusCalls = 0;
|
|
319
|
+
let captureCalls = 0;
|
|
320
|
+
const correction = correctionPlanInput(lineageId);
|
|
321
|
+
const ambiguousNative = {
|
|
322
|
+
targetStatus: async () => { statusCalls += 1; return status(lineageId, [correction]); },
|
|
323
|
+
captureCorrectionPlan: async () => {
|
|
324
|
+
captureCalls += 1;
|
|
325
|
+
throw Object.assign(new Error("response lost"), { mutationOutcome: "unknown", nextAction: "review.status" });
|
|
326
|
+
},
|
|
327
|
+
} as unknown as NativeReviewCli;
|
|
328
|
+
const reconciled = await capture(lineageId, correction, ambiguousNative, { correctionLines: 2 });
|
|
329
|
+
assert.equal(reconciled.status, "reconciled");
|
|
330
|
+
assert.equal(captureCalls, 1);
|
|
331
|
+
assert.equal(statusCalls, 2, "only initial STATUS plus one ambiguity reconciliation");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("unknown-capture reconciliation validates the target-scoped lineage without replay", async () => {
|
|
335
|
+
const calls: Array<Record<string, unknown>> = [];
|
|
336
|
+
const native = {
|
|
337
|
+
targetStatus: async (request: Record<string, unknown>) => {
|
|
338
|
+
calls.push(request);
|
|
339
|
+
assert.equal(request.lineageId, "reconcile-lineage");
|
|
340
|
+
return { targetIdentity: SHA, authority: { lineageId: "reconcile-lineage" } } as ReviewStatusV3;
|
|
341
|
+
},
|
|
342
|
+
} as unknown as NativeReviewCli;
|
|
343
|
+
const result = await reconcileUnknownReviewLastEventCapture(native, "/repo", { lineageId: "reconcile-lineage", targetIdentity: SHA });
|
|
344
|
+
await reconcileUnknownReviewLastEventCapture(native, "/repo", { lineageId: "reconcile-lineage", targetIdentity: SHA }, { baseRef: "refs/heads/main", committedOnly: true });
|
|
345
|
+
assert.deepEqual(calls, [{ cwd: "/repo", lineageId: "reconcile-lineage" }, { cwd: "/repo", lineageId: "reconcile-lineage", baseRef: "refs/heads/main", committedOnly: true }]);
|
|
346
|
+
assert.equal(result.targetIdentity, SHA);
|
|
347
|
+
let launches = 0;
|
|
348
|
+
const strictNative = new nativeReviewCliModule.NativeReviewCliV216(async () => { launches += 1; throw new Error("must not launch"); }, "/package/.gentle-ai/gentle-ai");
|
|
349
|
+
for (const selector of [{ committedOnly: true }, { baseRef: "refs/heads/main" }, { baseRef: "refs/heads/main", committedOnly: false }] as unknown as readonly Parameters<typeof reconcileUnknownReviewLastEventCapture>[3][]) await assert.rejects(() => reconcileUnknownReviewLastEventCapture(strictNative, "/repo", { lineageId: "reconcile-lineage", targetIdentity: SHA }, selector));
|
|
350
|
+
assert.equal(launches, 0);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// One approved terminal closure carrying the exact continuation the provider
|
|
354
|
+
// renders: closed positional arguments, the single approved precondition, and a
|
|
355
|
+
// binding that names the candidate being burned.
|
|
356
|
+
function approvedClosureWithAcknowledgement(): Record<string, unknown> {
|
|
357
|
+
const lineageId = "review-acknowledgement-fixture";
|
|
358
|
+
const body = closure("review/capture-result", lineageId);
|
|
359
|
+
body.acknowledgement = {
|
|
360
|
+
operation: "review.acknowledge-approved",
|
|
361
|
+
command: "gentle-ai review acknowledge-approved",
|
|
362
|
+
arguments: [
|
|
363
|
+
{ name: "cwd", value: "/repo", token: "--cwd=/repo" },
|
|
364
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
365
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
366
|
+
{ name: "expected-revision", value: SHA, token: `--expected-revision=${SHA}` },
|
|
367
|
+
{ name: "token", value: "a".repeat(64), token: `--token=${"a".repeat(64)}` },
|
|
368
|
+
],
|
|
369
|
+
preconditions: [{ name: "state", value: "approved" }],
|
|
370
|
+
binding: { lineage_id: lineageId, revision: SHA, target_identity: SHA },
|
|
371
|
+
};
|
|
372
|
+
return body;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
test("the approved acknowledgement fixture decodes as a runnable continuation", () => {
|
|
376
|
+
const decoded = decodeReviewLastEventClosureV1(approvedClosureWithAcknowledgement());
|
|
377
|
+
assert.equal(decoded.acknowledgement?.operation, "review.acknowledge-approved");
|
|
378
|
+
assert.equal(decoded.acknowledgementUndecodable, undefined);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("an approved acknowledgement must prove the target its relayed token burns", () => {
|
|
382
|
+
const closure = approvedClosureWithAcknowledgement();
|
|
383
|
+
const acknowledgement = closure.acknowledgement as Record<string, unknown>;
|
|
384
|
+
const args = (acknowledgement.arguments as Record<string, unknown>[]).map((argument) => ({ ...argument }));
|
|
385
|
+
// The relayed token now names a different candidate than the binding does.
|
|
386
|
+
const decoyTarget = `sha256:${"b".repeat(64)}`;
|
|
387
|
+
args[2] = { name: "target", value: decoyTarget, token: `--target=${decoyTarget}` };
|
|
388
|
+
acknowledgement.arguments = args;
|
|
389
|
+
assert.throws(
|
|
390
|
+
() => decodeReviewLastEventClosureV1(closure),
|
|
391
|
+
/acknowledgement target argument does not match its binding/,
|
|
392
|
+
"a relayed target the binding does not commit to must be refused",
|
|
393
|
+
);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("an unreadable acknowledgement degrades the continuation, never the approval", () => {
|
|
397
|
+
const closure = approvedClosureWithAcknowledgement();
|
|
398
|
+
// One extra provider-side argument is enough to miss the closed shape.
|
|
399
|
+
const acknowledgement = closure.acknowledgement as Record<string, unknown>;
|
|
400
|
+
acknowledgement.arguments = [
|
|
401
|
+
...(acknowledgement.arguments as unknown[]),
|
|
402
|
+
{ name: "future", value: "1", token: "--future=1" },
|
|
403
|
+
];
|
|
404
|
+
const decoded = decodeReviewLastEventClosureV1(closure);
|
|
405
|
+
assert.equal(decoded.state, "approved", "the approval outcome must survive an unreadable continuation");
|
|
406
|
+
assert.equal(decoded.acknowledgement, undefined, "an unreadable continuation must not be offered as runnable");
|
|
407
|
+
assert.equal(decoded.acknowledgementUndecodable, true, "the host must be told the continuation exists and could not be read");
|
|
408
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
import { REVIEW_LENS_PARITY_PATTERNS } from "./support/review-lens-parity.ts";
|
|
6
6
|
|
|
@@ -14,8 +14,6 @@ const REVIEW_LENSES = [
|
|
|
14
14
|
"assets/agents/review-reliability.md",
|
|
15
15
|
] as const;
|
|
16
16
|
const JUDGES = ["assets/agents/jd-judge-a.md", "assets/agents/jd-judge-b.md"] as const;
|
|
17
|
-
const REFUTER = "assets/agents/review-refuter.md";
|
|
18
|
-
const VALIDATOR = "assets/agents/review-validator.md";
|
|
19
17
|
const FIX_AGENT = "assets/agents/jd-fix-agent.md";
|
|
20
18
|
const JD_SKILL = "skills/judgment-day/SKILL.md";
|
|
21
19
|
const JD_PROMPTS = "skills/judgment-day/references/prompts-and-formats.md";
|
|
@@ -87,7 +85,7 @@ const FIX_PATTERNS = [
|
|
|
87
85
|
/Do not add findings, alter frozen claims, authorize transitions, deliver, publish, or start another actor\./,
|
|
88
86
|
] as const;
|
|
89
87
|
|
|
90
|
-
test("canonical contract defines compact risk, causal admission, correction, CAS, compatibility, and
|
|
88
|
+
test("canonical contract defines compact risk, causal admission, correction, CAS, compatibility, and the delivery boundary", () => {
|
|
91
89
|
const content = read(CANONICAL);
|
|
92
90
|
assertMatches(CANONICAL, content, [
|
|
93
91
|
/start -> finalize -> validate/,
|
|
@@ -103,29 +101,25 @@ test("canonical contract defines compact risk, causal admission, correction, CAS
|
|
|
103
101
|
/original budget/i,
|
|
104
102
|
/frozen findings and genesis scope/i,
|
|
105
103
|
/content-derived revisions, compare-and-swap replacement, exact retry idempotency/i,
|
|
106
|
-
/graph-v1 ordinary lineages remain readable
|
|
104
|
+
/graph-v1 ordinary lineages remain readable for compatibility but reject new mutation/i,
|
|
107
105
|
/Legacy graph bundle export\/import is retired/i,
|
|
108
106
|
/Judgment Day remains mutable on graph-v1/i,
|
|
109
|
-
|
|
110
|
-
/
|
|
111
|
-
/
|
|
112
|
-
/
|
|
113
|
-
/
|
|
107
|
+
/--agent=pi --materialize=true/,
|
|
108
|
+
/provider-owned submission form/i,
|
|
109
|
+
/self-contained authority-advancing vectors/i,
|
|
110
|
+
/Commit, push, pull-request creation, and release creation are not RDD gates/i,
|
|
111
|
+
/Review outcomes and receipt state are informational and never authorize, consume, rewrite, or block a Bash delivery command/i,
|
|
112
|
+
/Pi does not inspect RDD mode or native authority for those commands/i,
|
|
113
|
+
/Review transactions, validation, and SDD never perform delivery commands themselves/i,
|
|
114
114
|
/local orchestrator and same-user process are trusted/i,
|
|
115
115
|
/reviewer and validator outputs remain semantically untrusted/i,
|
|
116
116
|
/do not report.*trusted local orchestrator.*security finding/i,
|
|
117
117
|
/untrusted repository content.*malformed inputs.*stale authority.*path drift.*external callers/i,
|
|
118
118
|
...JUDGMENT_DAY_PATTERNS,
|
|
119
119
|
]);
|
|
120
|
-
assert.match(read(README), /
|
|
121
|
-
assert.doesNotMatch(read(README), /
|
|
122
|
-
assert.
|
|
123
|
-
assert.match(read(README), /Residual gap \(separate follow-up\): native first-push authorization remains unsupported until Pi has a persisted explicit advertised-base source\./);
|
|
124
|
-
const lifecycleSpec = read("openspec/specs/review-transaction/spec.md");
|
|
125
|
-
assert.match(lifecycleSpec, /split fetch\/push[\s\S]*upstream contract limitation/i);
|
|
126
|
-
assert.match(lifecycleSpec, /allow response MUST return the exact requested gate/i);
|
|
127
|
-
assert.match(lifecycleSpec, /non-authorizing denial MAY return an empty gate[\s\S]*pre_pr_boundary/i);
|
|
128
|
-
assert.match(lifecycleSpec, /one aggregate bash-time deadline/i);
|
|
120
|
+
assert.match(read(README), /Review outcomes and receipt state are informational; commit, push, pull-request, and release delivery follow ordinary repository policy\./);
|
|
121
|
+
assert.doesNotMatch(read(README), /one one-shot authorization for the exact command/i);
|
|
122
|
+
assert.doesNotMatch(read(README), /review-publication-gate/i);
|
|
129
123
|
});
|
|
130
124
|
|
|
131
125
|
for (const path of REVIEW_LENSES) {
|
|
@@ -135,6 +129,69 @@ for (const path of REVIEW_LENSES) {
|
|
|
135
129
|
});
|
|
136
130
|
}
|
|
137
131
|
|
|
132
|
+
function packagePaths(): string[] {
|
|
133
|
+
const manifest = JSON.parse(read("package.json")) as { files?: unknown };
|
|
134
|
+
if (!Array.isArray(manifest.files)) throw new Error("package.json must declare package files");
|
|
135
|
+
return manifest.files.map((entry) => {
|
|
136
|
+
if (typeof entry !== "string") throw new Error("package.json files entries must be strings");
|
|
137
|
+
return entry.replace(/\/+$/, "");
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function projectPath(absolutePath: string): string | undefined {
|
|
142
|
+
const path = relative(ROOT, absolutePath);
|
|
143
|
+
if (path === "" || path === ".." || path.startsWith(`..${sep}`)) return undefined;
|
|
144
|
+
return path.split(sep).join("/");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isPackagedFile(path: string, packageRoots: readonly string[]): boolean {
|
|
148
|
+
const absolutePath = resolve(ROOT, path);
|
|
149
|
+
const projectRelativePath = projectPath(absolutePath);
|
|
150
|
+
if (!projectRelativePath || !existsSync(absolutePath) || !statSync(absolutePath).isFile()) return false;
|
|
151
|
+
return packageRoots.some((root) => projectRelativePath === root || projectRelativePath.startsWith(`${root}/`));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function packagedOrdinaryReviewLenses(): string[] {
|
|
155
|
+
const packageRoots = packagePaths();
|
|
156
|
+
return readdirSync(join(ROOT, "assets", "agents"), { withFileTypes: true })
|
|
157
|
+
.filter((entry) => entry.isFile() && entry.name.startsWith("review-") && entry.name.endsWith(".md"))
|
|
158
|
+
.map((entry) => `assets/agents/${entry.name}`)
|
|
159
|
+
.filter((path) => isPackagedFile(path, packageRoots))
|
|
160
|
+
.sort();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const MARKDOWN_FILE_REFERENCE = /(?:https?:\/\/[^\s)`\]]+|(?:\.{1,2}\/)?[A-Za-z0-9][A-Za-z0-9_./-]*)\.md\b/g;
|
|
164
|
+
|
|
165
|
+
function assertMarkdownFileDependenciesResolve(path: string, content: string, packageRoots: readonly string[]): void {
|
|
166
|
+
const sourceDirectory = dirname(join(ROOT, path));
|
|
167
|
+
const unresolved = [...content.matchAll(MARKDOWN_FILE_REFERENCE)]
|
|
168
|
+
.map((match) => match[0])
|
|
169
|
+
.filter((reference) => {
|
|
170
|
+
if (reference.startsWith("http://") || reference.startsWith("https://")) return true;
|
|
171
|
+
return ![resolve(ROOT, reference), resolve(sourceDirectory, reference)].some((candidate) => {
|
|
172
|
+
const candidatePath = projectPath(candidate);
|
|
173
|
+
return candidatePath !== undefined && isPackagedFile(candidatePath, packageRoots);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
assert.deepEqual(unresolved, [], `${path} names unavailable Markdown dependencies: ${unresolved.join(", ")}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
test("packaged ordinary review lenses resolve their Markdown file dependencies", () => {
|
|
180
|
+
const packageRoots = packagePaths();
|
|
181
|
+
const lenses = packagedOrdinaryReviewLenses();
|
|
182
|
+
assert.ok(lenses.length > 0, "package must ship at least one ordinary review lens");
|
|
183
|
+
for (const path of lenses) assertMarkdownFileDependenciesResolve(path, read(path), packageRoots);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("ordinary review dependencies allow packaged files and reject unresolved paths", () => {
|
|
187
|
+
const packageRoots = packagePaths();
|
|
188
|
+
assertMarkdownFileDependenciesResolve("assets/agents/review-risk.md", "Sources: docs/native-authority-architecture.md", packageRoots);
|
|
189
|
+
assert.throws(
|
|
190
|
+
() => assertMarkdownFileDependenciesResolve("assets/agents/review-risk.md", "Sources: docs/nonexistent/security.md", packageRoots),
|
|
191
|
+
/unavailable Markdown dependencies: docs\/nonexistent\/security\.md/,
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
138
195
|
test("ordinary lens prompts contain the literal compact-v2 native result envelope", () => {
|
|
139
196
|
const expectedLenses = ["review-risk", "review-resilience", "review-readability", "review-reliability"];
|
|
140
197
|
for (const [index, path] of REVIEW_LENSES.entries()) {
|
|
@@ -196,29 +253,15 @@ test("risk lens distinguishes trusted orchestration from concrete boundary bypas
|
|
|
196
253
|
assert.match(content, /untrusted repository content.*malformed inputs.*stale authority.*path drift.*external callers/i);
|
|
197
254
|
});
|
|
198
255
|
|
|
199
|
-
test("
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
/Do not create findings, alter frozen claims, request fixes, launch actors, persist authority, or repeat/,
|
|
209
|
-
]);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
test("targeted validator checks only original criteria and correction regression without expanding scope", () => {
|
|
213
|
-
const content = read(VALIDATOR);
|
|
214
|
-
assertMatches(VALIDATOR, content, [
|
|
215
|
-
/original-criteria proof/,
|
|
216
|
-
/correction-regression proof/,
|
|
217
|
-
/Validate the original criteria and correction regression only/,
|
|
218
|
-
/Never expand paths, IDs, untracked scope, acceptance criteria, or correction purpose/,
|
|
219
|
-
/empty `fix_caused_findings` array/,
|
|
220
|
-
/Do not request another fix or attempt, launch actors, persist authority, or repeat yourself/,
|
|
221
|
-
]);
|
|
256
|
+
test("the Pi-owned adversarial role agents are retired: roles execute through Go-owned pi processes", () => {
|
|
257
|
+
// gentle-pi#311 P5: the refuter and targeted validator are no longer
|
|
258
|
+
// Pi-authored actors. The provider renders self-contained
|
|
259
|
+
// review.capture-refuter / review.capture-validation vectors; executing
|
|
260
|
+
// them makes Go materialize the role prompt, spawn its own locked-down pi
|
|
261
|
+
// subprocess, and admit the raw verdict.
|
|
262
|
+
for (const retired of ["assets/agents/review-refuter.md", "assets/agents/review-validator.md"]) {
|
|
263
|
+
assert.throws(() => read(retired), `${retired} must be deleted`);
|
|
264
|
+
}
|
|
222
265
|
});
|
|
223
266
|
|
|
224
267
|
for (const path of JUDGES) {
|
|
@@ -267,33 +310,36 @@ test("Judgment Day skill and prompts preserve bounded fix and re-judgment author
|
|
|
267
310
|
assertMatches(FIX_AGENT, read(FIX_AGENT), FIX_PATTERNS);
|
|
268
311
|
});
|
|
269
312
|
|
|
270
|
-
test("orchestrator, skill, and README
|
|
313
|
+
test("orchestrator, injected skill, and README defer RDD lifecycle ownership to Gentle AI", () => {
|
|
314
|
+
const boundary = "Gentle AI dynamically supplies runtime-specific RDD instructions via generated Pi APPEND_SYSTEM composition. Follow only those exact native instructions; if absent or unsupported, this package does not invent or fall back.";
|
|
315
|
+
const orchestrator = union(ORCHESTRATOR);
|
|
316
|
+
assert.ok(orchestrator.includes(boundary), "orchestrator must carry the sole static ownership boundary");
|
|
317
|
+
|
|
271
318
|
for (const [label, content] of [
|
|
272
|
-
["orchestrator", union(ORCHESTRATOR)],
|
|
273
319
|
[GENTLE_SKILL, read(GENTLE_SKILL)],
|
|
274
320
|
[README, read(README)],
|
|
275
321
|
] as const) {
|
|
276
322
|
assertMatches(label, content, [
|
|
277
|
-
/
|
|
278
|
-
|
|
279
|
-
/one correction transaction/i,
|
|
280
|
-
/(?:graph-v1|legacy)[\s\S]*(?:read-only|reject mutation)/i,
|
|
281
|
-
/Judgment Day[\s\S]*(?:explicit|separate)/i,
|
|
282
|
-
/(?:one-shot|one exact one-shot)[\s\S]*(?:bash time|bash-time)/i,
|
|
323
|
+
/Gentle AI dynamically supplies runtime-specific RDD instructions/i,
|
|
324
|
+
/(?:sole lifecycle authority|does not define an RDD lifecycle)/i,
|
|
283
325
|
]);
|
|
284
326
|
}
|
|
327
|
+
|
|
328
|
+
for (const [label, content] of [
|
|
329
|
+
["orchestrator", orchestrator],
|
|
330
|
+
[GENTLE_SKILL, read(GENTLE_SKILL)],
|
|
331
|
+
] as const) {
|
|
332
|
+
assert.doesNotMatch(content, /start -> finalize -> validate|INSPECT before START|next_transition|review\.capture-result/i, label);
|
|
333
|
+
}
|
|
285
334
|
});
|
|
286
335
|
|
|
287
|
-
test("README documents the
|
|
336
|
+
test("README documents the dynamic runtime authority boundary without an old package route", () => {
|
|
288
337
|
const content = read(README);
|
|
289
|
-
assert.match(content, /
|
|
290
|
-
assert.match(content, /
|
|
291
|
-
assert.
|
|
292
|
-
assert.match(content, /
|
|
293
|
-
assert.match(content, /
|
|
294
|
-
assert.match(content, /MUST NOT run a downgraded binary/i);
|
|
295
|
-
assert.match(content, /existing branch.*advertised commit equals.*old object/is);
|
|
296
|
-
assert.match(content, /never guesses? a base.*upstream.*default branch.*nearest ancestor/is);
|
|
338
|
+
assert.match(content, /Gentle AI dynamically supplies runtime-specific RDD instructions/i);
|
|
339
|
+
assert.match(content, /does not define an RDD lifecycle/i);
|
|
340
|
+
assert.doesNotMatch(content, /New ordinary review uses compact `gentle_review` `start -> finalize -> validate`\./);
|
|
341
|
+
assert.match(content, /Dangerous-command safety remains independent and authoritative/);
|
|
342
|
+
assert.match(content, /Project and user overrides may shadow a package asset/);
|
|
297
343
|
});
|
|
298
344
|
|
|
299
345
|
test("managed contracts retain no fresh lifecycle review directive", () => {
|