gentle-pi 2.4.0 → 2.6.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 +292 -25
- package/assets/agents/gentle-ai-worker.md +13 -0
- package/assets/agents/jd-fix-agent.md +18 -0
- package/assets/agents/jd-judge-a.md +1 -1
- package/assets/agents/jd-judge-b.md +1 -1
- package/assets/agents/sdd-apply.md +7 -5
- package/assets/agents/sdd-archive.md +5 -3
- package/assets/agents/sdd-design.md +4 -0
- package/assets/agents/sdd-explore.md +4 -0
- package/assets/agents/sdd-init.md +4 -0
- package/assets/agents/sdd-onboard.md +4 -0
- package/assets/agents/sdd-proposal.md +4 -0
- package/assets/agents/sdd-remediate.md +37 -0
- package/assets/agents/sdd-research.md +26 -3
- package/assets/agents/sdd-spec.md +4 -0
- package/assets/agents/sdd-status.md +9 -75
- package/assets/agents/sdd-sync.md +4 -0
- package/assets/agents/sdd-tasks.md +4 -0
- package/assets/agents/sdd-verify.md +5 -3
- package/assets/chains/sdd-full.chain.md +4 -0
- package/assets/chains/sdd-plan.chain.md +4 -0
- package/assets/chains/sdd-verify.chain.md +4 -0
- package/assets/migrations/managed-assets-v2.5.0.json +7 -0
- package/assets/orchestrator-delegation.md +39 -11
- package/assets/orchestrator.md +5 -5
- package/assets/sdd-orchestrator-workflow.md +54 -21
- package/assets/support/sdd-status-contract.md +34 -90
- package/contracts/telemetry/runtime-aggregate-v1.schema.json +65 -0
- package/docs/delegated-verification.md +25 -0
- package/docs/telemetry.md +94 -0
- package/docs/windows-startup-console-visibility.md +18 -0
- package/extensions/ask-user-choice.ts +159 -25
- package/extensions/codegraph-tools.ts +95 -5
- package/extensions/gentle-agents.ts +1337 -0
- package/extensions/gentle-ai.ts +2916 -386
- package/extensions/gentle-shell.ts +650 -0
- package/extensions/gentle-todo.ts +234 -0
- package/extensions/quiet-tools.ts +2 -1
- package/extensions/runtime-metrics.ts +130 -0
- package/extensions/sdd-init.ts +2 -2
- package/extensions/startup-banner.ts +52 -75
- package/lib/agent-profiles.ts +550 -0
- package/lib/agents-completion-delivery.ts +72 -0
- package/lib/agents-config.ts +315 -0
- package/lib/agents-history.ts +88 -0
- package/lib/agents-messaging.ts +187 -0
- package/lib/agents-protocol.ts +501 -0
- package/lib/agents-runner.ts +1012 -0
- package/lib/agents-thread-view.ts +57 -0
- package/lib/agents-transcript.ts +87 -0
- package/lib/agents-view-layout.ts +40 -0
- package/lib/agents-view.ts +914 -0
- package/lib/agents-widget.ts +241 -0
- package/lib/gentle-ai-binary.ts +3 -1
- package/lib/gentle-ai-renderer.ts +143 -25
- package/lib/native-choice-list.ts +194 -0
- package/lib/native-fullscreen-interaction.ts +47 -0
- package/lib/native-pointer-region.ts +164 -0
- package/lib/native-review-cli.ts +371 -13
- package/lib/orchestrator-presence.ts +337 -0
- package/lib/profiles-orchestrator.ts +203 -0
- package/lib/review-candidate-view-owner.ts +427 -0
- package/lib/review-candidate-view.ts +150 -48
- package/lib/review-consent-component.ts +247 -0
- package/lib/review-consent-ui.ts +110 -0
- package/lib/review-host-relay.ts +28 -0
- package/lib/review-integration-v2.ts +243 -11
- package/lib/review-last-event-controller.ts +8 -4
- package/lib/review-relay-contract.ts +11 -0
- package/lib/review-reminder-receipt.ts +74 -0
- package/lib/review-repository.ts +2 -2
- package/lib/review-risk-assessment.ts +339 -0
- package/lib/review-session-standing-permission-ipc.ts +309 -0
- package/lib/review-session-standing-permission.ts +240 -0
- package/lib/runtime-metrics-children.ts +199 -0
- package/lib/runtime-metrics-delivery.ts +68 -0
- package/lib/runtime-metrics-native.ts +166 -0
- package/lib/runtime-metrics-pi-identity.ts +113 -0
- package/lib/runtime-metrics-policy.ts +51 -0
- package/lib/runtime-metrics.ts +255 -0
- package/lib/sdd-preflight.ts +362 -81
- package/lib/sdd-research-capabilities.ts +228 -0
- package/lib/sdd-status.ts +29 -7
- package/lib/session-worktree-registry.ts +118 -0
- package/lib/shell-bar.ts +184 -0
- package/lib/shell-card.ts +133 -0
- package/lib/shell-changes-view.ts +530 -0
- package/lib/shell-changes.ts +290 -0
- package/lib/shell-gauge.ts +40 -0
- package/lib/shell-prompt.ts +115 -0
- package/lib/shell-sidebar-banner.ts +11 -0
- package/lib/shell-sidebar-layout.ts +213 -0
- package/lib/shell-sidebar.ts +41 -0
- package/lib/shell-todo.ts +297 -0
- package/lib/shell-usage-view.ts +76 -0
- package/lib/shell-usage.ts +246 -0
- package/lib/telemetry-trigger.ts +153 -0
- package/package.json +8 -5
- package/runtime/gentle-ai-binary.mjs +3 -1
- package/runtime/native-review-cli.mjs +370 -12
- package/runtime/review-integration-v2.mjs +243 -11
- package/runtime/review-relay-contract.mjs +11 -0
- package/runtime/review-risk-assessment.mjs +340 -0
- package/runtime/telemetry-trigger.mjs +154 -0
- package/scripts/build-runtime-modules.mjs +11 -1
- package/scripts/check-types.mjs +125 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/install-gentle-ai.mjs +12 -0
- package/scripts/install-tui-mode-setting.mjs +114 -0
- package/scripts/test-packed-runner.mjs +38 -2
- package/scripts/types-baseline.json +99 -0
- package/scripts/verify-package-files.mjs +8 -2
- package/skills/_shared/review-ledger-contract.md +20 -2
- package/skills/issue-creation/SKILL.md +3 -3
- package/skills/judgment-day/SKILL.md +17 -3
- package/skills/judgment-day/references/prompts-and-formats.md +14 -3
- package/tests/agent-profiles.test.ts +722 -0
- package/tests/agents-completion-delivery.test.ts +94 -0
- package/tests/agents-config.test.ts +205 -0
- package/tests/agents-fake-child.ts +66 -0
- package/tests/agents-grouping.test.ts +179 -0
- package/tests/agents-history.test.ts +54 -0
- package/tests/agents-integration.test.ts +100 -0
- package/tests/agents-messaging.test.ts +94 -0
- package/tests/agents-protocol.test.ts +198 -0
- package/tests/agents-queries.test.ts +190 -0
- package/tests/agents-responsive.test.ts +43 -0
- package/tests/agents-runner-process.test.ts +111 -0
- package/tests/agents-runner.test.ts +959 -0
- package/tests/agents-thread-view.test.ts +45 -0
- package/tests/agents-transcript.test.ts +30 -0
- package/tests/agents-view.test.ts +685 -0
- package/tests/agents-widget.test.ts +141 -0
- package/tests/artifact-language.test.ts +25 -2
- package/tests/ask-user-choice.test.ts +325 -5
- package/tests/asset-installation-runtime.test.ts +108 -0
- package/tests/autonomous-guard.test.ts +116 -1
- package/tests/codegraph-tools.test.ts +112 -2
- package/tests/delegated-key-learnings-contract.test.ts +1 -1
- package/tests/devbinary/native-review-parity.devtest.ts +110 -0
- package/tests/feature-request-form.test.ts +67 -0
- package/tests/fixtures/agents-messaging-child.mjs +5 -0
- package/tests/fixtures/agents-process-child.mjs +23 -0
- package/tests/fixtures/runtime-metrics-native-batches.json +6 -0
- package/tests/gentle-agents.test.ts +2168 -0
- package/tests/gentle-ai-binary.test.ts +7 -2
- package/tests/gentle-ai-installer.test.ts +47 -47
- package/tests/gentle-ai-renderer.test.ts +103 -0
- package/tests/gentle-ai.test.ts +971 -15
- package/tests/gentle-card-text.ts +35 -0
- package/tests/gentle-shell.test.ts +818 -0
- package/tests/gentle-todo.test.ts +226 -0
- package/tests/install-tui-mode-setting.test.ts +324 -0
- package/tests/issue-creation-skill.test.ts +22 -0
- package/tests/model-routing-authority.test.ts +12 -0
- package/tests/native-choice-list.test.ts +202 -0
- package/tests/native-fullscreen-interaction.test.ts +125 -0
- package/tests/native-pointer-region.test.ts +245 -0
- package/tests/native-review-capability-contract.test.ts +27 -1
- package/tests/native-review-cli.test.ts +317 -3
- package/tests/native-review-consent.test.ts +91 -0
- package/tests/native-review-parity-runtime.test.ts +8 -2
- package/tests/native-review-parity.test.ts +43 -29
- package/tests/native-sdd-attempt-authority.test.ts +7 -2
- package/tests/orchestrator-budget.test.ts +69 -0
- package/tests/orchestrator-presence.test.ts +389 -0
- package/tests/orchestrator-rdd-ownership.test.ts +9 -0
- package/tests/package-manifest.test.ts +243 -7
- package/tests/profiles-orchestrator.test.ts +208 -0
- package/tests/quiet-tool-rendering.test.ts +97 -37
- package/tests/rdd-aware-verification-contract.test.ts +226 -0
- package/tests/rdd-status-line.test.ts +286 -0
- package/tests/review-agent-end-preflight.test.ts +332 -24
- package/tests/review-candidate-view.test.ts +751 -7
- package/tests/review-consent-ui.test.ts +352 -0
- package/tests/review-contract-prompt.test.ts +17 -0
- package/tests/review-controller-native-recovery.test.ts +29 -4
- package/tests/review-controller-native-routing.test.ts +884 -7
- package/tests/review-controller-workspace-root.test.ts +45 -2
- package/tests/review-controller.test.ts +26 -1
- package/tests/review-host-relay-restart-parity.test.ts +142 -1
- package/tests/review-host-relay-routing.test.ts +384 -8
- package/tests/review-host-relay.test.ts +29 -0
- package/tests/review-integration-v2-forward.test.ts +44 -0
- package/tests/review-integration-v2.test.ts +276 -0
- package/tests/review-last-event-closure.test.ts +112 -3
- package/tests/review-ledger-contract.test.ts +61 -6
- package/tests/review-relay-contract.test.ts +26 -0
- package/tests/review-reminder-receipt.test.ts +62 -0
- package/tests/review-repository.test.ts +28 -1
- package/tests/review-risk-assessment.test.ts +626 -0
- package/tests/review-session-standing-permission-controller.test.ts +656 -0
- package/tests/review-session-standing-permission-ipc.test.ts +233 -0
- package/tests/review-session-standing-permission-runtime.test.ts +212 -0
- package/tests/review-session-standing-permission.test.ts +156 -0
- package/tests/runtime-harness.mjs +447 -39
- package/tests/runtime-metrics-children.test.ts +206 -0
- package/tests/runtime-metrics-delivery.test.ts +85 -0
- package/tests/runtime-metrics-extension.test.ts +187 -0
- package/tests/runtime-metrics-native.test.ts +209 -0
- package/tests/runtime-metrics-pi-identity.test.ts +113 -0
- package/tests/runtime-metrics-policy.test.ts +62 -0
- package/tests/runtime-metrics.test.ts +184 -0
- package/tests/sdd-agent-tools.test.ts +10 -1
- package/tests/sdd-execution-routing-contract.test.ts +28 -0
- package/tests/sdd-managed-runtime-settlement.test.ts +331 -0
- package/tests/sdd-native-managed-uptake.test.ts +253 -0
- package/tests/sdd-planning-routing-contract.test.ts +45 -0
- package/tests/sdd-preflight.test.ts +252 -8
- package/tests/sdd-research-capabilities.test.ts +256 -0
- package/tests/sdd-research-live.test.ts +241 -0
- package/tests/sdd-selection-transport.test.ts +504 -0
- package/tests/sdd-status.test.ts +51 -0
- package/tests/session-worktree-registry.test.ts +135 -0
- package/tests/shell-bar.test.ts +176 -0
- package/tests/shell-card.test.ts +139 -0
- package/tests/shell-changes-view.test.ts +609 -0
- package/tests/shell-changes.test.ts +350 -0
- package/tests/shell-prompt.test.ts +140 -0
- package/tests/shell-sidebar-banner.test.ts +23 -0
- package/tests/shell-sidebar-layout.test.ts +387 -0
- package/tests/shell-sidebar.test.ts +50 -0
- package/tests/shell-todo.test.ts +259 -0
- package/tests/shell-usage-view.test.ts +62 -0
- package/tests/shell-usage.test.ts +197 -0
- package/tests/startup-banner.test.ts +126 -0
- package/tests/telemetry-trigger.test.ts +351 -0
|
@@ -6,9 +6,9 @@ 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
8
|
import { __testing, createGentleAiExtension } from "../extensions/gentle-ai.ts";
|
|
9
|
-
import type
|
|
9
|
+
import { NativeReviewIntegrationError, type NativeReviewCli } from "../lib/native-review-cli.ts";
|
|
10
10
|
import { CandidateViewRegistry } from "../lib/review-candidate-view.ts";
|
|
11
|
-
import type
|
|
11
|
+
import { decodeReviewFailureV2, type AuthorityRepairAssessmentV1, type ReviewStatusV3 } from "../lib/review-integration-v2.ts";
|
|
12
12
|
|
|
13
13
|
interface RegisteredTool {
|
|
14
14
|
execute: (
|
|
@@ -240,6 +240,49 @@ test("INSPECT and STATUS operate on the explicit workspace root while the sessio
|
|
|
240
240
|
assert.deepEqual(observedCwds, [realpathSync(worktree), realpathSync(worktree), sessionCwd]);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
+
// gentle-pi#599: inspect without an explicit workspaceRoot against a parent
|
|
244
|
+
// repository that wraps an untracked nested Git repository fails at the
|
|
245
|
+
// native preflight with a decoded, actionable `invalid_request` envelope. The
|
|
246
|
+
// facade used to discard that envelope's cause, code, retry_safe, and
|
|
247
|
+
// next_action in favor of a generic `native-status-unavailable` outcome
|
|
248
|
+
// indistinguishable from authority corruption. It must preserve the native
|
|
249
|
+
// envelope instead.
|
|
250
|
+
test("inspect without workspaceRoot preserves the native invalid_request cause for a nested foreign Git repository", async (t) => {
|
|
251
|
+
const sessionCwd = repository(t);
|
|
252
|
+
const { controller } = runtime(fakeNative({
|
|
253
|
+
targetStatus: async () => {
|
|
254
|
+
throw new NativeReviewIntegrationError(decodeReviewFailureV2({
|
|
255
|
+
schema: "gentle-ai.review-integration.failure/v2",
|
|
256
|
+
contract: "gentle-ai.review-integration/v2",
|
|
257
|
+
operation: "review.status",
|
|
258
|
+
phase: "preflight",
|
|
259
|
+
code: "invalid_request",
|
|
260
|
+
message: "The negotiated review request is invalid.",
|
|
261
|
+
mutation_outcome: "not_started",
|
|
262
|
+
authority_applicability: "not_evaluated",
|
|
263
|
+
retry_safe: true,
|
|
264
|
+
replayability: "not_replayable",
|
|
265
|
+
required_inputs: [],
|
|
266
|
+
next_action: "correct_request",
|
|
267
|
+
cause: "untracked directory \"engram\" holds another Git repository that is not a linked worktree of this one, so it cannot enter the review candidate: add it to .gitignore, move it outside this repository, or register it as a linked worktree",
|
|
268
|
+
}));
|
|
269
|
+
},
|
|
270
|
+
}));
|
|
271
|
+
const result = await controller.execute("inspect-nested-foreign-repo", { operation: "inspect" }, undefined, undefined, context(sessionCwd));
|
|
272
|
+
const details = result.details as {
|
|
273
|
+
status?: string;
|
|
274
|
+
outcome?: string;
|
|
275
|
+
next_action?: string;
|
|
276
|
+
native_failure?: { code?: string; cause?: string; retry_safe?: boolean };
|
|
277
|
+
};
|
|
278
|
+
assert.equal(details.status, "blocked");
|
|
279
|
+
assert.equal(details.outcome, "native-status-unavailable");
|
|
280
|
+
assert.equal(details.native_failure?.code, "invalid_request");
|
|
281
|
+
assert.equal(details.native_failure?.retry_safe, true);
|
|
282
|
+
assert.match(details.native_failure?.cause ?? "", /engram/);
|
|
283
|
+
assert.equal(details.next_action, "correct_request");
|
|
284
|
+
});
|
|
285
|
+
|
|
243
286
|
test("START freezes the candidate from the explicit workspace root and returns the actor binding envelope", async (t) => {
|
|
244
287
|
const sessionCwd = repository(t);
|
|
245
288
|
writeFileSync(join(sessionCwd, "unrelated.ts"), "export const unrelated = true;\n");
|
|
@@ -208,7 +208,7 @@ test("controller SDD status treats removed OpenSpec recovery authority and delet
|
|
|
208
208
|
const status = await __testing.resolveControllerSddStatus(fixture.repository, changeName, false, "openspec");
|
|
209
209
|
|
|
210
210
|
assert.equal(status.dependencies.archive, "blocked");
|
|
211
|
-
assert.equal(status.nextRecommended, "
|
|
211
|
+
assert.equal(status.nextRecommended, "blocked");
|
|
212
212
|
assert.match(status.blockedReasons.join("\n"), /active change not found/i);
|
|
213
213
|
});
|
|
214
214
|
|
|
@@ -363,6 +363,7 @@ test("general STATUS returns the typed native-status-unsupported boundary withou
|
|
|
363
363
|
mutation_performed: false,
|
|
364
364
|
inventory_complete: false,
|
|
365
365
|
next_action: "require-upstream-read-only-native-status-inventory",
|
|
366
|
+
remediation_command: "gentle-ai review status --cwd <repo> --contract gentle-ai.review-integration/v2 --agent pi --next-transition",
|
|
366
367
|
evidence: {
|
|
367
368
|
native_contract: "gentle-ai/2.1.4",
|
|
368
369
|
general_status: "unsupported",
|
|
@@ -371,6 +372,30 @@ test("general STATUS returns the typed native-status-unsupported boundary withou
|
|
|
371
372
|
});
|
|
372
373
|
});
|
|
373
374
|
|
|
375
|
+
test("gentle-pi#185: general STATUS on a non-negotiated native CLI names the exact status command to run", async (t) => {
|
|
376
|
+
// The legacy `correctionForecast` restoration guard this issue originally
|
|
377
|
+
// reported (extensions/gentle-ai.ts, then around line 5329) was scoped to
|
|
378
|
+
// `targetStatus !== undefined` and skipped restoring a candidate view when
|
|
379
|
+
// the native CLI lacked negotiated STATUS support, reproducing the #176
|
|
380
|
+
// empty-registry failure. That entire manual FINALIZE lifecycle (and the
|
|
381
|
+
// `correctionForecast` guard with it) has since been replaced by the
|
|
382
|
+
// STATUS/capture-driven flow: every operation now checks negotiated STATUS
|
|
383
|
+
// support up front, so no candidate-view restoration is ever attempted on
|
|
384
|
+
// a non-negotiated CLI. This test proves the current equivalent boundary
|
|
385
|
+
// (`nativeStatusUnsupported`) fails closed with an actionable envelope
|
|
386
|
+
// instead of a bare machine token, so a non-negotiated CLI can never
|
|
387
|
+
// surface as a silent empty-registry failure.
|
|
388
|
+
const fixture = createRepository(t);
|
|
389
|
+
const { controller } = registerRuntime();
|
|
390
|
+
const result = await controllerCall(controller, extensionContext(fixture.repository), { operation: "status" });
|
|
391
|
+
assert.equal(result.status, "blocked");
|
|
392
|
+
assert.equal(result.outcome, "native-status-unsupported");
|
|
393
|
+
assert.equal(
|
|
394
|
+
result.remediation_command,
|
|
395
|
+
"gentle-ai review status --cwd <repo> --contract gentle-ai.review-integration/v2 --agent pi --next-transition",
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
|
|
374
399
|
test("failed START gives exact mode and serialization guidance and creates no lineage", async (t) => {
|
|
375
400
|
const fixture = createRepository(t);
|
|
376
401
|
const { controller } = registerRuntime();
|
|
@@ -72,6 +72,21 @@ function relayCollectInput(lineageId, lens, order) {
|
|
|
72
72
|
{ name: "agent", value: "pi", token: "--agent=pi" },
|
|
73
73
|
{ name: "materialize", value: "true", token: "--materialize=true" },
|
|
74
74
|
],
|
|
75
|
+
// gentle-pi#638: the artifact subject is what makes the slot declarable —
|
|
76
|
+
// its subject_hash is the --request-hash the capture-unachievable verb
|
|
77
|
+
// binds to. A slot without one cannot be declared, only retried.
|
|
78
|
+
artifactSubject: {
|
|
79
|
+
schema: "gentle-ai.review-artifact-subject/v2",
|
|
80
|
+
subjectHash: `sha256:${String(order).repeat(64)}`,
|
|
81
|
+
lineageId,
|
|
82
|
+
authorityRevision: SHA,
|
|
83
|
+
targetIdentity: SHA,
|
|
84
|
+
baseTree: TREE,
|
|
85
|
+
candidateTree: TREE,
|
|
86
|
+
changedPathManifestSha256: SHA,
|
|
87
|
+
lens,
|
|
88
|
+
selectedOrder: order,
|
|
89
|
+
},
|
|
75
90
|
submission: providerSubmission(lineageId, lens, order),
|
|
76
91
|
};
|
|
77
92
|
}
|
|
@@ -146,6 +161,7 @@ const { ReviewHostRelayError, REVIEW_HOST_RELAY_FAILURE } = await import(${JSON.
|
|
|
146
161
|
const statusQueue = JSON.parse(await readFile(statusFile, "utf8"));
|
|
147
162
|
const relayRequests = [];
|
|
148
163
|
const statusCalls = [];
|
|
164
|
+
const unachievableCalls = [];
|
|
149
165
|
let captureCalls = 0;
|
|
150
166
|
|
|
151
167
|
__testing.setReviewHostRelayRunnerForTesting(async (request) => {
|
|
@@ -156,6 +172,9 @@ __testing.setReviewHostRelayRunnerForTesting(async (request) => {
|
|
|
156
172
|
if (mode === "capture-fail") {
|
|
157
173
|
throw new ReviewHostRelayError(REVIEW_HOST_RELAY_FAILURE.PI_FAILED, "pi", "pi subprocess failed", { exitCode: 4 });
|
|
158
174
|
}
|
|
175
|
+
if (mode === "capture-timeout") {
|
|
176
|
+
throw new ReviewHostRelayError(REVIEW_HOST_RELAY_FAILURE.PI_TIMED_OUT, "pi", "pi reviewer subprocess exceeded the relay bound", { exitCode: null, timedOut: true, elapsedMs: 2256004, timeoutMs: 2256000 });
|
|
177
|
+
}
|
|
159
178
|
if (mode === "capture-ambiguous") {
|
|
160
179
|
throw Object.assign(new Error("capture response lost"), { mutationOutcome: "unknown", nextAction: "review.status" });
|
|
161
180
|
}
|
|
@@ -172,6 +191,27 @@ const nativeReviewCli = {
|
|
|
172
191
|
if (next === undefined) throw new Error("status queue exhausted");
|
|
173
192
|
return next;
|
|
174
193
|
},
|
|
194
|
+
captureUnachievableLens: async (request) => {
|
|
195
|
+
unachievableCalls.push({
|
|
196
|
+
cwd: request.cwd,
|
|
197
|
+
lineageId: request.lineageId,
|
|
198
|
+
targetIdentity: request.targetIdentity,
|
|
199
|
+
expectedRevision: request.expectedRevision,
|
|
200
|
+
requestHash: request.requestHash,
|
|
201
|
+
reason: request.reason,
|
|
202
|
+
...(request.detail === undefined ? {} : { detail: request.detail }),
|
|
203
|
+
...(request.repositoryContext === undefined ? {} : { repositoryContext: request.repositoryContext }),
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
schema: "gentle-ai.review-capture-unachievable/v1",
|
|
207
|
+
lineageId: request.lineageId,
|
|
208
|
+
targetIdentity: request.targetIdentity,
|
|
209
|
+
lens: ${JSON.stringify(LENS)},
|
|
210
|
+
selectedOrder: ${JSON.stringify(ORDER)},
|
|
211
|
+
reason: request.reason,
|
|
212
|
+
recorded: true,
|
|
213
|
+
};
|
|
214
|
+
},
|
|
175
215
|
};
|
|
176
216
|
|
|
177
217
|
let result;
|
|
@@ -199,7 +239,7 @@ try {
|
|
|
199
239
|
error = caught instanceof Error ? { name: caught.name, message: caught.message } : String(caught);
|
|
200
240
|
}
|
|
201
241
|
|
|
202
|
-
await writeFile(outFile, JSON.stringify({ result, inspectResult, captureBinding, error, relayRequests, statusCalls, captureCalls }, null, 2));
|
|
242
|
+
await writeFile(outFile, JSON.stringify({ result, inspectResult, captureBinding, error, relayRequests, statusCalls, captureCalls, unachievableCalls }, null, 2));
|
|
203
243
|
`;
|
|
204
244
|
}
|
|
205
245
|
|
|
@@ -225,6 +265,107 @@ function inspectCollectBinding(inspectResult) {
|
|
|
225
265
|
const PENDING = collectStatus(LINEAGE, [relayCollectInput(LINEAGE, LENS, ORDER)]);
|
|
226
266
|
const CONVERGED = collectStatus(LINEAGE, []);
|
|
227
267
|
|
|
268
|
+
// gentle-pi#638: the typed stop the provider renders once the slot is declared
|
|
269
|
+
// unachievable — no collect inputs, one withdraw binding per declared slot.
|
|
270
|
+
// Built in the same plain-object style as collectStatus: typed fields for the
|
|
271
|
+
// controller, raw fields for the envelope passthrough.
|
|
272
|
+
function unachievableStopStatus(lineageId, lens, order) {
|
|
273
|
+
const subjectHash = `sha256:${String(order).repeat(64)}`;
|
|
274
|
+
const repositoryContext = `rctx1_${"e".repeat(64)}`;
|
|
275
|
+
const withdrawArguments = [
|
|
276
|
+
{ name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
|
|
277
|
+
{ name: "expected-revision", value: SHA, token: `--expected-revision=${SHA}` },
|
|
278
|
+
{ name: "target", value: SHA, token: `--target=${SHA}` },
|
|
279
|
+
{ name: "repository-context", value: repositoryContext, token: `--repository-context=${repositoryContext}` },
|
|
280
|
+
{ name: "request-hash", value: subjectHash, token: `--request-hash=${subjectHash}` },
|
|
281
|
+
{ name: "withdraw", value: "true", token: "--withdraw=true" },
|
|
282
|
+
];
|
|
283
|
+
const withdrawCommand = `gentle-ai review capture-unachievable --lineage=${lineageId} --expected-revision=${SHA} --target=${SHA} --repository-context=${repositoryContext} --request-hash=${subjectHash} --withdraw=true`;
|
|
284
|
+
return {
|
|
285
|
+
...collectStatus(lineageId, []),
|
|
286
|
+
nextTransition: {
|
|
287
|
+
kind: "stop",
|
|
288
|
+
reasonCode: "unachievable_lens_slot",
|
|
289
|
+
unachievableLensSlots: [
|
|
290
|
+
{
|
|
291
|
+
lens,
|
|
292
|
+
selectedOrder: order,
|
|
293
|
+
subjectHash,
|
|
294
|
+
reason: "relay_transport_bound_exceeded",
|
|
295
|
+
withdraw: {
|
|
296
|
+
operation: "review.capture-unachievable",
|
|
297
|
+
command: withdrawCommand,
|
|
298
|
+
arguments: withdrawArguments,
|
|
299
|
+
binding: { targetIdentity: SHA, lineageId, revision: SHA },
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
raw: {
|
|
305
|
+
schema: "gentle-ai.review-integration.status/v5",
|
|
306
|
+
action: "stop",
|
|
307
|
+
lineage_id: lineageId,
|
|
308
|
+
target_identity: SHA,
|
|
309
|
+
next_transition: {
|
|
310
|
+
kind: "stop",
|
|
311
|
+
reason_code: "unachievable_lens_slot",
|
|
312
|
+
unachievable_lens_slots: [
|
|
313
|
+
{
|
|
314
|
+
lens,
|
|
315
|
+
selected_order: order,
|
|
316
|
+
subject_hash: subjectHash,
|
|
317
|
+
reason: "relay_transport_bound_exceeded",
|
|
318
|
+
withdraw: {
|
|
319
|
+
operation: "review.capture-unachievable",
|
|
320
|
+
command: withdrawCommand,
|
|
321
|
+
arguments: withdrawArguments,
|
|
322
|
+
binding: { lineage_id: lineageId, revision: SHA, target_identity: SHA, repository_context: repositoryContext },
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const UNACHIEVABLE_STOP = unachievableStopStatus(LINEAGE, LENS, ORDER);
|
|
331
|
+
|
|
332
|
+
// gentle-pi#638: the declared slot must not survive the restart as a reoffer.
|
|
333
|
+
// Process A declares after the deterministic bound kill; fresh Process B sees
|
|
334
|
+
// the provider's typed stop with the withdraw hint instead of the public
|
|
335
|
+
// collectBinding it would have seen before the declaration.
|
|
336
|
+
test("an unachievable declaration survives the controller restart as a typed stop, not a reoffer", async (t) => {
|
|
337
|
+
const cwd = repository(t);
|
|
338
|
+
|
|
339
|
+
// Process A: the deterministic relay bound kills the reviewer; the capture
|
|
340
|
+
// path declares the slot unachievable and renders the provider's stop. The
|
|
341
|
+
// worker always runs INSPECT first, so the queue carries the pending collect
|
|
342
|
+
// twice (inspect + capture selection) before the post-declaration stop.
|
|
343
|
+
const declared = runWorker(t, cwd, [PENDING, PENDING, UNACHIEVABLE_STOP], "capture-timeout");
|
|
344
|
+
assert.equal(declared.error, undefined, `declared run threw: ${JSON.stringify(declared.error)}`);
|
|
345
|
+
assert.equal(declared.captureCalls, 1, "Process A invokes one capture");
|
|
346
|
+
assert.equal(declared.relayRequests.length, 1, "Process A launches one relay transport");
|
|
347
|
+
assert.equal(declared.unachievableCalls.length, 1, "the declaration is recorded exactly once");
|
|
348
|
+
assert.deepEqual(declared.unachievableCalls, [{ cwd, lineageId: LINEAGE, targetIdentity: SHA, expectedRevision: SHA, requestHash: `sha256:${String(ORDER).repeat(64)}`, reason: "relay_transport_bound_exceeded", detail: "killed after 2256004ms against a 2256000ms relay bound", repositoryContext: `rctx1_${"e".repeat(64)}` }]);
|
|
349
|
+
assert.equal(declared.statusCalls.length, 3, "inspect and selection STATUS plus exactly one bound re-query");
|
|
350
|
+
assert.equal(declared.result.tool, "gentle_review_capture");
|
|
351
|
+
assert.equal(declared.result.status, "blocked");
|
|
352
|
+
assert.equal(declared.result.outcome, "unachievable-lens-slot-declared");
|
|
353
|
+
assert.equal(declared.result.mutation_performed, true);
|
|
354
|
+
assert.match(String(declared.result.next_action), /withdraw/);
|
|
355
|
+
|
|
356
|
+
// Process B (fresh): the provider's STATUS no longer reoffers the binding;
|
|
357
|
+
// INSPECT renders the typed stop whose hint is the withdraw command.
|
|
358
|
+
const inspect = runWorker(t, cwd, [UNACHIEVABLE_STOP], "inspect");
|
|
359
|
+
assert.equal(inspect.error, undefined, `inspect run threw: ${JSON.stringify(inspect.error)}`);
|
|
360
|
+
assert.equal(inspect.captureCalls, 0, "no capture runs without a reoffered binding");
|
|
361
|
+
assert.equal(inspect.relayRequests.length, 0, "INSPECT never launches the relay");
|
|
362
|
+
assert.equal(inspect.unachievableCalls.length, 0, "INSPECT never declares");
|
|
363
|
+
assert.equal(inspectCollectBinding(inspect.result), undefined, "the declared slot is not reoffered after restart");
|
|
364
|
+
assert.equal(inspect.result.status, "blocked");
|
|
365
|
+
assert.match(String(inspect.result.hint), /capture-unachievable/);
|
|
366
|
+
assert.match(String(inspect.result.hint), /--withdraw=true/);
|
|
367
|
+
});
|
|
368
|
+
|
|
228
369
|
test("a fresh controller process reoffers the exact public binding before one capture closes", async (t) => {
|
|
229
370
|
const cwd = repository(t);
|
|
230
371
|
|