gentle-pi 2.3.0 → 2.5.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 +195 -11
- package/assets/agents/gentle-ai-worker.md +9 -0
- package/assets/agents/sdd-explore.md +1 -0
- package/assets/orchestrator-delegation.md +21 -10
- package/assets/orchestrator.md +8 -12
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
- package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/README.md +10 -0
- package/contracts/review-provider-contract-mirror/v1.2.0/bundle/manifest.json +74 -0
- package/contracts/review-provider-contract-mirror/v1.2.0/bundle/orchestration/pi.md +53 -0
- package/contracts/review-provider-contract-mirror/v1.2.0/bundle/schemas/targeted-validator.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/generated/provider-capabilities.baseline.json +9 -2
- package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/generated/provider-roles.baseline.json +2 -2
- package/docs/delegated-verification.md +25 -0
- package/docs/review-integration.md +1 -1
- package/docs/telemetry.md +38 -0
- package/extensions/ask-user-choice.ts +26 -20
- package/extensions/codegraph-tools.ts +94 -5
- package/extensions/gentle-agents.ts +588 -0
- package/extensions/gentle-ai.ts +1421 -143
- package/extensions/gentle-shell.ts +547 -0
- package/extensions/gentle-todo.ts +199 -0
- package/extensions/quiet-tools.ts +1 -1
- package/lib/agent-home.ts +8 -0
- package/lib/agents-config.ts +318 -0
- package/lib/agents-history.ts +80 -0
- package/lib/agents-protocol.ts +429 -0
- package/lib/agents-runner.ts +490 -0
- package/lib/agents-transcript.ts +87 -0
- package/lib/agents-view.ts +557 -0
- package/lib/agents-widget.ts +222 -0
- package/lib/gentle-ai-renderer.ts +142 -26
- 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 +103 -12
- package/lib/provider-contract-bundle.ts +88 -6
- package/lib/review-candidate-view-owner.ts +177 -0
- package/lib/review-candidate-view.ts +127 -35
- package/lib/review-consent-ui.ts +65 -0
- package/lib/review-host-relay.ts +146 -60
- package/lib/review-integration-v2.ts +92 -13
- package/lib/review-last-event-controller.ts +1 -0
- package/lib/review-relay-contract.ts +11 -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 +219 -0
- package/lib/sdd-preflight.ts +2 -2
- package/lib/shell-bar.ts +138 -0
- package/lib/shell-card.ts +136 -0
- package/lib/shell-changes-view.ts +205 -0
- package/lib/shell-changes.ts +210 -0
- package/lib/shell-gauge.ts +40 -0
- package/lib/shell-prompt.ts +119 -0
- package/lib/shell-todo.ts +280 -0
- package/lib/shell-usage-view.ts +76 -0
- package/lib/shell-usage.ts +246 -0
- package/lib/telemetry-trigger.ts +151 -0
- package/package.json +4 -4
- package/runtime/native-review-cli.mjs +102 -11
- package/runtime/review-integration-v2.mjs +92 -13
- package/runtime/review-relay-contract.mjs +11 -0
- package/runtime/review-risk-assessment.mjs +340 -0
- package/runtime/telemetry-trigger.mjs +152 -0
- package/scripts/build-runtime-modules.mjs +2 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/test-packed-runner.mjs +22 -0
- package/scripts/verify-package-files.mjs +18 -13
- package/skills/_shared/review-ledger-contract.md +9 -1
- package/skills/issue-creation/SKILL.md +53 -93
- package/tests/agents-config.test.ts +143 -0
- package/tests/agents-fake-child.ts +52 -0
- package/tests/agents-history.test.ts +54 -0
- package/tests/agents-protocol.test.ts +153 -0
- package/tests/agents-runner-process.test.ts +111 -0
- package/tests/agents-runner.test.ts +402 -0
- package/tests/agents-transcript.test.ts +30 -0
- package/tests/agents-view.test.ts +274 -0
- package/tests/agents-widget.test.ts +111 -0
- package/tests/ask-user-choice.test.ts +157 -3
- package/tests/codegraph-tools.test.ts +110 -1
- package/tests/devbinary/native-review-parity.devtest.ts +108 -0
- package/tests/fixtures/agents-process-child.mjs +23 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/README.md +22 -0
- package/{contracts/review-provider-contract-mirror/v1.1.0/bundle → tests/fixtures/provider-contract-bundle/v1.2.0}/manifest.json +11 -2
- package/tests/fixtures/provider-contract-bundle/v1.2.0/orchestration/pi.md +97 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/targeted-validator.json +1 -0
- package/tests/gentle-agents.test.ts +741 -0
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-installer.test.ts +47 -47
- package/tests/gentle-ai-renderer.test.ts +65 -0
- package/tests/gentle-ai.test.ts +31 -14
- package/tests/gentle-card-text.ts +35 -0
- package/tests/gentle-shell.test.ts +527 -0
- package/tests/gentle-todo.test.ts +182 -0
- package/tests/issue-creation-skill.test.ts +103 -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 +33 -1
- package/tests/native-review-cli.test.ts +40 -0
- 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 +29 -22
- package/tests/orchestrator-budget.test.ts +71 -2
- package/tests/orchestrator-rdd-ownership.test.ts +10 -1
- package/tests/package-manifest.test.ts +134 -9
- package/tests/provider-contract-bundle.test.ts +76 -0
- package/tests/provider-contract-mirror.test.ts +19 -0
- package/tests/quiet-tool-rendering.test.ts +96 -37
- package/tests/rdd-aware-verification-contract.test.ts +216 -0
- package/tests/rdd-status-line.test.ts +286 -0
- package/tests/review-agent-end-preflight.test.ts +408 -0
- package/tests/review-candidate-view.test.ts +452 -6
- package/tests/review-contract-prompt.test.ts +142 -0
- package/tests/review-controller-native-recovery.test.ts +29 -4
- package/tests/review-controller-native-routing.test.ts +321 -4
- package/tests/review-controller-workspace-root.test.ts +45 -2
- package/tests/review-controller.test.ts +26 -1
- package/tests/review-host-relay-routing.test.ts +229 -11
- package/tests/review-host-relay.test.ts +195 -7
- package/tests/review-integration-v2-forward.test.ts +47 -0
- package/tests/review-integration-v2.test.ts +112 -0
- package/tests/review-last-event-closure.test.ts +7 -2
- package/tests/review-ledger-contract.test.ts +1 -1
- package/tests/review-relay-contract.test.ts +26 -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 +608 -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 +126 -0
- package/tests/runtime-harness.mjs +1 -0
- package/tests/shell-bar.test.ts +176 -0
- package/tests/shell-card.test.ts +118 -0
- package/tests/shell-changes-view.test.ts +146 -0
- package/tests/shell-changes.test.ts +182 -0
- package/tests/shell-prompt.test.ts +118 -0
- package/tests/shell-todo.test.ts +170 -0
- package/tests/shell-usage-view.test.ts +62 -0
- package/tests/shell-usage.test.ts +197 -0
- package/tests/telemetry-trigger.test.ts +349 -0
- package/tests/writer-edit-surface-scope.test.ts +153 -17
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/targeted-validator.json +0 -0
- /package/{contracts/review-provider-contract-mirror/v1.1.0/bundle → tests/fixtures/provider-contract-bundle/v1.2.0}/schemas/targeted-validator.schema.json +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
|
-
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { chmodSync, existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir, 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
8
|
import codeGraphTools, {
|
|
9
9
|
createCodeGraphTool,
|
|
10
|
+
codeGraphNodeScript,
|
|
11
|
+
findCodeGraphNodeScriptOnPath,
|
|
10
12
|
type CodeGraphRunner,
|
|
11
13
|
} from "../extensions/codegraph-tools.ts";
|
|
12
14
|
|
|
@@ -211,3 +213,110 @@ test("CodeGraph tool registration exposes a single constrained custom tool", ()
|
|
|
211
213
|
},
|
|
212
214
|
});
|
|
213
215
|
});
|
|
216
|
+
|
|
217
|
+
function writeCodeGraphPackage(binDir: string, scriptSource = "#!/usr/bin/env node\n"): string {
|
|
218
|
+
const pkgRoot = join(binDir, "node_modules", "@colbymchenry", "codegraph");
|
|
219
|
+
mkdirSync(pkgRoot, { recursive: true });
|
|
220
|
+
const scriptTarget = join(pkgRoot, "npm-shim.js");
|
|
221
|
+
writeFileSync(scriptTarget, scriptSource);
|
|
222
|
+
writeFileSync(
|
|
223
|
+
join(pkgRoot, "package.json"),
|
|
224
|
+
JSON.stringify({
|
|
225
|
+
name: "@colbymchenry/codegraph",
|
|
226
|
+
version: "1.0.0",
|
|
227
|
+
bin: { codegraph: "npm-shim.js" },
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
230
|
+
writeFileSync(
|
|
231
|
+
join(binDir, "codegraph.cmd"),
|
|
232
|
+
"@ECHO off\nnode \"%~dp0\\node_modules\\@colbymchenry\\codegraph\\npm-shim.js\" %*\n",
|
|
233
|
+
);
|
|
234
|
+
return scriptTarget;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function withWindowsPath(t: test.TestContext, path: string): void {
|
|
238
|
+
const previousPath = process.env.PATH;
|
|
239
|
+
const previousWindowsPath = process.env.Path;
|
|
240
|
+
const windowsPath = process.platform === "win32"
|
|
241
|
+
? `${path};${previousWindowsPath ?? previousPath ?? ""}`
|
|
242
|
+
: path;
|
|
243
|
+
process.env.Path = windowsPath;
|
|
244
|
+
process.env.PATH = windowsPath;
|
|
245
|
+
t.after(() => {
|
|
246
|
+
if (previousPath === undefined) delete process.env.PATH;
|
|
247
|
+
else process.env.PATH = previousPath;
|
|
248
|
+
if (previousWindowsPath === undefined) delete process.env.Path;
|
|
249
|
+
else process.env.Path = previousWindowsPath;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
test("Windows shim helpers skip stale PATH shims before resolving the npm script", (t) => {
|
|
254
|
+
const staleBinDir = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-stale-bin-")));
|
|
255
|
+
const validBinDir = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-valid-bin-")));
|
|
256
|
+
t.after(() => rmSync(staleBinDir, { recursive: true, force: true }));
|
|
257
|
+
t.after(() => rmSync(validBinDir, { recursive: true, force: true }));
|
|
258
|
+
writeFileSync(join(staleBinDir, "codegraph.cmd"), "@ECHO off\n");
|
|
259
|
+
const stalePkgRoot = join(staleBinDir, "node_modules", "@colbymchenry", "codegraph");
|
|
260
|
+
mkdirSync(stalePkgRoot, { recursive: true });
|
|
261
|
+
writeFileSync(join(stalePkgRoot, "package.json"), JSON.stringify({ bin: { codegraph: "." } }));
|
|
262
|
+
const scriptTarget = writeCodeGraphPackage(validBinDir);
|
|
263
|
+
|
|
264
|
+
withWindowsPath(t, `${staleBinDir};${validBinDir}`);
|
|
265
|
+
|
|
266
|
+
assert.equal(codeGraphNodeScript(join(staleBinDir, "codegraph.cmd")), undefined);
|
|
267
|
+
assert.equal(findCodeGraphNodeScriptOnPath(), scriptTarget);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("Windows shim helpers resolve quoted PATH entries in order without stripping unmatched quotes", (t) => {
|
|
271
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-quoted-bin-")));
|
|
272
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
273
|
+
const firstBin = join(root, "Program Files", "nodejs");
|
|
274
|
+
const secondBin = join(root, "other-bin");
|
|
275
|
+
const firstScript = writeCodeGraphPackage(firstBin);
|
|
276
|
+
const secondScript = writeCodeGraphPackage(secondBin);
|
|
277
|
+
withWindowsPath(t, `"${firstBin}";${secondBin}`);
|
|
278
|
+
|
|
279
|
+
assert.equal(findCodeGraphNodeScriptOnPath(), firstScript);
|
|
280
|
+
for (const [path, expected] of [
|
|
281
|
+
[`${firstBin};"${secondBin}"`, firstScript],
|
|
282
|
+
[`"${firstBin};${secondBin}`, secondScript],
|
|
283
|
+
[`${firstBin}";${secondBin}`, secondScript],
|
|
284
|
+
[`${secondBin};"${firstBin}"`, secondScript],
|
|
285
|
+
]) {
|
|
286
|
+
process.env.Path = path;
|
|
287
|
+
process.env.PATH = path;
|
|
288
|
+
assert.equal(findCodeGraphNodeScriptOnPath(), expected);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("Windows launcher passes command metacharacters as one literal argv value", async (t) => {
|
|
293
|
+
const binDir = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-bin-")));
|
|
294
|
+
t.after(() => rmSync(binDir, { recursive: true, force: true }));
|
|
295
|
+
writeCodeGraphPackage(binDir, "process.stdout.write(JSON.stringify(process.argv.slice(2)));\n");
|
|
296
|
+
const cwd = workspace(t);
|
|
297
|
+
if (process.platform !== "win32") {
|
|
298
|
+
const gitShim = join(binDir, "git");
|
|
299
|
+
writeFileSync(gitShim, "#!/bin/sh\nprintf '%s\\n' \"$PWD\"\n");
|
|
300
|
+
chmodSync(gitShim, 0o755);
|
|
301
|
+
}
|
|
302
|
+
withWindowsPath(t, binDir);
|
|
303
|
+
|
|
304
|
+
const previousPlatform = process.platform;
|
|
305
|
+
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
|
|
306
|
+
t.after(() => Object.defineProperty(process, "platform", { configurable: true, value: previousPlatform }));
|
|
307
|
+
|
|
308
|
+
const query = "literal & echo injected > shell-injection.txt";
|
|
309
|
+
const result = await createCodeGraphTool().execute(
|
|
310
|
+
"test",
|
|
311
|
+
{ operation: "query", query },
|
|
312
|
+
undefined,
|
|
313
|
+
undefined,
|
|
314
|
+
{ cwd } as ExtensionContext,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
assert.deepEqual(result.content, [{
|
|
318
|
+
type: "text",
|
|
319
|
+
text: JSON.stringify(["query", "--path", cwd, "--limit", "10", "--", query]),
|
|
320
|
+
}]);
|
|
321
|
+
assert.equal(existsSync(join(cwd, "shell-injection.txt")), false);
|
|
322
|
+
});
|
|
@@ -5,6 +5,7 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import test from "node:test";
|
|
7
7
|
import { __testing } from "../../extensions/gentle-ai.ts";
|
|
8
|
+
import { CandidateViewRegistry } from "../../lib/review-candidate-view.ts";
|
|
8
9
|
import {
|
|
9
10
|
NATIVE_REVIEW_ERROR_CODE,
|
|
10
11
|
NativeReviewCliError,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
createNodeExecFileAdapter,
|
|
14
15
|
type ExecFileAdapter,
|
|
15
16
|
} from "../../lib/native-review-cli.ts";
|
|
17
|
+
import type { ReviewStatusV3 } from "../../lib/review-integration-v2.ts";
|
|
16
18
|
import { requireDevBinary } from "../support/native-binary-gate.ts";
|
|
17
19
|
|
|
18
20
|
// Organic RDD Parity: candidate-bound dev-binary journeys.
|
|
@@ -43,6 +45,35 @@ function bridgeAdapter(binary: string, calls: NativeCall[]): ExecFileAdapter {
|
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
function selectedStatusFieldNames(stdout: string): string {
|
|
49
|
+
try {
|
|
50
|
+
const status = JSON.parse(stdout) as Record<string, unknown>;
|
|
51
|
+
const projection = status.projection as Record<string, unknown> | undefined;
|
|
52
|
+
const transition = status.next_transition as Record<string, unknown> | undefined;
|
|
53
|
+
const execute = transition?.execute as Record<string, unknown> | undefined;
|
|
54
|
+
const argument = Array.isArray(execute?.arguments) ? execute.arguments[0] as Record<string, unknown> | undefined : undefined;
|
|
55
|
+
const names = (prefix: string, value?: Record<string, unknown>) => value === undefined ? [] : Object.keys(value).sort().map((key) => `${prefix}.${key}`);
|
|
56
|
+
return [...names("status", status), ...names("status.projection", projection), ...names("status.next_transition", transition), ...names("status.next_transition.execute", execute), ...names("status.next_transition.execute.arguments[]", argument)].join(", ");
|
|
57
|
+
} catch {
|
|
58
|
+
return "status JSON";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function controllerResultStructure(result: Record<string, unknown>): string {
|
|
63
|
+
const diagnostics = typeof result.diagnostics === "object" && result.diagnostics !== null && !Array.isArray(result.diagnostics)
|
|
64
|
+
? result.diagnostics as Record<string, unknown>
|
|
65
|
+
: undefined;
|
|
66
|
+
const safeCode = (name: string, value: unknown) => typeof value === "string" && /^[a-z0-9][a-z0-9._-]*$/.test(value) ? `${name}=${value}` : undefined;
|
|
67
|
+
const safeBooleanNames = ["candidate_view_created", "candidate_view_pre_native", "native_invocation_attempted", "lineage_created", "mutation_performed", "reset_eligible"];
|
|
68
|
+
return [
|
|
69
|
+
`keys=[${Object.keys(result).sort().join(",")}]`,
|
|
70
|
+
...(diagnostics === undefined ? [] : [`diagnostics.keys=[${Object.keys(diagnostics).sort().join(",")}]`]),
|
|
71
|
+
safeCode("operation", result.operation), safeCode("status", result.status), safeCode("outcome", result.outcome), safeCode("reason", result.reason),
|
|
72
|
+
safeCode("diagnostics.error_code", diagnostics?.error_code), safeCode("diagnostics.code", diagnostics?.code), safeCode("diagnostics.reason", diagnostics?.reason),
|
|
73
|
+
...safeBooleanNames.flatMap((name) => typeof result[name] === "boolean" ? [`${name}=${String(result[name])}`] : []),
|
|
74
|
+
].filter((value): value is string => value !== undefined).join("; ");
|
|
75
|
+
}
|
|
76
|
+
|
|
46
77
|
function journeyNative(binary: string): { native: NativeReviewCliV216; calls: NativeCall[] } {
|
|
47
78
|
const calls: NativeCall[] = [];
|
|
48
79
|
return { native: new NativeReviewCliV216(bridgeAdapter(binary, calls), binary), calls };
|
|
@@ -202,6 +233,83 @@ test("dev-binary: an empty candidate exposes the current STATUS refusal and neve
|
|
|
202
233
|
assert.equal(calls.filter((arguments_) => arguments_.at(0) === "review" && arguments_.at(1) === "start").length, 0);
|
|
203
234
|
});
|
|
204
235
|
|
|
236
|
+
test("dev-binary: v7 intended-untracked selection decodes both selected statuses before START", { skip: !RUNNABLE }, async (t) => {
|
|
237
|
+
const cwd = repository(t);
|
|
238
|
+
writeFileSync(join(cwd, "app.ts"), "export const value = 1;\n");
|
|
239
|
+
git(cwd, "add", "app.ts");
|
|
240
|
+
git(cwd, "commit", "-qm", "test: base candidate");
|
|
241
|
+
writeFileSync(join(cwd, "app.ts"), "export const value = 2;\n");
|
|
242
|
+
writeFileSync(join(cwd, "chosen.md"), "# Chosen\n");
|
|
243
|
+
|
|
244
|
+
const calls: NativeCall[] = [], selectedWireFields: string[] = [];
|
|
245
|
+
let interceptedStarts = 0;
|
|
246
|
+
let delegatedStarts = 0;
|
|
247
|
+
const startIntercept = new Error("test-only native START intercept");
|
|
248
|
+
const real = createNodeExecFileAdapter();
|
|
249
|
+
const adapter: ExecFileAdapter = async (request) => {
|
|
250
|
+
calls.push([...request.arguments]);
|
|
251
|
+
if (request.arguments.at(0) === "review" && request.arguments.at(1) === "start") {
|
|
252
|
+
interceptedStarts += 1;
|
|
253
|
+
throw startIntercept;
|
|
254
|
+
}
|
|
255
|
+
if (request.arguments.at(0) === "review" && request.arguments.at(1) === "start") delegatedStarts += 1;
|
|
256
|
+
const result = await real({ ...request, file: DEV_BINARY! });
|
|
257
|
+
if (request.arguments.some((argument) => argument.startsWith("--intended-untracked-selection="))) selectedWireFields.push(selectedStatusFieldNames(result.stdout));
|
|
258
|
+
return result;
|
|
259
|
+
};
|
|
260
|
+
const native = new NativeReviewCliV216(adapter, DEV_BINARY!);
|
|
261
|
+
const targetStatus = native.targetStatus.bind(native);
|
|
262
|
+
const selectedStatuses: ReviewStatusV3[] = [];
|
|
263
|
+
const selectedFailures: Array<{ call: number; code: string }> = [];
|
|
264
|
+
let selectedStatusCalls = 0;
|
|
265
|
+
native.targetStatus = async (request) => {
|
|
266
|
+
if (request.intendedUntrackedSelection === undefined) return targetStatus(request);
|
|
267
|
+
selectedStatusCalls += 1;
|
|
268
|
+
try {
|
|
269
|
+
const status = await targetStatus(request);
|
|
270
|
+
selectedStatuses.push(status);
|
|
271
|
+
return status;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
selectedFailures.push({ call: selectedStatusCalls, code: error instanceof NativeReviewCliError ? error.code : "unexpected-error" });
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const candidateViews = new CandidateViewRegistry(), retainedSelections = new Map();
|
|
279
|
+
await enableReview(native, cwd);
|
|
280
|
+
const initial = await __testing.executeReviewControllerOperation({ operation: "status" }, cwd, native, undefined, candidateViews, undefined, retainedSelections);
|
|
281
|
+
assert.equal((initial.result as Record<string, unknown> | undefined)?.schema, "gentle-ai.review-integration.status/v7", "initial status.schema");
|
|
282
|
+
assert.equal(typeof initial.selectionBinding, "string", "initial selectionBinding");
|
|
283
|
+
const result = await __testing.executeReviewControllerOperation({ operation: "select-intended-untracked", selectionBinding: initial.selectionBinding as string, intendedUntracked: ["chosen.md"] } as never, cwd, native, undefined, candidateViews, undefined, retainedSelections);
|
|
284
|
+
|
|
285
|
+
if (selectedStatusCalls !== 2) assert.fail(`intended-selection STATUS call count=${selectedStatusCalls}; ${controllerResultStructure(result)}`);
|
|
286
|
+
if (selectedFailures.length > 0) {
|
|
287
|
+
const failure = selectedFailures[0]!;
|
|
288
|
+
assert.fail(`selected STATUS call ${failure.call} decode failed at fields: ${selectedWireFields[failure.call - 1] ?? "status"}; error=${failure.code}`);
|
|
289
|
+
}
|
|
290
|
+
assert.equal(selectedStatusCalls, 2, "intended-selection STATUS call count");
|
|
291
|
+
assert.equal(selectedStatuses.length, 2, "decoded intended-selection STATUS count");
|
|
292
|
+
for (const [index, status] of selectedStatuses.entries()) {
|
|
293
|
+
assert.equal(status.raw.schema, "gentle-ai.review-integration.status/v7", `selected STATUS call ${index + 1} status.schema`);
|
|
294
|
+
assert.deepEqual(status.projection.intendedUntracked, ["chosen.md"], `selected STATUS call ${index + 1} status.projection.intended_untracked`);
|
|
295
|
+
assert.equal(status.nextTransition?.kind, "execute", `selected STATUS call ${index + 1} status.next_transition.kind`);
|
|
296
|
+
assert.equal(status.nextTransition?.execute?.operation, "review.start", `selected STATUS call ${index + 1} status.next_transition.execute.operation`);
|
|
297
|
+
assert.ok((status.nextTransition?.execute?.arguments.length ?? 0) > 0, `selected STATUS call ${index + 1} status.next_transition.execute.arguments`);
|
|
298
|
+
assert.equal(status.nextTransition?.execute?.arguments.every((argument) => typeof argument.token === "string" && argument.token.length > 0), true, `selected STATUS call ${index + 1} status.next_transition.execute.arguments[].token`);
|
|
299
|
+
}
|
|
300
|
+
assert.equal(interceptedStarts, 1, "adapter must intercept the one native review.start attempt");
|
|
301
|
+
assert.equal(delegatedStarts, 0, "intercepted native START must not reach the real adapter");
|
|
302
|
+
assert.equal(calls.filter((arguments_) => arguments_.at(0) === "review" && arguments_.at(1) === "start").length, 1, "native review.start adapter attempt count");
|
|
303
|
+
assert.equal(result.outcome, "native-mutation-status-reconciled", "controller result after intentional START intercept");
|
|
304
|
+
assert.equal("lineage_id" in result, false, "intercepted START must report no lineage binding");
|
|
305
|
+
assert.equal("authority" in result, false, "intercepted START must report no authority binding");
|
|
306
|
+
assert.equal("lineage_created" in result, false, "reconciled unknown mutation must not claim lineage creation");
|
|
307
|
+
assert.equal("mutation_performed" in result, false, "reconciled unknown mutation must not claim a mutation result");
|
|
308
|
+
assert.equal(result.mutation_outcome, "unknown", "the controller cannot prove mutation status after an intercepted launch");
|
|
309
|
+
const after = await native.targetStatus({ cwd, agent: "pi" });
|
|
310
|
+
assert.equal(after.authority, undefined, "post-intercept STATUS must not expose authority");
|
|
311
|
+
});
|
|
312
|
+
|
|
205
313
|
test("dev-binary: a low-risk START closes the review with no receipt or follow-up capture", { skip: !RUNNABLE }, async (t) => {
|
|
206
314
|
const cwd = repository(t);
|
|
207
315
|
writeFileSync(join(cwd, "guide.md"), "# Guide\n\nBase\n");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
const descendant = spawn(process.execPath, ["-e", "process.on('SIGTERM', () => {}); process.stdout.write('ready'); setInterval(() => {}, 1000);"], { stdio: ["ignore", "pipe", "ignore"] });
|
|
4
|
+
descendant.stdout.once("data", () => {
|
|
5
|
+
console.log(`DESCENDANT:${descendant.pid}`);
|
|
6
|
+
if (process.env.AGENTS_PROCESS_CHILD_EXIT_AFTER_READY === "1") process.exit(0);
|
|
7
|
+
});
|
|
8
|
+
process.on("SIGTERM", () => {
|
|
9
|
+
if (process.env.AGENTS_PROCESS_CHILD_EXIT_ON_TERM === "1") process.exit(0);
|
|
10
|
+
});
|
|
11
|
+
process.stdin.setEncoding("utf8");
|
|
12
|
+
process.stdin.on("data", (chunk) => {
|
|
13
|
+
for (const line of chunk.split("\n")) {
|
|
14
|
+
if (!line) continue;
|
|
15
|
+
try {
|
|
16
|
+
const command = JSON.parse(line);
|
|
17
|
+
process.stdout.write(`${JSON.stringify({ type: "response", id: command.id, success: true })}\n`);
|
|
18
|
+
} catch {
|
|
19
|
+
// Ignore malformed input; the runner owns the RPC protocol.
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
setInterval(() => {}, 1000);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Gentle AI review provider contract
|
|
2
|
+
|
|
3
|
+
This data-only bundle describes the provider result contracts admitted by Gentle AI.
|
|
4
|
+
|
|
5
|
+
## Activation
|
|
6
|
+
|
|
7
|
+
1. Verify the signed release checksum manifest before using this archive.
|
|
8
|
+
2. Verify every listed file hash and the transport capability before activation.
|
|
9
|
+
3. Confirm your runtime identity appears in the manifest's registered runtimes before trusting the layout.
|
|
10
|
+
4. Pass the Go-materialized opaque prompt to the provider and return only raw output or an error.
|
|
11
|
+
|
|
12
|
+
## Orchestration
|
|
13
|
+
|
|
14
|
+
manifest.json's orchestration array lists, for closed runtimes only, one
|
|
15
|
+
orchestration/<runtime>.md file: the exact review execution contract text
|
|
16
|
+
Gentle AI's own installer would have spliced into that runtime's system
|
|
17
|
+
prompt, for a runtime whose adapter has no system prompt to splice it into.
|
|
18
|
+
It carries no executable content, same as every other file in this bundle: a
|
|
19
|
+
runtime mirrors and delivers the text as-is and still relies on Go for every
|
|
20
|
+
review decision, prompt, receipt, and delivery gate.
|
|
21
|
+
|
|
22
|
+
Go remains the admission authority for prompts, results, receipts, and delivery gates.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "gentle-ai.review-provider-contract-bundle/v1",
|
|
3
|
-
"contract_semver": "1.
|
|
3
|
+
"contract_semver": "1.2.0",
|
|
4
4
|
"transport_capability": "gentle-ai.provider-transport/v1",
|
|
5
5
|
"runtimes": [
|
|
6
6
|
"claude-code",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
],
|
|
11
11
|
"readme": {
|
|
12
12
|
"path": "README.md",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "18baab5ee79aefd0bc62a28da0dadcf2162544f57a940c5b859cd6bc4932a085"
|
|
14
14
|
},
|
|
15
15
|
"roles": [
|
|
16
16
|
{
|
|
@@ -61,5 +61,14 @@
|
|
|
61
61
|
"sha256": "7b5a5165e3a913863fb98d2ba5387811860c21333b5c37e6ed3b4e0d121cf2fa"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
],
|
|
65
|
+
"orchestration": [
|
|
66
|
+
{
|
|
67
|
+
"runtime": "pi",
|
|
68
|
+
"file": {
|
|
69
|
+
"path": "orchestration/pi.md",
|
|
70
|
+
"sha256": "54fba57b11b280f56f7f15e91ddad3d844c70e0127e5c1adeff0a9d1b7a98e64"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
64
73
|
]
|
|
65
74
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Native Compact Review Orchestration
|
|
2
|
+
|
|
3
|
+
**When a final capture returns `status_continuation`, execute its operation and ordered argument tokens unchanged. Do not reconstruct lifecycle selectors from retained prose or transcript state.**
|
|
4
|
+
|
|
5
|
+
The parent orchestrator coordinates one native transaction; reviewers, refuters, correction actors, and validators receive only their provider-issued role input. Prompt prose never creates authority or decides delivery.
|
|
6
|
+
|
|
7
|
+
## Entry rule
|
|
8
|
+
|
|
9
|
+
Enter this lifecycle once per candidate, after an authorized source-mutating implementation is complete and normalized and before reporting it complete, whenever the user-owned review switch is enabled (`gentle-ai review mode status` reads it without changing it). Run the selectorless STATUS in step 1 and route only from its returned `next_transition`; the START consent envelope lets the human decide this candidate, so never skip the preflight because the user did not ask for a review. Skip it only for a trivial passive documentation-only edit, when the user explicitly left this candidate unreviewed, or while a transaction is already bound to it. A runtime that runs this preflight itself hands the agent the exact returned START tokens and never runs START.
|
|
10
|
+
|
|
11
|
+
## Atomic lifecycle
|
|
12
|
+
|
|
13
|
+
1. **Preflight only.** Selectorless STATUS only preflights the current worktree candidate and returns one exact START invocation. It never discovers, resumes, recovers, or evaluates ambient authority from another lineage or worktree: `gentle-ai review status --cwd <repo> --contract gentle-ai.review-integration/v2 --agent pi --next-transition`.
|
|
14
|
+
|
|
15
|
+
2. **Freeze once.** Invoke only the returned START operation and its ordered tokens unchanged. START freezes one compact atomic transaction with an explicit lineage, worktree, and target binding. It ignores every other lineage and worktree. Capture the returned lineage, revision, and target tokens. An exact replay of an active START may return `replayed`; a genuinely new START is independent.
|
|
16
|
+
|
|
17
|
+
3. **Stay bound.** A reviewing START carries `next_transition.execute(review.status)` — the provider-issued re-entry for its frozen binding: run that provider-issued command verbatim, with the repository as process cwd, and satisfy every later STATUS and collection call only with the exact tokens each returned transition names. Route only from that transaction's returned `next_transition`; the root `action` field is informational. Never infer a command from prose, ambient state, a gate, or a stale reply. Do not start another lineage, reuse an acknowledged-and-burned lineage, or perform ambient recovery. For `execute`, run the exact operation and ordered arguments. For `collect`, satisfy only the named inputs and their exact capture operations, then ask STATUS again with the same binding. For `stop`, run no lifecycle operation.
|
|
18
|
+
|
|
19
|
+
4. **Acknowledge exactly.** Native Go owns frozen lenses, provider context and admission, refutation, one bounded correction, repository evidence, targeted validation, and approved closure. A final approved capture commits one pending acknowledgement token and returns `review.acknowledge-approved`; restarted STATUS returns the same operation, ordered arguments, token, and live revision. Only that exact invocation burns authority and artifacts, and on success it prints one `gentle-ai.review-acknowledged/v1` envelope; report the burn from that envelope, never from a later STATUS. Wrong, stale, or replayed acknowledgement refuses without creating a receipt, tombstone, witness, mirror, sidecar, or delivery authority. Unrelated transactions survive unchanged.
|
|
20
|
+
|
|
21
|
+
The final reviewer, refuter, or targeted-validator capture owns closure. A malformed, incomplete, or unavailable capture never reaches acknowledgement: issue one retained target-bound read-only STATUS and relaunch only when it reoffers the same bound slot. Never invent a binding or retry from prose.
|
|
22
|
+
|
|
23
|
+
When v2 returns `forecast`, relay it losslessly in the user's language: preserve every step's order and fields (`step`, `kind`, `reason_code`, `description`) and the horizon. Forecast is informational; route only from `next_transition`.
|
|
24
|
+
|
|
25
|
+
### Cross-repository lifecycle root
|
|
26
|
+
|
|
27
|
+
A session in repository A may review an explicitly selected nested target in unrelated repository B only after explicit user authorization. Native Go resolves the requested path to the canonical B worktree root; adapters never parse authorization or roots.
|
|
28
|
+
|
|
29
|
+
- After B is selected, retain canonical B as the lifecycle working root from selectorless STATUS through consent, collection, correction, targeted validation, acknowledgement, and burn. Do not fall back to A.
|
|
30
|
+
- Run provider-issued command tokens exactly. Never append, remove, or rebuild provider-issued command tokens. When a command omits `--cwd`, run it with process cwd B.
|
|
31
|
+
- Opaque `repository_context` can capture or materialize from any process cwd, but the host still retains B for lifecycle continuity. Go owns repository binding; adapters never parse authorization or roots.
|
|
32
|
+
- The same lineage text in A and B is independent. Approval awaits acknowledgement in B; exact acknowledgement burns B only, and A remains untouched.
|
|
33
|
+
|
|
34
|
+
This lifecycle is rendered for exactly Claude Code, Codex, OpenCode, and Pi. Unsupported runtimes remain unavailable before repository or authority mutation.
|
|
35
|
+
|
|
36
|
+
## Capture and correction
|
|
37
|
+
|
|
38
|
+
For each returned `review.capture-result` input, run the exact capture operation once. The reviewer prompt begins with the exact literal prefix `GENTLE_AI_REVIEW_BINDING ` (trailing space, never `=`), followed by one-line JSON assembled only from that input: `lineage`, `target`, `lens`, `order`, `revision` from `expected-revision`, `repository_context`, and `subject_hash` from `artifact_subject.subject_hash`; omit only provider-omitted fields. Return one JSON object that echoes `subject_hash`, reports completed inspection of every manifest path in order, and contains findings/evidence with severe evidence class and causality. Access failure is incomplete inspection, never completion.
|
|
39
|
+
|
|
40
|
+
After an empty, malformed, schema-invalid, access/provider-failed, or incomplete capture, query the same exact-lineage STATUS. Relaunch only if its fresh `next_transition` reoffers the same bound slot. Never infer a retry from transcript text. A relayed capture passes its result through `--input <path|->`, one per lens in lens order; BOM-less UTF-8 is required on Windows PowerShell 5.1. Tokens carrying `--agent` capture in process with no `--input`.
|
|
41
|
+
|
|
42
|
+
Only candidate-caused severe findings block. Pre-existing/base-only findings are follow-ups; unknown causality escalates. A deterministic blocker needs no refuter; inferential blockers share one read-only refuter batch. A four-lens review is long work: before its first lens, give one forecast covering four reviewer runs, the frozen correction budget, and the at-most-one bounded correction.
|
|
43
|
+
|
|
44
|
+
A correction is native-scoped. When the final reviewer or refuter capture opens `correction_required`, its `status_continuation` is the only re-entry: run its `review.status` operation and ordered tokens unchanged before acting again. Native Go maps edits only to corroborated frozen findings, owns repository evidence and the targeted validator, and permits at most one bounded correction. A validator that cannot inspect the immutable trees produced no verdict: surface one blocked human decision and submit nothing. Do not route it to a refuter or another actor without read-only immutable-tree access. Independent requirements/runtime verification never starts another reviewer, refuter, correction, or validator.
|
|
45
|
+
|
|
46
|
+
## Consent and immutable inspection
|
|
47
|
+
|
|
48
|
+
If exact provider-returned START returns the typed `gentle-ai.review-integration.consent/v3` envelope, relay it as a Lossless Blocking Prompt. Global RDD enabled permits review; it never grants consent for this candidate. For medium/high candidates, faithfully translate the headline, reason, `value`, risk evidence, choice labels, every choice `effect`, and the off-path note while preserving original groups/order, selection mode, allowed-answer domain, answer tokens, commands, target IDs, and invocations. Project `value` as benefits and every `effect` as consequences. Do not translate machine answer tokens (`granted`, `declined`). Run exactly the invocation selected by the human; a decline is candidate-scoped and is not the kill switch.
|
|
49
|
+
|
|
50
|
+
Claude Code, OpenCode, Codex, and Pi use the shared Go provider contract. Go owns frozen evidence, binding, schema, byte bounds, validation, admission, and capture; runtime adapters transport opaque provider output. Claude uses a tool-free fresh reviewer; OpenCode relays one host Task through its live Go transport; Codex uses its provider-bound subprocess; Pi uses its gentle-pi-owned relay. Compiled capability is authoritative before repository, target, authority, collection, or process work.
|
|
51
|
+
|
|
52
|
+
Reviewers inspect only the provider-bound immutable trees. Never hand candidate bytes through `/tmp`, an external file, a repository scratch file, or `GENTLE_AI_FROZEN_CANDIDATE_CONTEXT`. Use the provider-issued inspection path, never the live worktree, index, `HEAD`, or an unbound revision. Never pass `--binary`, change checkout, or substitute live files.
|
|
53
|
+
|
|
54
|
+
<!-- authority-first-terminal-procedure:start -->
|
|
55
|
+
### Authority-First Terminal Procedure
|
|
56
|
+
|
|
57
|
+
| Order | Operation | Required result |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| 01 | canonical initial STATUS above | exactly one current-worktree START preflight; no authority discovery |
|
|
60
|
+
| 02 | exact returned START | one compact lineage/worktree/target binding; retain lineage, revision, and target |
|
|
61
|
+
| 03 | exact-lineage STATUS and collect | only returned transaction actions; no ambient resume, reuse, or delivery gate |
|
|
62
|
+
| 04 | final admitted capture | native readback, approved authority, and one exact acknowledgement continuation |
|
|
63
|
+
| 05 | STATUS restart + exact acknowledgement | replayed operation/token/revision; only exact acknowledgement burns authority |
|
|
64
|
+
| 06 | terminal lifecycle stop | ordinary repository policy owns any later delivery decision |
|
|
65
|
+
|
|
66
|
+
<!-- authority-first-terminal-procedure:end -->
|
|
67
|
+
|
|
68
|
+
### Continue after a stop reason code
|
|
69
|
+
|
|
70
|
+
A `stop` ends its transition, never approves delivery. Complete atomic inventory: B stays target root; gates stay unmanaged. `D` means `gentle-ai review mode disable --scope clone --cwd <B>`; B ordinary policy decides delivery. `S` means re-query the exact captured target-root STATUS command with lineage and target.
|
|
71
|
+
|
|
72
|
+
| Reason codes | Continuation |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| `captured_artifacts_unverifiable` | Terminal — maintainer inspects B authority, or `D`. |
|
|
75
|
+
| `captured_result_selection_unavailable` | Terminal — maintainer inspects lineage, or `D`. |
|
|
76
|
+
| `missing_authority_binding` | Terminal — file a bounded defect with lineage, or `D`. |
|
|
77
|
+
| `corrupted_or_unverifiable_authority`, `manual_intervention_required`, `native_stop_required` | Terminal — maintainer inspects authority/lineage, or `D`. |
|
|
78
|
+
| `empty_base_diff_bootstrap_required` | Terminal — authorized empty-root bootstrap for a new target, or `D`. |
|
|
79
|
+
| `lens_context_budget_exceeded` | Terminal — reduce B scope and start a new transaction, or `D`. |
|
|
80
|
+
| `staged_workspace_overlay_recovery_unavailable` | Terminal — pass `--lineage <id>` to recover, or drop `--workspace-overlay` and start fresh; otherwise `D`. |
|
|
81
|
+
| `corrected_candidate_unavailable` | Change B correction candidate, then `S`; do not reuse the pre-correction target. |
|
|
82
|
+
| `recovery_scope_unchanged` | Change B target identity, then retry the exact returned `gentle-ai review recover`. |
|
|
83
|
+
| `rdd_disabled` | `--scope clone` only clears a clone-local off; `gentle-ai review mode enable --scope global`, then `S`. |
|
|
84
|
+
|
|
85
|
+
## Delivery follows ordinary repository policy
|
|
86
|
+
|
|
87
|
+
Shipped `review validate` and gate commands are compatibility/informational only. They never discover authority or decide delivery: enabled gates return `invalidated/unmanaged`; disabled gates return `disabled/unmanaged`. They never allow, approve, block, commit, push, open a PR, or govern release.
|
|
88
|
+
|
|
89
|
+
After exact acknowledgement burns terminal `approved` authority, the review lifecycle stops. Commit, push, PR, and release remain separate human decisions under ordinary repository policy. A review outcome is informational and never authorizes delivery, including when the selected repository is B.
|
|
90
|
+
|
|
91
|
+
Historical compatibility commands may read older artifacts manually, but they are never the ordinary lifecycle and never restore delivery authority.
|
|
92
|
+
|
|
93
|
+
### Concurrent Reviewer Group (MANDATORY)
|
|
94
|
+
|
|
95
|
+
When one fresh `collect.inputs` set contains multiple distinct independent `review.capture-result` reviewer slots, launch every returned capture operation concurrently in provider order: start all without waiting between launches, then wait for every result. For canonical 4R, preserve `review-risk`, `review-resilience`, `review-readability`, `review-reliability` order.
|
|
96
|
+
|
|
97
|
+
Each launch runs only its own provider-issued `review.capture-result` argument tokens exactly as returned. Completion order is not authority: shared Go admission/election owns reduction and semantics. The final admitted capture owns reduction and closure. On `approved`, authority is already burned: do not FINALIZE or issue a trailing STATUS. On `correction_required`, continue only through exact bound STATUS and the provider-issued `review.capture-correction-plan` binding. After a malformed or nonterminal capture, reconcile through exact bound STATUS and retry only an identically reoffered slot.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://gentle-ai.dev/schema/review/reviewer/v1",
|
|
4
|
+
"title": "Gentle AI reviewer result",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["subject_hash", "inspection", "findings", "evidence"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"subject_hash": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
|
|
10
|
+
"inspection": {"type": "object", "additionalProperties": false, "required": ["status", "paths"], "properties": {"status": {"const": "completed"}, "paths": {"type": "array", "description": "Complete unique unordered set of every changed_path_manifest.path.", "uniqueItems": true, "items": {"type": "string", "minLength": 1}}}},
|
|
11
|
+
"lens": {"type": "string", "description": "Optional selected lens binding. Omission canonicalizes to the selected subject lens.", "enum": ["risk", "resilience", "readability", "reliability", "review-risk", "review-resilience", "review-readability", "review-reliability"]},
|
|
12
|
+
"findings": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["location", "severity", "claim", "proof_refs"], "allOf": [{"if": {"properties": {"severity": {"enum": ["BLOCKER", "CRITICAL"]}}, "required": ["severity"]}, "then": {"required": ["evidence_class", "causal_disposition"]}}], "properties": {"id": {"type": "string", "pattern": "^R[1-4]-[A-Za-z0-9][A-Za-z0-9._-]*$", "description": "Optional explicit ID; omit it to receive a native-assigned ID. When present it must carry the prefix bound to the selected lens, not the selection order: review-risk=R1-, review-readability=R2-, review-reliability=R3-, review-resilience=R4-."}, "lens": {"type": "string", "enum": ["risk", "resilience", "readability", "reliability", "review-risk", "review-resilience", "review-readability", "review-reliability"]}, "location": {"type": "string", "description": "One canonical repository-relative path:line or inclusive path:start-end span.", "pattern": "^.+:[1-9][0-9]*(?:-[1-9][0-9]*)?$"}, "severity": {"type": "string", "enum": ["BLOCKER", "CRITICAL", "WARNING", "SUGGESTION"]}, "claim": {"type": "string", "minLength": 1}, "proof_refs": {"type": "array", "minItems": 1, "items": {"type": "string", "pattern": "\\S", "not": {"pattern": "^\\s*(?:[nN]/[aA]|[nN][aA]|[nN][oO][nN][eE]|[tT][oO][dD][oO]|[tT][bB][dD]|[pP][aA][sS][sS]|[pP][aA][sS][sS][eE][dD]|[sS][uU][cC][cC][eE][sS][sS]|[pP][lL][aA][cC][eE][hH][oO][lL][dD][eE][rR])\\s*$"}}}, "evidence_class": {"type": "string", "enum": ["deterministic", "inferential", "insufficient"]}, "causal_disposition": {"type": "string", "enum": ["introduced", "behavior-activated", "worsened", "pre-existing", "base-only", "unknown"]}}}},
|
|
13
|
+
"evidence": {"type": "array", "minItems": 1, "items": {"type": "string", "pattern": "\\S", "not": {"pattern": "^\\s*(?:[nN]/[aA]|[nN][aA]|[nN][oO][nN][eE]|[tT][oO][dD][oO]|[tT][bB][dD]|[pP][aA][sS][sS]|[pP][aA][sS][sS][eE][dD]|[sS][uU][cC][cC][eE][sS][sS]|[pP][lL][aA][cC][eE][hH][oO][lL][dD][eE][rR])\\s*$"}}}
|
|
14
|
+
},
|
|
15
|
+
"examples": [{"subject_hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "inspection": {"status": "completed", "paths": ["internal/example.go"]}, "findings": [], "evidence": ["reviewed the complete candidate scope"]}]
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://gentle-ai.dev/schema/review/refuter/v1","title":"Gentle AI refuter result","type":"object","additionalProperties":false,"required":["refuter_request_hash","results"],"properties":{"refuter_request_hash":{"$ref":"#/$defs/sha256"},"results":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["finding_id","outcome","proof_refs"],"properties":{"finding_id":{"type":"string"},"outcome":{"type":"string","enum":["corroborated","refuted","inconclusive"]},"proof_refs":{"type":"array","minItems":1,"items":{"type":"string","pattern":"\\S"}}}}}},"$defs":{"sha256":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"}},"examples":[{"refuter_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","results":[]}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"subject_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","inspection":{"status":"completed","paths":["internal/example.go"]},"findings":[],"evidence":["reviewed the complete candidate scope"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"refuter_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","results":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"targeted_validation_request_hash":"sha256:0000000000000000000000000000000000000000000000000000000000000000","correction_target_identity":"sha256:1111111111111111111111111111111111111111111111111111111111111111","original_criteria":{"passed":true,"evidence":["acceptance test passed"]},"correction_regression":{"passed":true,"evidence":["regression test passed"]},"follow_ups":[]}
|