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,134 @@
|
|
|
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 type { NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
7
|
+
import { assertReviewLastEventClosureBinding, decodeReviewLastEventClosureV1, type ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
8
|
+
|
|
9
|
+
const CAPTURED_FIXTURES = join(process.cwd(), "tests", "fixtures", "devbinary");
|
|
10
|
+
|
|
11
|
+
function captured(name: string): Record<string, unknown> {
|
|
12
|
+
return JSON.parse(readFileSync(join(CAPTURED_FIXTURES, name), "utf8")) as Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test("captured correction-required result preserves the provider status continuation", () => {
|
|
16
|
+
const fixture = captured("last-event-capture-result-correction-required.captured.json");
|
|
17
|
+
const closure = decodeReviewLastEventClosureV1(fixture);
|
|
18
|
+
assert.equal(closure.operation, "review/capture-result");
|
|
19
|
+
assert.equal(closure.state, "correction_required");
|
|
20
|
+
assert.match(closure.storeRevision, /^sha256:[a-f0-9]{64}$/);
|
|
21
|
+
assert.equal(closure.statusContinuation?.operation, "review.status");
|
|
22
|
+
assert.deepEqual(
|
|
23
|
+
closure.statusContinuation?.arguments.map((argument) => argument.token),
|
|
24
|
+
[
|
|
25
|
+
"--cwd=/tmp/repository",
|
|
26
|
+
"--contract=gentle-ai.review-integration/v2",
|
|
27
|
+
"--next-transition=true",
|
|
28
|
+
"--lineage=review-afeed0d5a9aa24a1",
|
|
29
|
+
"--agent=pi",
|
|
30
|
+
],
|
|
31
|
+
);
|
|
32
|
+
assert.deepEqual(closure.statusContinuation?.raw, fixture.status_continuation);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("correction status continuation stays bound to its enclosing closure and provider target", () => {
|
|
36
|
+
const mismatch = `sha256:${"b".repeat(64)}`;
|
|
37
|
+
const cases = [
|
|
38
|
+
{
|
|
39
|
+
name: "missing binding lineage",
|
|
40
|
+
mutate: (binding: Record<string, unknown>) => { delete binding.lineage_id; },
|
|
41
|
+
error: /lineage does not match its enclosing closure/,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "different binding lineage",
|
|
45
|
+
mutate: (binding: Record<string, unknown>) => { binding.lineage_id = "review-different"; },
|
|
46
|
+
error: /lineage does not match its enclosing closure/,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "missing binding revision",
|
|
50
|
+
mutate: (binding: Record<string, unknown>) => { delete binding.revision; },
|
|
51
|
+
error: /revision does not match its enclosing closure/,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "different binding revision",
|
|
55
|
+
mutate: (binding: Record<string, unknown>) => { binding.revision = mismatch; },
|
|
56
|
+
error: /revision does not match its enclosing closure/,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
for (const scenario of cases) {
|
|
60
|
+
const fixture = captured("last-event-capture-result-correction-required.captured.json");
|
|
61
|
+
const continuation = fixture.status_continuation as Record<string, unknown>;
|
|
62
|
+
scenario.mutate(continuation.binding as Record<string, unknown>);
|
|
63
|
+
assert.throws(() => decodeReviewLastEventClosureV1(fixture), scenario.error, scenario.name);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const argumentMismatch = captured("last-event-capture-result-correction-required.captured.json");
|
|
67
|
+
const continuation = argumentMismatch.status_continuation as Record<string, unknown>;
|
|
68
|
+
const argumentsList = continuation.arguments as Array<Record<string, unknown>>;
|
|
69
|
+
const lineageArgument = argumentsList.find((argument) => argument.name === "lineage")!;
|
|
70
|
+
lineageArgument.value = "review-different";
|
|
71
|
+
lineageArgument.token = "--lineage=review-different";
|
|
72
|
+
assert.throws(() => decodeReviewLastEventClosureV1(argumentMismatch), /lineage argument does not match its enclosing closure/);
|
|
73
|
+
|
|
74
|
+
const closure = decodeReviewLastEventClosureV1(captured("last-event-capture-result-correction-required.captured.json"));
|
|
75
|
+
assert.throws(
|
|
76
|
+
() => assertReviewLastEventClosureBinding(closure, { lineageId: closure.lineageId, targetIdentity: mismatch }),
|
|
77
|
+
/status continuation target does not match its provider binding/,
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("correction-required result and refuter closures require one status continuation", () => {
|
|
82
|
+
for (const name of [
|
|
83
|
+
"last-event-capture-result-correction-required.captured.json",
|
|
84
|
+
"last-event-capture-refuter-correction-required.captured.json",
|
|
85
|
+
]) {
|
|
86
|
+
const fixture = captured(name);
|
|
87
|
+
delete fixture.status_continuation;
|
|
88
|
+
assert.throws(() => decodeReviewLastEventClosureV1(fixture), /requires status_continuation/, name);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("closures outside result or refuter correction-required forbid a status continuation", () => {
|
|
93
|
+
const approved = captured("last-event-capture-result-approved.captured.json");
|
|
94
|
+
approved.status_continuation = captured("last-event-capture-result-correction-required.captured.json").status_continuation;
|
|
95
|
+
assert.throws(() => decodeReviewLastEventClosureV1(approved), /status_continuation is only valid/, "approved result closure");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("current capture fails closed when fresh STATUS no longer offers the corrected candidate binding", async () => {
|
|
99
|
+
const lineageId = "corrected-candidate";
|
|
100
|
+
const sha = `sha256:${"a".repeat(64)}`;
|
|
101
|
+
const binding = {
|
|
102
|
+
name: "provider_targeted_validator",
|
|
103
|
+
schema: "https://gentle-ai.dev/schema/review/targeted-validator/v1",
|
|
104
|
+
captureOperation: "review.capture-validation",
|
|
105
|
+
arguments: [
|
|
106
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
107
|
+
{ name: "target", value: sha, token: `--target=${sha}` },
|
|
108
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
109
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
let captureCalls = 0;
|
|
113
|
+
const native = {
|
|
114
|
+
targetStatus: async () => ({
|
|
115
|
+
contract: "gentle-ai.review-integration/v2",
|
|
116
|
+
applicability: "current_target",
|
|
117
|
+
authority: { version: "compact-v2", lineageId, state: "correction_required", generation: 1, revision: sha },
|
|
118
|
+
receipt: { status: "expected_missing" },
|
|
119
|
+
action: "stop",
|
|
120
|
+
replayability: "not_replayable",
|
|
121
|
+
targetIdentity: sha,
|
|
122
|
+
projection: { schema: "gentle-ai.review-candidate-projection/v1", kind: "current-changes", projection: "workspace", baseTree: "b".repeat(40), initialReviewTree: "b".repeat(40), currentCandidateTree: "c".repeat(40), pathsDigest: sha, paths: ["corrected.ts"], intendedUntracked: [], intendedUntrackedProof: sha, initialSnapshotIdentity: sha, currentSnapshotIdentity: sha },
|
|
123
|
+
repair: { schema: "gentle-ai.review-authority-repair-assessment/v1", status: "unsupported", counts: { lineages: 0, compactLineages: 0, legacyLineages: 0, events: 0, bytes: 0, eligibleCandidates: 0, unsupportedLineages: 0, conflicts: 0 }, supportedOperations: [], authorizationSchema: "gentle-ai.review-repair-authorization/v1" },
|
|
124
|
+
candidates: [],
|
|
125
|
+
nextTransition: { kind: "stop", reasonCode: "corrected_candidate_unavailable" },
|
|
126
|
+
raw: { schema: "gentle-ai.review-integration.status/v5" },
|
|
127
|
+
} as unknown as ReviewStatusV3),
|
|
128
|
+
captureProviderRole: async () => { captureCalls += 1; throw new Error("must not capture"); },
|
|
129
|
+
} as unknown as NativeReviewCli;
|
|
130
|
+
const result = await __testing.executeReviewCaptureOperation({ lineageId, collectBinding: JSON.stringify(binding) }, process.cwd(), native);
|
|
131
|
+
assert.equal(result.outcome, "capture-binding-rejected");
|
|
132
|
+
assert.equal(result.mutation_outcome, "none");
|
|
133
|
+
assert.equal(captureCalls, 0);
|
|
134
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { __testing } from "../extensions/gentle-ai.ts";
|
|
8
|
+
import type { NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
9
|
+
import { CandidateViewError, CandidateViewRegistry, injectReviewCandidateView } from "../lib/review-candidate-view.ts";
|
|
10
|
+
import type { ReviewCollectInputV3, ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
11
|
+
|
|
12
|
+
// Field report (2026-08-16, gentle-pi main 402f9f77 + gentle-ai
|
|
13
|
+
// 2.4.0-main.20278905): after #340 the FINALIZE routing defect was fixed but
|
|
14
|
+
// the Pi relay's candidate-view registration still refused for an externally
|
|
15
|
+
// recovered successor. Reproduced against the live binary: hydration itself
|
|
16
|
+
// works for that shape (a dirty LINKED worktree hydrates fine) — the gap is
|
|
17
|
+
// that hydration was wired into the STATUS controller operation ONLY, while
|
|
18
|
+
// the maintainer's flow is `finalize` (now correctly blocked with
|
|
19
|
+
// review.capture-result) followed by a reviewer dispatch. The FINALIZE lane
|
|
20
|
+
// decodes the same authoritative status and never hydrated from it.
|
|
21
|
+
//
|
|
22
|
+
// Second defect in the same area: every hydration failure was swallowed by a
|
|
23
|
+
// bare catch, so the later dispatch refusal claimed no binding existed
|
|
24
|
+
// without ever admitting hydration had been attempted and why it failed.
|
|
25
|
+
|
|
26
|
+
const SHA = `sha256:${"1".repeat(64)}`;
|
|
27
|
+
|
|
28
|
+
function git(cwd: string, ...arguments_: string[]): string {
|
|
29
|
+
return execFileSync("git", arguments_, { cwd, encoding: "utf8" }).trim();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A LINKED worktree (not the primary checkout) with dirty tracked files. */
|
|
33
|
+
function linkedDirtyWorktree(t: test.TestContext): string {
|
|
34
|
+
const root = mkdtempSync(join(tmpdir(), "gentle-pi-hydration-gap-"));
|
|
35
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
36
|
+
const primary = join(root, "primary");
|
|
37
|
+
const linked = join(root, "linked");
|
|
38
|
+
mkdirSync(primary, { recursive: true });
|
|
39
|
+
git(primary, "init", "-b", "main");
|
|
40
|
+
git(primary, "config", "user.name", "Hydration Test");
|
|
41
|
+
git(primary, "config", "user.email", "hydration@example.invalid");
|
|
42
|
+
mkdirSync(join(primary, "internal"), { recursive: true });
|
|
43
|
+
writeFileSync(join(primary, "internal", "parser.go"), "package internal\n\nfunc Parse(s string) string {\n\treturn s\n}\n");
|
|
44
|
+
git(primary, "add", "-A");
|
|
45
|
+
git(primary, "commit", "-m", "base");
|
|
46
|
+
git(primary, "worktree", "add", linked, "-b", "feature/fence-info");
|
|
47
|
+
// Uncommitted tracked modification, exactly the field shape.
|
|
48
|
+
writeFileSync(join(linked, "internal", "parser.go"), "package internal\n\nimport \"strings\"\n\nfunc Parse(s string) string {\n\treturn strings.TrimSpace(s)\n}\n");
|
|
49
|
+
return linked;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function reviewerResultCollectInput(lineageId: string, lens: string): ReviewCollectInputV3 {
|
|
53
|
+
return {
|
|
54
|
+
name: "reviewer_result",
|
|
55
|
+
schema: "https://gentle-ai.dev/schema/review/reviewer/v1",
|
|
56
|
+
captureOperation: "review.capture-result",
|
|
57
|
+
arguments: [
|
|
58
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
59
|
+
{ name: "expected-revision", value: SHA, token: `--expected-revision=${SHA}` },
|
|
60
|
+
{ name: "lens", value: lens, token: `--lens=${lens}` },
|
|
61
|
+
{ name: "order", value: "0", token: "--order=0" },
|
|
62
|
+
],
|
|
63
|
+
artifactSubject: {
|
|
64
|
+
schema: "gentle-ai.review-artifact-subject/v2",
|
|
65
|
+
subjectHash: SHA,
|
|
66
|
+
lineageId,
|
|
67
|
+
authorityRevision: SHA,
|
|
68
|
+
targetIdentity: SHA,
|
|
69
|
+
baseTree: "3".repeat(40),
|
|
70
|
+
candidateTree: "4".repeat(40),
|
|
71
|
+
changedPathManifestSha256: SHA,
|
|
72
|
+
lens,
|
|
73
|
+
selectedOrder: 0,
|
|
74
|
+
},
|
|
75
|
+
} as unknown as ReviewCollectInputV3;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function successorStatus(lineageId: string, projection: { baseTree: string; currentCandidateTree: string; paths: readonly string[] }): ReviewStatusV3 {
|
|
79
|
+
return {
|
|
80
|
+
contract: "gentle-ai.review-integration/v2",
|
|
81
|
+
applicability: "current_target",
|
|
82
|
+
authority: { version: "compact-v2", lineageId, state: "reviewer_results_required", generation: 2, revision: SHA },
|
|
83
|
+
receipt: { status: "expected_missing" },
|
|
84
|
+
action: "finalize",
|
|
85
|
+
replayability: "not_replayable",
|
|
86
|
+
targetIdentity: SHA,
|
|
87
|
+
projection: {
|
|
88
|
+
schema: "gentle-ai.review-integration.projection/v1",
|
|
89
|
+
kind: "current-changes",
|
|
90
|
+
projection: "workspace",
|
|
91
|
+
baseTree: projection.baseTree,
|
|
92
|
+
initialReviewTree: projection.currentCandidateTree,
|
|
93
|
+
currentCandidateTree: projection.currentCandidateTree,
|
|
94
|
+
pathsDigest: SHA,
|
|
95
|
+
paths: [...projection.paths],
|
|
96
|
+
intendedUntracked: [],
|
|
97
|
+
intendedUntrackedProof: SHA,
|
|
98
|
+
initialSnapshotIdentity: SHA,
|
|
99
|
+
currentSnapshotIdentity: SHA,
|
|
100
|
+
},
|
|
101
|
+
candidates: [],
|
|
102
|
+
nextTransition: { kind: "collect", reasonCode: "reviewer_results_required", collect: { inputs: [reviewerResultCollectInput(lineageId, "review-reliability")] } },
|
|
103
|
+
raw: { schema: "gentle-ai.review-integration.status/v5", action: "finalize", lineage_id: lineageId },
|
|
104
|
+
} as unknown as ReviewStatusV3;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The live frozen identity of the dirty linked worktree candidate. */
|
|
108
|
+
function liveProjection(cwd: string): { baseTree: string; currentCandidateTree: string; paths: readonly string[] } {
|
|
109
|
+
const probe = new CandidateViewRegistry();
|
|
110
|
+
const view = probe.create({ contributorRoot: cwd });
|
|
111
|
+
try {
|
|
112
|
+
return { baseTree: view.baseTree, currentCandidateTree: view.candidateTree, paths: view.paths };
|
|
113
|
+
} finally {
|
|
114
|
+
probe.cleanup(view.token);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function statusNative(status: ReviewStatusV3): NativeReviewCli {
|
|
119
|
+
return {
|
|
120
|
+
targetStatus: async () => status,
|
|
121
|
+
finalize: async () => { throw new Error("native finalize must not run while reviewer results are outstanding"); },
|
|
122
|
+
} as unknown as NativeReviewCli;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function runController(parameters: Record<string, unknown>, cwd: string, native: NativeReviewCli, candidateViews: CandidateViewRegistry): Promise<Record<string, unknown>> {
|
|
126
|
+
return await __testing.executeReviewControllerOperation(
|
|
127
|
+
parameters, cwd, native, undefined, candidateViews,
|
|
128
|
+
) as Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
test("STATUS keeps hydrating and stays read-only when hydration fails", async (t) => {
|
|
132
|
+
const cwd = linkedDirtyWorktree(t);
|
|
133
|
+
const lineageId = "status-drifted-successor";
|
|
134
|
+
const registry = new CandidateViewRegistry();
|
|
135
|
+
const statusLive = liveProjection(cwd);
|
|
136
|
+
const drifted = { ...statusLive, currentCandidateTree: statusLive.baseTree };
|
|
137
|
+
// STATUS must never fail because hydration failed.
|
|
138
|
+
const envelope = await runController({ operation: "status", lineageId }, cwd, statusNative(successorStatus(lineageId, drifted)), registry);
|
|
139
|
+
assert.equal(envelope.operation, "status");
|
|
140
|
+
assert.equal(registry.hasCurrentBinding(), false);
|
|
141
|
+
assert.throws(
|
|
142
|
+
() => injectReviewCandidateView({ agent: "review-reliability", task: "review", mode: "task" }, registry),
|
|
143
|
+
(error: unknown) => error instanceof CandidateViewError && error.reason === "current-binding-hydration-failed",
|
|
144
|
+
);
|
|
145
|
+
});
|
|
@@ -4,7 +4,6 @@ import { chmodSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:f
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import test from "node:test";
|
|
7
|
-
import { __testing } from "../extensions/gentle-ai.ts";
|
|
8
7
|
import {
|
|
9
8
|
EXTERNAL_RELEASE_EVIDENCE,
|
|
10
9
|
GATE_RESULT,
|
|
@@ -175,14 +174,6 @@ function temporaryAuthority(t: test.TestContext): GateRepository & {
|
|
|
175
174
|
return { ...repository, store, receipt: receiptFor(state), authoritativeReceipt: store.createAuthoritativeReceipt("approved-lineage") };
|
|
176
175
|
}
|
|
177
176
|
|
|
178
|
-
test("lifecycle command classification identifies gates but never runs review routing", () => {
|
|
179
|
-
assert.equal(__testing.classifyReviewEvent("git commit -m fix"), "pre-commit");
|
|
180
|
-
assert.equal(__testing.classifyReviewEvent("git -C /repo push origin main"), "pre-push");
|
|
181
|
-
assert.equal(__testing.classifyReviewEvent("gh pr create --draft"), "pre-pr");
|
|
182
|
-
assert.equal(__testing.classifyReviewEvent("gh release create v1.2.3"), "pre-release");
|
|
183
|
-
assert.equal(__testing.classifyReviewEvent("git status"), null);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
177
|
test("unbranded receipts are rejected before lifecycle gate evaluation", (t) => {
|
|
187
178
|
const { repository, finalTree, store, receipt } = temporaryAuthority(t);
|
|
188
179
|
execFileSync("git", ["read-tree", finalTree], { cwd: repository });
|
|
@@ -589,42 +580,6 @@ test("scope child claim and parent gate journal publish atomically across faults
|
|
|
589
580
|
assert.equal(store.read(receipt.body.lineage_id).child_claims?.length, 1);
|
|
590
581
|
});
|
|
591
582
|
|
|
592
|
-
test("receipt gate cannot bypass independent dangerous-command safety", async () => {
|
|
593
|
-
let safetyCalls = 0;
|
|
594
|
-
let gateCalls = 0;
|
|
595
|
-
const safetyBlock = { block: true, reason: "dangerous command denied" };
|
|
596
|
-
const result = await __testing.enforceReviewGateAndCommandSafety(
|
|
597
|
-
"git push origin main",
|
|
598
|
-
() => {
|
|
599
|
-
gateCalls += 1;
|
|
600
|
-
return undefined;
|
|
601
|
-
},
|
|
602
|
-
async () => {
|
|
603
|
-
safetyCalls += 1;
|
|
604
|
-
return safetyBlock;
|
|
605
|
-
},
|
|
606
|
-
);
|
|
607
|
-
assert.deepEqual(result, safetyBlock);
|
|
608
|
-
assert.equal(safetyCalls, 1);
|
|
609
|
-
assert.equal(gateCalls, 0);
|
|
610
|
-
|
|
611
|
-
const gateBlock = { block: true, reason: "exact receipt required" };
|
|
612
|
-
const blocked = await __testing.enforceReviewGateAndCommandSafety(
|
|
613
|
-
"git push origin main",
|
|
614
|
-
() => {
|
|
615
|
-
gateCalls += 1;
|
|
616
|
-
return gateBlock;
|
|
617
|
-
},
|
|
618
|
-
async () => {
|
|
619
|
-
safetyCalls += 1;
|
|
620
|
-
return undefined;
|
|
621
|
-
},
|
|
622
|
-
);
|
|
623
|
-
assert.deepEqual(blocked, gateBlock);
|
|
624
|
-
assert.equal(safetyCalls, 2);
|
|
625
|
-
assert.equal(gateCalls, 1);
|
|
626
|
-
});
|
|
627
|
-
|
|
628
583
|
function releaseTarget(repository: GateRepository): GateTargetV1 {
|
|
629
584
|
return {
|
|
630
585
|
kind: GATE_TARGET_KIND.RELEASE,
|