gentle-pi 2.2.0 → 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 +51 -80
- package/assets/agents/review-readability.md +0 -2
- package/assets/agents/review-reliability.md +0 -2
- package/assets/agents/review-resilience.md +0 -2
- package/assets/agents/review-risk.md +0 -2
- package/assets/agents/sdd-apply.md +5 -3
- package/assets/agents/sdd-proposal.md +2 -0
- package/assets/agents/sdd-research.md +54 -0
- package/assets/agents/sdd-status.md +4 -4
- package/assets/agents/sdd-tasks.md +2 -3
- package/assets/agents/sdd-verify.md +21 -1
- package/assets/chains/sdd-full.chain.md +1 -1
- package/assets/chains/sdd-verify.chain.md +1 -1
- package/assets/orchestrator-delegation.md +54 -248
- package/assets/orchestrator-memory.md +2 -0
- package/assets/orchestrator.md +22 -38
- package/assets/sdd-orchestrator-workflow.md +30 -37
- package/assets/support/sdd-status-contract.md +7 -7
- package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
- 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/docs/native-authority-architecture.md +9 -11
- package/docs/review-integration.md +27 -373
- package/extensions/ask-user-choice.ts +151 -0
- package/extensions/gentle-ai.ts +1778 -3673
- package/extensions/quiet-tools.ts +515 -32
- package/extensions/sdd-init.ts +4 -8
- package/lib/gentle-ai-renderer.ts +70 -0
- package/lib/model-routing-authority.ts +133 -0
- package/lib/native-review-cli.ts +372 -940
- package/lib/opaque-pi-reviewer-adapter.ts +284 -0
- package/lib/review-candidate-view.ts +341 -132
- package/lib/review-host-relay.ts +210 -68
- package/lib/review-integration-v2.ts +839 -307
- package/lib/review-last-event-controller.ts +35 -0
- package/lib/sdd-preflight.ts +177 -66
- package/lib/sdd-status.ts +66 -111
- package/lib/terminal-theme.ts +1 -1
- package/package.json +83 -82
- package/runtime/gentle-ai-binary.mjs +1 -1
- package/runtime/native-review-cli.mjs +322 -890
- package/runtime/review-integration-v2.mjs +789 -257
- package/runtime/review-relay-contract.mjs +1 -1
- package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +4 -5
- package/scripts/gentle-ai-installer.mjs +75 -22
- package/scripts/maintainer/provider-relay-matrix.mjs +195 -11
- package/scripts/test-packed-runner.mjs +4 -7
- package/scripts/verify-package-files.mjs +12 -13
- package/skills/_shared/review-ledger-contract.md +8 -14
- 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/judgment-day/SKILL.md +5 -7
- package/skills/rdd-defect-workflow/SKILL.md +3 -3
- 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/codegraph-tools.test.ts +3 -3
- package/tests/crosslane/cross-lane.mjs +15 -1168
- package/tests/delegated-key-learnings-contract.test.ts +8 -6
- package/tests/devbinary/native-review-parity.devtest.ts +167 -250
- package/tests/devbinary/pi-host-relay.devtest.ts +867 -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/review-acknowledged-v1.captured.json +9 -0
- package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
- package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
- package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
- package/tests/gentle-ai-binary.test.ts +83 -13
- package/tests/gentle-ai-dev-binary.test.ts +51 -228
- package/tests/gentle-ai-installer.test.ts +137 -47
- package/tests/gentle-ai.test.ts +820 -131
- package/tests/gentle-theme.test.ts +133 -0
- package/tests/maintainer/provider-relay.maintest.ts +340 -4
- package/tests/model-routing-authority.test.ts +257 -0
- package/tests/native-review-capability-contract.test.ts +28 -1
- package/tests/native-review-cli.test.ts +586 -962
- package/tests/native-review-consent.test.ts +150 -66
- package/tests/native-review-parity-runtime.test.ts +80 -359
- package/tests/native-review-parity.test.ts +674 -925
- package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
- package/tests/orchestrator-budget.test.ts +132 -88
- package/tests/orchestrator-rdd-ownership.test.ts +103 -0
- package/tests/package-manifest.test.ts +37 -35
- package/tests/provider-defect-handoff.test.ts +15 -118
- package/tests/quiet-tool-rendering.test.ts +1055 -28
- package/tests/review-authority-recovery-docs.test.ts +1 -2
- package/tests/review-candidate-view.test.ts +409 -3
- package/tests/review-compact-contract.test.ts +4 -30
- package/tests/review-controller-lock-status.test.ts +2 -2
- package/tests/review-controller-native-recovery.test.ts +363 -1450
- package/tests/review-controller-native-routing.test.ts +1251 -5642
- package/tests/review-controller-retired-ops.test.ts +1 -1
- package/tests/review-controller-workspace-root.test.ts +222 -71
- package/tests/review-controller.test.ts +26 -816
- package/tests/review-corrected-finalize-binding.test.ts +112 -153
- package/tests/review-dispatch-hydration-gap.test.ts +1 -53
- 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 +117 -82
- package/tests/review-host-relay.test.ts +242 -8
- package/tests/review-integration-v2-forward.test.ts +576 -553
- package/tests/review-integration-v2.test.ts +239 -165
- package/tests/review-last-event-closure.test.ts +408 -0
- package/tests/review-ledger-contract.test.ts +97 -35
- package/tests/review-recovered-lineage-routing.test.ts +0 -47
- package/tests/review-relay-transport-agent.test.ts +119 -46
- package/tests/review-snapshot.test.ts +3 -2
- package/tests/runtime-harness.mjs +361 -196
- package/tests/sdd-agent-tools.test.ts +36 -0
- package/tests/sdd-preflight.test.ts +81 -15
- package/tests/sdd-status.test.ts +109 -110
- package/tests/skill-collision-prefixes.test.ts +5 -8
- 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/lib/git-commit-transaction.ts +0 -861
- package/runtime/git-commit-transaction.mjs +0 -862
- package/scripts/run-git-commit-transaction.mjs +0 -35
- 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 -530
|
@@ -91,8 +91,7 @@ function recoveredStatus(lineageId: string, materialize: boolean): ReviewStatusV
|
|
|
91
91
|
contract: "gentle-ai.review-integration/v2",
|
|
92
92
|
applicability: "current_target",
|
|
93
93
|
authority: { version: "compact-v2", lineageId, state: "reviewer_results_required", generation: 2, revision: SHA },
|
|
94
|
-
|
|
95
|
-
action: "finalize",
|
|
94
|
+
action: "stop",
|
|
96
95
|
replayability: "not_replayable",
|
|
97
96
|
targetIdentity: SHA,
|
|
98
97
|
projection: {
|
|
@@ -111,7 +110,7 @@ function recoveredStatus(lineageId: string, materialize: boolean): ReviewStatusV
|
|
|
111
110
|
},
|
|
112
111
|
candidates: [],
|
|
113
112
|
nextTransition: { kind: "collect", reasonCode: "reviewer_results_required", collect: { inputs: [collectInput(lineageId, materialize)] } },
|
|
114
|
-
raw: { schema: "gentle-ai.review-integration.status/v5", action: "
|
|
113
|
+
raw: { schema: "gentle-ai.review-integration.status/v5", action: "stop", lineage_id: lineageId },
|
|
115
114
|
} as unknown as ReviewStatusV3;
|
|
116
115
|
}
|
|
117
116
|
|
|
@@ -119,18 +118,21 @@ function recoveredStatus(lineageId: string, materialize: boolean): ReviewStatusV
|
|
|
119
118
|
* Mirrors the MEASURED live provider: the materialize-marked relay slot is
|
|
120
119
|
* offered only when the caller asks for the pi agent.
|
|
121
120
|
*/
|
|
122
|
-
function transportAwareNative(options: {
|
|
121
|
+
function transportAwareNative(options: { refusalCode?: string } = {}): {
|
|
122
|
+
native: NativeReviewCli;
|
|
123
|
+
agents: Array<string | undefined>;
|
|
124
|
+
} {
|
|
123
125
|
const agents: Array<string | undefined> = [];
|
|
124
126
|
const native = {
|
|
125
127
|
targetStatus: async (request: { lineageId?: string; agent?: string }) => {
|
|
126
128
|
agents.push(request.agent);
|
|
127
|
-
if (request.agent === "pi" && options.
|
|
129
|
+
if (request.agent === "pi" && options.refusalCode !== undefined) {
|
|
128
130
|
throw new NativeReviewIntegrationError({
|
|
129
131
|
schema: "gentle-ai.review-integration.failure/v2",
|
|
130
132
|
contract: "gentle-ai.review-integration/v2",
|
|
131
133
|
operation: "review.status",
|
|
132
134
|
phase: "pre_native",
|
|
133
|
-
code:
|
|
135
|
+
code: options.refusalCode,
|
|
134
136
|
message: "supported immutable review runtimes: claude-code, opencode, codex",
|
|
135
137
|
mutationOutcome: "none",
|
|
136
138
|
authorityApplicability: "current_target",
|
|
@@ -142,15 +144,22 @@ function transportAwareNative(options: { refusePiAgent?: boolean } = {}): { nati
|
|
|
142
144
|
}
|
|
143
145
|
return recoveredStatus(request.lineageId ?? "relay-lineage", request.agent === "pi");
|
|
144
146
|
},
|
|
145
|
-
finalize: async () => { throw new Error("native finalize must not run while reviewer results are outstanding"); },
|
|
146
147
|
} as unknown as NativeReviewCli;
|
|
147
148
|
return { native, agents };
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
async function
|
|
151
|
+
async function runCapture(cwd: string, native: NativeReviewCli, lineageId: string, input: Record<string, unknown> = { reviewerRunAcknowledged: true }): Promise<Record<string, unknown>> {
|
|
152
|
+
return await __testing.executeReviewCaptureOperation(
|
|
153
|
+
{ lineageId, collectBinding: JSON.stringify(collectInput(lineageId, true)), ...input },
|
|
154
|
+
cwd, native, undefined, new CandidateViewRegistry(),
|
|
155
|
+
) as Record<string, unknown>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async function runStatus(cwd: string, native: NativeReviewCli, lineageId: string): Promise<Record<string, unknown>> {
|
|
151
160
|
return await __testing.executeReviewControllerOperation(
|
|
152
|
-
{ operation: "
|
|
153
|
-
cwd,
|
|
161
|
+
{ operation: "status", lineageId },
|
|
162
|
+
cwd, native, undefined, new CandidateViewRegistry(),
|
|
154
163
|
) as Record<string, unknown>;
|
|
155
164
|
}
|
|
156
165
|
|
|
@@ -165,19 +174,19 @@ test("the negotiated status asks for the pi agent so the provider offers its mat
|
|
|
165
174
|
return { promptByteLength: 128, resultByteLength: 64, submission: '{"admission_decision":"completed"}' };
|
|
166
175
|
});
|
|
167
176
|
|
|
168
|
-
const result = await
|
|
177
|
+
const result = await runCapture(cwd, native, lineageId);
|
|
169
178
|
|
|
170
|
-
assert.
|
|
179
|
+
assert.equal(agents.at(0), "pi", "the successful Pi route starts with pi-bound STATUS negotiation");
|
|
180
|
+
assert.ok(agents.every((agent) => agent === "pi"), "the selected capture performs no post-success agent-less STATUS re-query");
|
|
171
181
|
assert.equal(relayed.length, 1, "the materialize-marked slot must reach the host relay");
|
|
172
182
|
assert.ok(relayed[0]!.captureArgumentTokens.includes("--materialize=true"));
|
|
173
183
|
assert.ok(relayed[0]!.submission !== undefined, "the provider submission drives the completing form");
|
|
174
|
-
const hostRelay = result.host_relay as { transport: string
|
|
175
|
-
assert.ok(hostRelay !== undefined, "the envelope reports the relay capture");
|
|
184
|
+
const hostRelay = result.host_relay as { transport: string } | undefined;
|
|
185
|
+
assert.ok(hostRelay !== undefined, "the envelope reports the one relay capture");
|
|
176
186
|
assert.equal(hostRelay.transport, "pi_host_relay");
|
|
177
|
-
assert.equal(hostRelay.captured_slots.length, 1);
|
|
178
187
|
});
|
|
179
188
|
|
|
180
|
-
test("
|
|
189
|
+
test("capture forecasts the reviewer model run once and spends nothing until it is acknowledged", async (t) => {
|
|
181
190
|
t.after(() => __testing.setReviewHostRelayRunnerForTesting());
|
|
182
191
|
const cwd = repository(t);
|
|
183
192
|
const lineageId = "forecast-lineage";
|
|
@@ -188,62 +197,126 @@ test("finalize forecasts the reviewer model run once and spends nothing until it
|
|
|
188
197
|
return { promptByteLength: 128, resultByteLength: 64, submission: '{"admission_decision":"completed"}' };
|
|
189
198
|
});
|
|
190
199
|
|
|
191
|
-
// An unacknowledged
|
|
192
|
-
const forecast = await
|
|
200
|
+
// An unacknowledged capture must forecast, never spend.
|
|
201
|
+
const forecast = await runCapture(cwd, native, lineageId, {});
|
|
193
202
|
assert.equal(launches, 0, "no reviewer model run may start before the forecast is acknowledged");
|
|
194
203
|
assert.equal(forecast.status, "blocked");
|
|
195
204
|
assert.equal(forecast.outcome, "reviewer-model-run-forecast");
|
|
196
205
|
assert.equal(forecast.mutation_performed, false);
|
|
197
206
|
assert.equal(forecast.mutation_outcome, "none");
|
|
198
|
-
const cost = forecast.cost_forecast as { transport: string; model_runs: number; lenses: readonly string[]
|
|
207
|
+
const cost = forecast.cost_forecast as { transport: string; model_runs: number; lenses: readonly string[] };
|
|
199
208
|
assert.equal(cost.transport, "pi_host_relay");
|
|
200
209
|
assert.equal(cost.model_runs, 1, "one model run per outstanding lens, forecast once before launch");
|
|
201
210
|
assert.deepEqual(cost.lenses, ["review-reliability"]);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const acknowledged = await runFinalize(cwd, native, lineageId, { reviewer_run_acknowledged: true });
|
|
208
|
-
assert.equal(launches, 1, "the acknowledged finalize runs the forecast model run");
|
|
209
|
-
assert.equal((acknowledged.host_relay as { captured_slots: readonly unknown[] }).captured_slots.length, 1);
|
|
211
|
+
|
|
212
|
+
// The acknowledged capture runs exactly the forecast work.
|
|
213
|
+
const acknowledged = await runCapture(cwd, native, lineageId, { reviewerRunAcknowledged: true });
|
|
214
|
+
assert.equal(launches, 1, "the acknowledged capture runs the forecast model run");
|
|
215
|
+
assert.equal(acknowledged.status, "captured");
|
|
210
216
|
});
|
|
211
217
|
|
|
212
|
-
test("a provider that refuses the pi transport
|
|
218
|
+
test("a provider that refuses the pi transport blocks immediately without an agent-less lifecycle fallback", async (t) => {
|
|
213
219
|
t.after(() => __testing.setReviewHostRelayRunnerForTesting());
|
|
214
220
|
const cwd = repository(t);
|
|
215
221
|
const lineageId = "legacy-transport-lineage";
|
|
216
|
-
const { native, agents } = transportAwareNative({
|
|
222
|
+
const { native, agents } = transportAwareNative({ refusalCode: TRANSPORT_REFUSAL_CODE });
|
|
217
223
|
let relayCalls = 0;
|
|
218
224
|
__testing.setReviewHostRelayRunnerForTesting(async () => {
|
|
219
225
|
relayCalls += 1;
|
|
220
226
|
return { promptByteLength: 1, resultByteLength: 1, submission: "{}" };
|
|
221
227
|
});
|
|
222
228
|
|
|
223
|
-
const result = await
|
|
229
|
+
const result = await runCapture(cwd, native, lineageId);
|
|
224
230
|
|
|
225
|
-
assert.
|
|
226
|
-
assert.
|
|
227
|
-
assert.equal(relayCalls, 0, "no relay slot exists on a provider without the pi transport");
|
|
228
|
-
// Deliverable: the user sees the real typed cause, never a generic
|
|
229
|
-
// candidate-view message.
|
|
231
|
+
assert.deepEqual(agents, ["pi"], "the refusal path issues one pi-bound STATUS and never falls back to an agent-less STATUS");
|
|
232
|
+
assert.equal(relayCalls, 0, "a refused transport must not launch a relay capture");
|
|
230
233
|
const transport = result.relay_transport as { supported: boolean; code: string; message: string } | undefined;
|
|
231
|
-
assert.ok(transport !== undefined, "the envelope must report the transport refusal");
|
|
234
|
+
assert.ok(transport !== undefined, "the blocked envelope must report the typed transport refusal");
|
|
232
235
|
assert.equal(transport.supported, false);
|
|
233
236
|
assert.equal(transport.code, TRANSPORT_REFUSAL_CODE);
|
|
234
237
|
assert.match(transport.message, /immutable review runtimes/i);
|
|
238
|
+
assert.equal(result.status, "blocked");
|
|
239
|
+
assert.equal(result.outcome, "pi-host-relay-transport-unavailable");
|
|
240
|
+
assert.equal(result.mutation_performed, false);
|
|
241
|
+
assert.equal(result.mutation_outcome, "none");
|
|
242
|
+
assert.match(String(result.next_action), /support.*--agent pi/i);
|
|
235
243
|
assert.doesNotMatch(JSON.stringify(result), /no current controller-owned candidate view/);
|
|
236
|
-
|
|
237
|
-
|
|
244
|
+
assert.doesNotMatch(JSON.stringify(result), /"status":"(?:approved|allowed)"/);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("a transport refusal names the runnable wrapper continuation, never a raw CLI command as the only exit", async (t) => {
|
|
248
|
+
// #535: a provider-printed raw `gentle-ai review ...` continuation is a dead
|
|
249
|
+
// end in this runtime — Pi is not in the provider's immutable review runtime
|
|
250
|
+
// list — so the refusal envelope itself must name the exit that runs in this
|
|
251
|
+
// surface: the gentle_review / gentle_review_capture wrapper tools. The
|
|
252
|
+
// provider's diagnostic stays intact as evidence alongside the runnable exit.
|
|
253
|
+
const captureCwd = repository(t);
|
|
254
|
+
const { native: captureNative } = transportAwareNative({ refusalCode: TRANSPORT_REFUSAL_CODE });
|
|
255
|
+
const capture = await runCapture(captureCwd, captureNative, "continuation-capture-lineage");
|
|
256
|
+
__testing.clearReviewTransportProbeForTesting(captureNative);
|
|
257
|
+
assert.equal(capture.outcome, "pi-host-relay-transport-unavailable");
|
|
258
|
+
const captureNextAction = String(capture.next_action);
|
|
259
|
+
assert.match(captureNextAction, /gentle_review \{"operation":"inspect"\}/, "the capture refusal re-enters negotiated STATUS through the wrapper controller");
|
|
260
|
+
assert.match(captureNextAction, /gentle_review_capture/, "the capture refusal names the wrapper resubmission, not a CLI command");
|
|
261
|
+
assert.doesNotMatch(captureNextAction, /gentle-ai review/, "a raw `gentle-ai review ...` invocation is not a runnable exit in this runtime");
|
|
262
|
+
assert.deepEqual(capture.wrapper_continuation, { tool: "gentle_review", operation: "inspect", then: "gentle_review_capture" });
|
|
263
|
+
assert.match(String((capture.relay_transport as { message: string }).message), /immutable review runtimes/i, "the provider diagnostic remains intact in the envelope");
|
|
264
|
+
|
|
265
|
+
const statusCwd = repository(t);
|
|
266
|
+
const { native: statusNative } = transportAwareNative({ refusalCode: TRANSPORT_REFUSAL_CODE });
|
|
267
|
+
const status = await runStatus(statusCwd, statusNative, "continuation-status-lineage");
|
|
268
|
+
__testing.clearReviewTransportProbeForTesting(statusNative);
|
|
269
|
+
assert.equal(status.outcome, "pi-host-relay-transport-unavailable");
|
|
270
|
+
const statusNextAction = String(status.next_action);
|
|
271
|
+
assert.match(statusNextAction, /gentle_review \{"operation":"inspect"\}/, "the controller refusal re-enters negotiated STATUS through the wrapper controller");
|
|
272
|
+
assert.doesNotMatch(statusNextAction, /gentle-ai review/, "a raw `gentle-ai review ...` invocation is not a runnable exit in this runtime");
|
|
273
|
+
assert.deepEqual(status.wrapper_continuation, { tool: "gentle_review", operation: "inspect" });
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("a remembered pi transport refusal remains blocked without re-probing or lifecycle continuation", async (t) => {
|
|
277
|
+
const cwd = repository(t);
|
|
278
|
+
const { native, agents } = transportAwareNative({ refusalCode: TRANSPORT_REFUSAL_CODE });
|
|
279
|
+
const first = await runCapture(cwd, native, "probe-lineage");
|
|
280
|
+
const second = await runCapture(cwd, native, "probe-lineage");
|
|
281
|
+
assert.equal(first.outcome, "pi-host-relay-transport-unavailable");
|
|
282
|
+
assert.equal(second.outcome, "pi-host-relay-transport-unavailable");
|
|
283
|
+
assert.deepEqual(agents, ["pi"], "a cached refusal must not re-probe or issue an agent-less STATUS");
|
|
284
|
+
__testing.clearReviewTransportProbeForTesting(native);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("every typed Pi transport refusal code fails closed", async (t) => {
|
|
288
|
+
const refusalCodes = [
|
|
289
|
+
"immutable_review_transport_unsupported",
|
|
290
|
+
"unsupported_agent",
|
|
291
|
+
"unknown_flag",
|
|
292
|
+
];
|
|
293
|
+
for (const refusalCode of refusalCodes) {
|
|
294
|
+
const cwd = repository(t);
|
|
295
|
+
const { native, agents } = transportAwareNative({ refusalCode });
|
|
296
|
+
const result = await runCapture(cwd, native, `refusal-${refusalCode}`);
|
|
297
|
+
assert.equal(result.status, "blocked", `${refusalCode} must be blocked`);
|
|
298
|
+
assert.equal(result.outcome, "pi-host-relay-transport-unavailable", `${refusalCode} must use the unavailable envelope`);
|
|
299
|
+
assert.equal((result.relay_transport as { code: string }).code, refusalCode);
|
|
300
|
+
assert.deepEqual(agents, ["pi"], `${refusalCode} must not fall back to agent-less STATUS`);
|
|
301
|
+
__testing.clearReviewTransportProbeForTesting(native);
|
|
302
|
+
}
|
|
238
303
|
});
|
|
239
304
|
|
|
240
|
-
test("
|
|
305
|
+
test("typed status errors outside the Pi transport refusal set remain native errors", async (t) => {
|
|
241
306
|
const cwd = repository(t);
|
|
242
|
-
const { native, agents } = transportAwareNative({
|
|
243
|
-
await
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
assert.
|
|
248
|
-
assert.equal(
|
|
307
|
+
const { native, agents } = transportAwareNative({ refusalCode: "provider_unavailable" });
|
|
308
|
+
const result = await runCapture(cwd, native, "non-transport-status-error");
|
|
309
|
+
assert.deepEqual(agents, ["pi"], "a non-transport error must not be retried as an agent-less lifecycle STATUS");
|
|
310
|
+
assert.equal(result.status, "blocked");
|
|
311
|
+
assert.equal(result.outcome, undefined, "a non-transport error must not be coerced into a transport refusal envelope");
|
|
312
|
+
assert.ok("native_failure" in result, "the native error envelope must remain available to the caller");
|
|
313
|
+
assert.equal(result.relay_transport, undefined);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("ordinary STATUS inspection uses the Pi public collect-binding route", async (t) => {
|
|
317
|
+
const cwd = repository(t);
|
|
318
|
+
const { native, agents } = transportAwareNative();
|
|
319
|
+
const result = await runStatus(cwd, native, "ordinary-status-lineage");
|
|
320
|
+
assert.deepEqual(agents, ["pi"], "ordinary STATUS must request the Pi public collect-binding route");
|
|
321
|
+
assert.equal(result.operation, "status");
|
|
249
322
|
});
|
|
@@ -2,9 +2,10 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import {
|
|
4
4
|
existsSync,
|
|
5
|
-
mkdtempSync,
|
|
6
5
|
mkdirSync,
|
|
6
|
+
mkdtempSync,
|
|
7
7
|
readFileSync,
|
|
8
|
+
realpathSync,
|
|
8
9
|
rmSync,
|
|
9
10
|
writeFileSync,
|
|
10
11
|
} from "node:fs";
|
|
@@ -60,7 +61,7 @@ function createRepository(t: test.TestContext): {
|
|
|
60
61
|
repository: string;
|
|
61
62
|
git: (...args: string[]) => string;
|
|
62
63
|
} {
|
|
63
|
-
const parent = mkdtempSync(join(tmpdir(), "gentle-pi-snapshot-"));
|
|
64
|
+
const parent = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-snapshot-")));
|
|
64
65
|
const repository = join(parent, "repo");
|
|
65
66
|
mkdirSync(repository);
|
|
66
67
|
t.after(() => rmSync(parent, { recursive: true, force: true }));
|