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
|
@@ -13,7 +13,7 @@ function scratchDir(prefix: string): string {
|
|
|
13
13
|
|
|
14
14
|
async function runRetiredOperation(parameters: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
15
15
|
const cwd = scratchDir("gentle-pi-retired-ops-");
|
|
16
|
-
return await __testing.executeReviewControllerOperation(parameters, cwd,
|
|
16
|
+
return await __testing.executeReviewControllerOperation(parameters, cwd, null);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function assertRetiredEnvelope(details: Record<string, unknown>, operation: string): void {
|
|
@@ -5,7 +5,7 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import test from "node:test";
|
|
7
7
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
import { createGentleAiExtension } from "../extensions/gentle-ai.ts";
|
|
8
|
+
import { __testing, createGentleAiExtension } from "../extensions/gentle-ai.ts";
|
|
9
9
|
import type { NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
10
10
|
import { CandidateViewRegistry } from "../lib/review-candidate-view.ts";
|
|
11
11
|
import type { AuthorityRepairAssessmentV1, ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
@@ -55,8 +55,13 @@ function context(cwd: string): ExtensionContext {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function repository(t: test.TestContext, prefix = "gentle-pi-workspace-root-"): string {
|
|
58
|
-
const cwd = mkdtempSync(join(tmpdir(), prefix));
|
|
59
|
-
t.after(() =>
|
|
58
|
+
const cwd = realpathSync(mkdtempSync(join(tmpdir(), prefix)));
|
|
59
|
+
t.after(() => {
|
|
60
|
+
if (process.platform !== "win32") {
|
|
61
|
+
try { execFileSync("chmod", ["-R", "u+w", cwd], { stdio: "ignore" }); } catch { /* best effort */ }
|
|
62
|
+
}
|
|
63
|
+
rmSync(cwd, { recursive: true, force: true });
|
|
64
|
+
});
|
|
60
65
|
execFileSync("git", ["init", "-b", "main"], { cwd });
|
|
61
66
|
writeFileSync(join(cwd, "app.ts"), "export const value = 1;\n");
|
|
62
67
|
execFileSync("git", ["add", "."], { cwd });
|
|
@@ -65,7 +70,7 @@ function repository(t: test.TestContext, prefix = "gentle-pi-workspace-root-"):
|
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
function addWorktree(t: test.TestContext, cwd: string, branch: string): string {
|
|
68
|
-
const parent = mkdtempSync(join(tmpdir(), "gentle-pi-workspace-worktrees-"));
|
|
73
|
+
const parent = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-workspace-worktrees-")));
|
|
69
74
|
t.after(() => {
|
|
70
75
|
try { execFileSync("git", ["worktree", "remove", "--force", join(parent, branch)], { cwd }); } catch {}
|
|
71
76
|
rmSync(parent, { recursive: true, force: true });
|
|
@@ -78,14 +83,11 @@ function addWorktree(t: test.TestContext, cwd: string, branch: string): string {
|
|
|
78
83
|
function fakeNative(overrides: Partial<NativeReviewCli> = {}): NativeReviewCli {
|
|
79
84
|
return {
|
|
80
85
|
start: async () => ({ lineageId: "native-lineage", state: "reviewing", riskLevel: "medium", selectedLenses: ["review-reliability"], changedFiles: 2, changedLines: 7, correctionBudget: 4, action: "created", lensesRequired: true }),
|
|
81
|
-
finalize: async () => ({ lineageId: "native-lineage", state: "approved", action: "approved", storeRevision: "r1", receiptPath: "/opaque/receipt" }),
|
|
82
|
-
validate: async () => { throw new Error("validate is not expected in workspace-root tests"); },
|
|
83
|
-
bindSdd: async () => { throw new Error("bind-sdd is not expected in workspace-root tests"); },
|
|
84
86
|
sddStatus: async () => ({ ready: false }),
|
|
85
87
|
reviewStatus: async () => ({ schema: "gentle-ai.review-authority-status/v1", repository: "/repo", complete: true, authoritative: true, status: "clean", entries: [], locks: [], diagnostics: [], raw: { schema: "gentle-ai.review-authority-status/v1", operation: "review/status", repository: "/repo", complete: true, authoritative: true, status: "clean", entries: [], locks: [], diagnostics: [] } }),
|
|
86
88
|
targetStatus: async (request) => request.lineageId === undefined
|
|
87
89
|
? candidateStartTargetStatus(request)
|
|
88
|
-
:
|
|
90
|
+
: candidateTargetStatus(request, request.lineageId),
|
|
89
91
|
...overrides,
|
|
90
92
|
};
|
|
91
93
|
}
|
|
@@ -102,17 +104,20 @@ function targetStatusFixture(options: {
|
|
|
102
104
|
applicability?: "current_target" | "unrelated";
|
|
103
105
|
action?: ReviewStatusV3["action"];
|
|
104
106
|
lineageId?: string;
|
|
107
|
+
authorityState?: NonNullable<ReviewStatusV3["authority"]>["state"];
|
|
105
108
|
baseTree?: string;
|
|
106
109
|
currentCandidateTree?: string;
|
|
107
110
|
paths?: readonly string[];
|
|
111
|
+
intendedUntracked?: readonly string[];
|
|
108
112
|
} = {}): ReviewStatusV3 {
|
|
109
113
|
const applicability = options.applicability ?? "current_target";
|
|
110
|
-
const action = options.action ?? (applicability === "current_target" ? "
|
|
111
|
-
const
|
|
114
|
+
const action = options.action ?? (applicability === "current_target" ? "stop" : "start"); const lineageId = options.lineageId ?? "native-lineage";
|
|
115
|
+
const authorityState = options.authorityState ?? "reviewing";
|
|
112
116
|
const sha = `sha256:${"a".repeat(64)}`;
|
|
113
117
|
const tree = options.currentCandidateTree ?? "b".repeat(40);
|
|
114
118
|
const baseTree = options.baseTree ?? tree;
|
|
115
119
|
const paths = options.paths ?? ["app.ts"];
|
|
120
|
+
const intendedUntracked = options.intendedUntracked ?? [];
|
|
116
121
|
const projection = {
|
|
117
122
|
schema: "gentle-ai.review-integration.projection/v1" as const,
|
|
118
123
|
kind: "current-changes" as const,
|
|
@@ -122,7 +127,7 @@ function targetStatusFixture(options: {
|
|
|
122
127
|
currentCandidateTree: tree,
|
|
123
128
|
pathsDigest: sha,
|
|
124
129
|
paths,
|
|
125
|
-
intendedUntracked
|
|
130
|
+
intendedUntracked,
|
|
126
131
|
intendedUntrackedProof: sha,
|
|
127
132
|
initialSnapshotIdentity: sha,
|
|
128
133
|
currentSnapshotIdentity: sha,
|
|
@@ -156,7 +161,7 @@ function targetStatusFixture(options: {
|
|
|
156
161
|
current_candidate_tree: tree,
|
|
157
162
|
paths_digest: sha,
|
|
158
163
|
paths,
|
|
159
|
-
intended_untracked:
|
|
164
|
+
intended_untracked: intendedUntracked,
|
|
160
165
|
intended_untracked_proof: sha,
|
|
161
166
|
initial_snapshot_identity: sha,
|
|
162
167
|
current_snapshot_identity: sha,
|
|
@@ -164,13 +169,13 @@ function targetStatusFixture(options: {
|
|
|
164
169
|
candidates: [],
|
|
165
170
|
};
|
|
166
171
|
if (applicability === "current_target") {
|
|
167
|
-
raw.authority = { version: "compact-v2", lineage_id: lineageId, state:
|
|
172
|
+
raw.authority = { version: "compact-v2", lineage_id: lineageId, state: authorityState, generation: 1, revision: sha };
|
|
168
173
|
raw.frozen = { tier: "medium", original_changed_lines: 2, correction_budget: 1 };
|
|
169
174
|
}
|
|
170
175
|
return {
|
|
171
176
|
contract: "gentle-ai.review-integration/v2",
|
|
172
177
|
applicability,
|
|
173
|
-
...(applicability === "current_target" ? { authority: { version: "compact-v2" as const, lineageId, state:
|
|
178
|
+
...(applicability === "current_target" ? { authority: { version: "compact-v2" as const, lineageId, state: authorityState, generation: 1, revision: sha } } : {}),
|
|
174
179
|
receipt: { status: applicability === "current_target" ? "expected_missing" : "not_applicable" },
|
|
175
180
|
action,
|
|
176
181
|
replayability: "not_replayable",
|
|
@@ -189,6 +194,7 @@ function candidateStartTargetStatus(request: Parameters<NonNullable<NativeReview
|
|
|
189
194
|
candidate = new CandidateViewRegistry().create({
|
|
190
195
|
contributorRoot: request.cwd,
|
|
191
196
|
...(request.baseRef === undefined ? {} : { baseRef: request.baseRef, committedOnly: true }),
|
|
197
|
+
...(request.intendedUntracked === undefined ? {} : { intendedUntracked: request.intendedUntracked }),
|
|
192
198
|
});
|
|
193
199
|
return targetStatusFixture({
|
|
194
200
|
applicability: "unrelated",
|
|
@@ -196,6 +202,22 @@ function candidateStartTargetStatus(request: Parameters<NonNullable<NativeReview
|
|
|
196
202
|
baseTree: candidate.baseTree,
|
|
197
203
|
currentCandidateTree: candidate.candidateTree,
|
|
198
204
|
paths: candidate.paths,
|
|
205
|
+
intendedUntracked: request.intendedUntracked,
|
|
206
|
+
});
|
|
207
|
+
} finally {
|
|
208
|
+
candidate?.cleanup();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function candidateTargetStatus(request: Parameters<NonNullable<NativeReviewCli["targetStatus"]>>[0], lineageId: string): ReviewStatusV3 {
|
|
213
|
+
let candidate: ReturnType<CandidateViewRegistry["create"]> | undefined;
|
|
214
|
+
try {
|
|
215
|
+
candidate = new CandidateViewRegistry().create({ contributorRoot: request.cwd });
|
|
216
|
+
return targetStatusFixture({
|
|
217
|
+
lineageId,
|
|
218
|
+
baseTree: candidate.baseTree,
|
|
219
|
+
currentCandidateTree: candidate.candidateTree,
|
|
220
|
+
paths: candidate.paths,
|
|
199
221
|
});
|
|
200
222
|
} finally {
|
|
201
223
|
candidate?.cleanup();
|
|
@@ -255,39 +277,164 @@ test("START freezes the candidate from the explicit workspace root and returns t
|
|
|
255
277
|
candidateViews.cleanup(view.token);
|
|
256
278
|
});
|
|
257
279
|
|
|
258
|
-
test("
|
|
259
|
-
const
|
|
260
|
-
|
|
280
|
+
test("an omitted workspaceRoot canonicalizes a nested same-worktree session before supported operations", async (t) => {
|
|
281
|
+
const root = repository(t);
|
|
282
|
+
const sessionCwd = join(root, "nested");
|
|
283
|
+
mkdirSync(sessionCwd);
|
|
284
|
+
writeFileSync(join(root, "app.ts"), "export const value = 3;\n");
|
|
261
285
|
const candidateViews = new CandidateViewRegistry();
|
|
286
|
+
const targetStatusCwds: string[] = [];
|
|
262
287
|
const startRequests: Parameters<NativeReviewCli["start"]>[0][] = [];
|
|
263
288
|
const { controller } = runtime(fakeNative({
|
|
289
|
+
targetStatus: async (request) => { targetStatusCwds.push(request.cwd); return request.lineageId === undefined ? candidateStartTargetStatus(request) : candidateTargetStatus(request, request.lineageId); },
|
|
264
290
|
start: async (request) => {
|
|
265
291
|
startRequests.push(request);
|
|
266
292
|
return { lineageId: "session-lineage", state: "reviewing", riskLevel: "medium", selectedLenses: ["review-reliability"], changedFiles: 1, changedLines: 1, correctionBudget: 1, action: "created", lensesRequired: true };
|
|
267
293
|
},
|
|
268
294
|
}), candidateViews);
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
};
|
|
274
|
-
assert.equal(details.workspace_root,
|
|
275
|
-
assert.equal(details.actor_binding.workspace_root,
|
|
295
|
+
const ctx = context(sessionCwd);
|
|
296
|
+
await controller.execute("inspect-session", { operation: "inspect" }, undefined, undefined, ctx);
|
|
297
|
+
const started = await controller.execute("start-session", { operation: "start", input: JSON.stringify({ mode: "ordinary" }) }, undefined, undefined, ctx);
|
|
298
|
+
const status = await controller.execute("status-session", { operation: "status", lineageId: "session-lineage" }, undefined, undefined, ctx);
|
|
299
|
+
const details = started.details as { workspace_root: string; actor_binding: { workspace_root: string; candidate_root: string; candidate_paths: readonly string[] } };
|
|
300
|
+
assert.equal(details.workspace_root, root);
|
|
301
|
+
assert.equal(details.actor_binding.workspace_root, root);
|
|
302
|
+
assert.equal((status.details as { workspace_root: string }).workspace_root, root);
|
|
276
303
|
assert.deepEqual(details.actor_binding.candidate_paths, ["app.ts"]);
|
|
277
|
-
assert.deepEqual(startRequests, [{ cwd:
|
|
304
|
+
assert.deepEqual(startRequests, [{ cwd: root, targetIdentity: `sha256:${"a".repeat(64)}`, projection: "workspace" }]);
|
|
278
305
|
const view = candidateViews.resolveForLens("session-lineage", "review-reliability");
|
|
279
306
|
assert.equal(details.actor_binding.candidate_root, view.root);
|
|
307
|
+
assert.ok(targetStatusCwds.every((cwd) => cwd === root), JSON.stringify(targetStatusCwds));
|
|
280
308
|
candidateViews.cleanup(view.token);
|
|
281
309
|
});
|
|
282
310
|
|
|
283
|
-
test("
|
|
311
|
+
test("an explicit nested foreign workspace root canonically owns inspect, status, and START", async (t) => {
|
|
312
|
+
const sessionCwd = repository(t, "gentle-pi-session-a-");
|
|
313
|
+
const target = repository(t, "gentle-pi-target-b-");
|
|
314
|
+
const nested = join(target, "nested");
|
|
315
|
+
mkdirSync(nested);
|
|
316
|
+
writeFileSync(join(target, "app.ts"), "export const value = 2; // target B\n");
|
|
317
|
+
const root = realpathSync(target);
|
|
318
|
+
const targetStatusCwds: string[] = [];
|
|
319
|
+
const targetStatusRequests: Parameters<NonNullable<NativeReviewCli["targetStatus"]>>[0][] = [];
|
|
320
|
+
const startCwds: string[] = [];
|
|
321
|
+
const candidateViews = new CandidateViewRegistry();
|
|
322
|
+
let initialCrossRootStatus = true;
|
|
323
|
+
const { controller } = runtime(fakeNative({
|
|
324
|
+
targetStatus: async (request) => {
|
|
325
|
+
targetStatusCwds.push(request.cwd);
|
|
326
|
+
targetStatusRequests.push(request);
|
|
327
|
+
if (request.lineageId === "cross-root-lineage" && initialCrossRootStatus) {
|
|
328
|
+
initialCrossRootStatus = false;
|
|
329
|
+
return request.untrackedScope === "select"
|
|
330
|
+
? targetStatusFixture({ lineageId: request.lineageId, authorityState: "correction_required" })
|
|
331
|
+
: targetStatusFixture({ applicability: "unrelated", action: "start" });
|
|
332
|
+
}
|
|
333
|
+
return request.lineageId === undefined
|
|
334
|
+
? candidateStartTargetStatus(request)
|
|
335
|
+
: candidateTargetStatus(request, request.lineageId);
|
|
336
|
+
},
|
|
337
|
+
start: async (request) => {
|
|
338
|
+
startCwds.push(request.cwd);
|
|
339
|
+
return { lineageId: "cross-root-lineage", state: "reviewing", riskLevel: "medium", selectedLenses: ["review-reliability"], changedFiles: 1, changedLines: 1, correctionBudget: 1, action: "created", lensesRequired: true };
|
|
340
|
+
},
|
|
341
|
+
}), candidateViews);
|
|
342
|
+
|
|
343
|
+
const digest = `sha256:${"b".repeat(64)}`;
|
|
344
|
+
const intendedUntracked = ["selected-b.ts", "selected-a.ts"];
|
|
345
|
+
const inspect = await controller.execute("inspect-target-b", { operation: "inspect", workspaceRoot: nested }, undefined, undefined, context(sessionCwd));
|
|
346
|
+
const status = await controller.execute("status-target-b", { operation: "status", lineageId: "cross-root-lineage", workspaceRoot: nested, input: JSON.stringify({ untrackedScope: "select", expectedUntrackedInventory: digest, intendedUntracked }) }, undefined, undefined, context(sessionCwd));
|
|
347
|
+
const started = await controller.execute("start-target-b", { operation: "start", workspaceRoot: nested, input: JSON.stringify({ mode: "ordinary" }) }, undefined, undefined, context(sessionCwd));
|
|
348
|
+
|
|
349
|
+
for (const result of [inspect, status, started]) {
|
|
350
|
+
assert.equal((result.details as { workspace_root?: string }).workspace_root, root);
|
|
351
|
+
}
|
|
352
|
+
const statusDetails = status.details as { status?: string; result?: { authority?: { state?: string } } };
|
|
353
|
+
assert.equal(statusDetails.status, "blocked");
|
|
354
|
+
assert.equal(statusDetails.result?.authority?.state, "correction_required");
|
|
355
|
+
assert.deepEqual(targetStatusRequests[1], { cwd: root, lineageId: "cross-root-lineage", untrackedScope: "select", expectedUntrackedInventory: digest, intendedUntracked, agent: "pi" });
|
|
356
|
+
assert.ok(targetStatusCwds.every((cwd) => cwd === root), JSON.stringify(targetStatusCwds));
|
|
357
|
+
assert.deepEqual(startCwds, [root]);
|
|
358
|
+
candidateViews.cleanup(candidateViews.resolveForLens("cross-root-lineage", "review-reliability").token);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
test("STATUS relays exclude selection and rejects malformed input before native status", async (t) => {
|
|
363
|
+
const cwd = repository(t);
|
|
364
|
+
const requests: Parameters<NonNullable<NativeReviewCli["targetStatus"]>>[0][] = [];
|
|
365
|
+
const { controller } = runtime(fakeNative({
|
|
366
|
+
targetStatus: async (request) => {
|
|
367
|
+
requests.push(request);
|
|
368
|
+
return targetStatusFixture({ lineageId: request.lineageId });
|
|
369
|
+
},
|
|
370
|
+
}));
|
|
371
|
+
const digest = `sha256:${"c".repeat(64)}`;
|
|
372
|
+
const excluded = await controller.execute("status-exclude", { operation: "status", lineageId: "excluded-lineage", input: JSON.stringify({ untrackedScope: "exclude", expectedUntrackedInventory: digest, intendedUntracked: [] }) }, undefined, undefined, context(cwd));
|
|
373
|
+
assert.equal((excluded.details as { status?: string }).status, "blocked");
|
|
374
|
+
assert.deepEqual(requests, [{ cwd, lineageId: "excluded-lineage", untrackedScope: "exclude", expectedUntrackedInventory: digest, intendedUntracked: [], agent: "pi" }]);
|
|
375
|
+
|
|
376
|
+
for (const [input, reason] of [
|
|
377
|
+
[{}, "untracked-selection-invalid"],
|
|
378
|
+
[{ untrackedScope: "select", expectedUntrackedInventory: digest, intendedUntracked: [] }, "untracked-selection-invalid"],
|
|
379
|
+
[{ untrackedScope: "select" }, "untracked-selection-invalid"],
|
|
380
|
+
[{ untrackedScope: "exclude", expectedUntrackedInventory: digest, intendedUntracked: [], unexpected: true }, "unknown-field"],
|
|
381
|
+
] as const) {
|
|
382
|
+
const rejected = await controller.execute("status-invalid", { operation: "status", input: JSON.stringify(input) }, undefined, undefined, context(cwd));
|
|
383
|
+
assert.equal((rejected.details as { reason?: string }).reason, reason);
|
|
384
|
+
}
|
|
385
|
+
assert.equal(requests.length, 1);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test("an explicit foreign workspace root works when the Pi session cwd is not a Git repository", async (t) => {
|
|
389
|
+
const target = repository(t, "gentle-pi-target-b-non-git-session-");
|
|
390
|
+
const nested = join(target, "nested");
|
|
391
|
+
mkdirSync(nested);
|
|
392
|
+
const nonGit = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-non-git-session-")));
|
|
393
|
+
t.after(() => rmSync(nonGit, { recursive: true, force: true }));
|
|
394
|
+
const observed: string[] = [];
|
|
395
|
+
const { controller } = runtime(fakeNative({
|
|
396
|
+
targetStatus: async (request) => {
|
|
397
|
+
observed.push(request.cwd);
|
|
398
|
+
return targetStatusFixture();
|
|
399
|
+
},
|
|
400
|
+
}));
|
|
401
|
+
const result = await controller.execute("inspect-target-from-non-git", { operation: "inspect", workspaceRoot: nested }, undefined, undefined, context(nonGit));
|
|
402
|
+
assert.equal((result.details as { workspace_root?: string }).workspace_root, realpathSync(target));
|
|
403
|
+
assert.deepEqual(observed, [realpathSync(target)]);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("omitted workspaceRoot fails closed for the same lineage bound to two target repositories", async (t) => {
|
|
407
|
+
const rootA = repository(t, "gentle-pi-ambiguous-a-");
|
|
408
|
+
const rootB = repository(t, "gentle-pi-ambiguous-b-");
|
|
409
|
+
const candidateViews = new CandidateViewRegistry();
|
|
410
|
+
const viewA = candidateViews.create({ contributorRoot: rootA });
|
|
411
|
+
const viewB = candidateViews.create({ contributorRoot: rootB });
|
|
412
|
+
candidateViews.bindCurrent({ token: viewA.token, lineageId: "shared-lineage", selectedLenses: ["review-reliability"] });
|
|
413
|
+
candidateViews.bindCurrent({ token: viewB.token, lineageId: "shared-lineage", selectedLenses: ["review-reliability"] });
|
|
414
|
+
let nativeCalls = 0;
|
|
415
|
+
const { controller } = runtime(fakeNative({
|
|
416
|
+
targetStatus: async (request) => {
|
|
417
|
+
nativeCalls += 1;
|
|
418
|
+
return targetStatusFixture({ lineageId: request.lineageId });
|
|
419
|
+
},
|
|
420
|
+
}), candidateViews);
|
|
421
|
+
await assert.rejects(
|
|
422
|
+
controller.execute("ambiguous-omission", { operation: "status", lineageId: "shared-lineage" }, undefined, undefined, context(rootA)),
|
|
423
|
+
/workspaceRoot/,
|
|
424
|
+
);
|
|
425
|
+
assert.equal(nativeCalls, 0);
|
|
426
|
+
const explicit = await controller.execute("explicit-b", { operation: "status", lineageId: "shared-lineage", workspaceRoot: rootB }, undefined, undefined, context(rootA));
|
|
427
|
+
assert.equal((explicit.details as { workspace_root?: string }).workspace_root, realpathSync(rootB));
|
|
428
|
+
assert.equal(nativeCalls, 1);
|
|
429
|
+
candidateViews.cleanupTerminal("shared-lineage", "approved", rootA);
|
|
430
|
+
candidateViews.cleanupTerminal("shared-lineage", "approved", rootB);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test("workspaceRoot fails closed before any native call for invalid target paths", async (t) => {
|
|
284
434
|
const sessionCwd = repository(t);
|
|
285
435
|
const worktree = addWorktree(t, sessionCwd, "feat-guard");
|
|
286
|
-
const
|
|
287
|
-
const nonGit = mkdtempSync(join(tmpdir(), "gentle-pi-non-git-"));
|
|
436
|
+
const nonGit = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-non-git-")));
|
|
288
437
|
t.after(() => rmSync(nonGit, { recursive: true, force: true }));
|
|
289
|
-
const nested = join(worktree, "nested");
|
|
290
|
-
mkdirSync(nested);
|
|
291
438
|
const filePath = join(worktree, "app.ts");
|
|
292
439
|
let nativeCalls = 0;
|
|
293
440
|
const counting = fakeNative({
|
|
@@ -296,16 +443,13 @@ test("workspaceRoot fails closed before any native call for non-worktree and for
|
|
|
296
443
|
reviewStatus: async () => { nativeCalls += 1; throw new Error("native review status must not run"); },
|
|
297
444
|
});
|
|
298
445
|
const { controller } = runtime(counting);
|
|
299
|
-
const rejected: Array<{ label: string; workspaceRoot: string
|
|
300
|
-
{ label: "non-git directory", workspaceRoot: nonGit
|
|
301
|
-
{ label: "missing directory", workspaceRoot: join(nonGit, "missing")
|
|
302
|
-
{ label: "file path", workspaceRoot: filePath
|
|
303
|
-
{ label: "relative path", workspaceRoot: "relative/worktree"
|
|
304
|
-
{ label: "worktree subdirectory", workspaceRoot: nested, ctx: sessionCwd },
|
|
305
|
-
{ label: "foreign repository", workspaceRoot: foreign, ctx: sessionCwd },
|
|
306
|
-
{ label: "session outside a repository", workspaceRoot: worktree, ctx: nonGit },
|
|
446
|
+
const rejected: Array<{ label: string; workspaceRoot: string }> = [
|
|
447
|
+
{ label: "non-git directory", workspaceRoot: nonGit },
|
|
448
|
+
{ label: "missing directory", workspaceRoot: join(nonGit, "missing") },
|
|
449
|
+
{ label: "file path", workspaceRoot: filePath },
|
|
450
|
+
{ label: "relative path", workspaceRoot: "relative/worktree" },
|
|
307
451
|
];
|
|
308
|
-
for (const { label, workspaceRoot
|
|
452
|
+
for (const { label, workspaceRoot } of rejected) {
|
|
309
453
|
for (const operation of ["inspect", "start", "status"] as const) {
|
|
310
454
|
await assert.rejects(
|
|
311
455
|
controller.execute(`${operation}-${label}`, {
|
|
@@ -313,7 +457,7 @@ test("workspaceRoot fails closed before any native call for non-worktree and for
|
|
|
313
457
|
...(operation === "start" ? { input: JSON.stringify({ mode: "ordinary" }) } : {}),
|
|
314
458
|
...(operation === "status" ? { lineageId: "native-lineage" } : {}),
|
|
315
459
|
workspaceRoot,
|
|
316
|
-
}, undefined, undefined, context(
|
|
460
|
+
}, undefined, undefined, context(sessionCwd)),
|
|
317
461
|
/workspaceRoot/,
|
|
318
462
|
`${operation} must fail closed for ${label}`,
|
|
319
463
|
);
|
|
@@ -322,40 +466,62 @@ test("workspaceRoot fails closed before any native call for non-worktree and for
|
|
|
322
466
|
assert.equal(nativeCalls, 0);
|
|
323
467
|
});
|
|
324
468
|
|
|
325
|
-
|
|
469
|
+
function providerRoleBinding(lineageId: string) {
|
|
470
|
+
return {
|
|
471
|
+
name: "provider_targeted_validator",
|
|
472
|
+
schema: "https://gentle-ai.dev/schema/review/targeted-validator/v1",
|
|
473
|
+
captureOperation: "review.capture-validation",
|
|
474
|
+
arguments: [
|
|
475
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
476
|
+
{ name: "target", value: `sha256:${"a".repeat(64)}`, token: `--target=sha256:${"a".repeat(64)}` },
|
|
477
|
+
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
478
|
+
{ name: "execute", value: "true", token: "--execute=true" },
|
|
479
|
+
],
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
test("same-session START binding migrates to one validation capture without a FINALIZE operation", async (t) => {
|
|
326
484
|
const sessionCwd = repository(t);
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
485
|
+
const worktree = addWorktree(t, sessionCwd, "feat-last-event");
|
|
486
|
+
writeFileSync(join(worktree, "app.ts"), "export const value = 2;\n");
|
|
487
|
+
const candidateViews = new CandidateViewRegistry();
|
|
488
|
+
const lineageId = "workspace-last-event";
|
|
489
|
+
const binding = providerRoleBinding(lineageId);
|
|
490
|
+
let captureCalls = 0;
|
|
491
|
+
const native = fakeNative({
|
|
492
|
+
targetStatus: async (request) => request.lineageId === undefined
|
|
493
|
+
? candidateStartTargetStatus(request)
|
|
494
|
+
: ({ ...candidateTargetStatus(request, lineageId), nextTransition: { kind: "collect", reasonCode: "provider_role_required", collect: { inputs: [binding] } } } as unknown as ReviewStatusV3),
|
|
495
|
+
start: async () => ({ lineageId, state: "reviewing", riskLevel: "medium", selectedLenses: ["review-reliability"], changedFiles: 1, changedLines: 1, correctionBudget: 1, action: "created", lensesRequired: true }),
|
|
496
|
+
captureProviderRole: async (request) => {
|
|
497
|
+
captureCalls += 1;
|
|
498
|
+
assert.equal(request.captureOperation, "review.capture-validation");
|
|
499
|
+
return { schema: "gentle-ai.review-last-event-closure/v1", operation: "review.capture-validation", lineageId, state: "approved", storeRevision: `sha256:${"a".repeat(64)}` };
|
|
336
500
|
},
|
|
337
|
-
);
|
|
501
|
+
});
|
|
502
|
+
const { controller } = runtime(native, candidateViews);
|
|
503
|
+
await controller.execute("start", { operation: "start", workspaceRoot: worktree, input: JSON.stringify({ mode: "ordinary" }) }, undefined, undefined, context(sessionCwd));
|
|
504
|
+
const captured = await __testing.executeReviewCaptureOperation({ lineageId, workspaceRoot: worktree, collectBinding: JSON.stringify(binding) }, sessionCwd, native, undefined, candidateViews);
|
|
505
|
+
assert.equal(captured.outcome, "native-last-event-closure");
|
|
506
|
+
assert.equal(captured.status, "closed");
|
|
507
|
+
assert.equal(captureCalls, 1);
|
|
508
|
+
candidateViews.cleanupTerminal(lineageId, "approved", realpathSync(worktree));
|
|
338
509
|
});
|
|
339
510
|
|
|
340
|
-
test("
|
|
511
|
+
test("an explicit linked-worktree root owns STATUS collect capture over the session candidate root", async (t) => {
|
|
341
512
|
const sessionCwd = repository(t);
|
|
342
|
-
const worktree = addWorktree(t, sessionCwd, "feat-
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return { lineageId: "finalize-lineage", state: "approved", action: "approved", storeRevision: "r1" };
|
|
513
|
+
const worktree = addWorktree(t, sessionCwd, "feat-explicit-capture");
|
|
514
|
+
const lineageId = "explicit-worktree-capture";
|
|
515
|
+
const binding = providerRoleBinding(lineageId);
|
|
516
|
+
const observedRoots: string[] = [];
|
|
517
|
+
const native = {
|
|
518
|
+
targetStatus: async (request: { cwd: string }) => {
|
|
519
|
+
observedRoots.push(request.cwd);
|
|
520
|
+
return { ...targetStatusFixture({ lineageId }), nextTransition: { kind: "collect", reasonCode: "provider_role_required", collect: { inputs: [binding] } } } as unknown as ReviewStatusV3;
|
|
351
521
|
},
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
assert.
|
|
357
|
-
assert.equal(finalizes, 0);
|
|
358
|
-
const matched = await controller.execute("finalize-right-root", { operation: "finalize", lineageId: "finalize-lineage", workspaceRoot: worktree, input }, undefined, undefined, context(sessionCwd));
|
|
359
|
-
assert.equal((matched.details as { result?: { state?: string } }).result?.state, "approved");
|
|
360
|
-
assert.equal(finalizes, 1);
|
|
522
|
+
captureProviderRole: async () => ({ schema: "gentle-ai.review-last-event-closure/v1", operation: "review.capture-validation", lineageId, state: "approved", storeRevision: `sha256:${"a".repeat(64)}` }),
|
|
523
|
+
} as unknown as NativeReviewCli;
|
|
524
|
+
const result = await __testing.executeReviewCaptureOperation({ lineageId, workspaceRoot: worktree, collectBinding: JSON.stringify(binding) }, sessionCwd, native);
|
|
525
|
+
assert.equal(result.outcome, "native-last-event-closure");
|
|
526
|
+
assert.deepEqual(observedRoots, [realpathSync(worktree)]);
|
|
361
527
|
});
|