gentle-pi 2.1.2 → 2.2.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 +70 -12
- package/assets/agents/gentle-ai-worker.md +7 -3
- package/assets/agents/jd-fix-agent.md +1 -1
- package/assets/agents/jd-judge-a.md +3 -1
- package/assets/agents/jd-judge-b.md +3 -1
- package/assets/agents/review-readability.md +4 -1
- package/assets/agents/review-reliability.md +4 -1
- package/assets/agents/review-resilience.md +4 -1
- package/assets/agents/review-risk.md +4 -1
- package/assets/agents/sdd-apply.md +6 -1
- package/assets/agents/sdd-archive.md +6 -1
- package/assets/agents/sdd-design.md +6 -1
- package/assets/agents/sdd-explore.md +6 -2
- package/assets/agents/sdd-init.md +10 -2
- package/assets/agents/sdd-onboard.md +6 -1
- package/assets/agents/sdd-proposal.md +6 -1
- package/assets/agents/sdd-spec.md +6 -1
- package/assets/agents/sdd-status.md +6 -1
- package/assets/agents/sdd-sync.md +6 -1
- package/assets/agents/sdd-tasks.md +6 -1
- package/assets/agents/sdd-verify.md +6 -1
- package/assets/chains/4r-review.chain.md +2 -0
- package/assets/chains/sdd-full.chain.md +1 -1
- package/assets/chains/sdd-plan.chain.md +1 -1
- package/assets/chains/sdd-verify.chain.md +1 -1
- package/assets/orchestrator-delegation.md +246 -67
- package/assets/orchestrator.md +7 -14
- package/assets/sdd-orchestrator-workflow.md +154 -9
- package/assets/support/sdd-status-contract.md +19 -1
- package/contracts/review-integration/v1/fixtures/consent.fixture.json +3 -3
- package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +19 -28
- package/contracts/review-integration/v1/fixtures/start.fixture.json +1 -10
- package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +12 -21
- package/contracts/review-integration/v1/schemas/correction-plan-request.schema.json +49 -0
- package/contracts/review-integration/v1/schemas/operation.schema.json +76 -0
- package/contracts/review-integration/v1/schemas/repair.schema.json +39 -0
- package/contracts/review-integration/v1/schemas/status-v2.schema.json +4 -2
- package/contracts/review-integration/v1/schemas/status.schema.json +4 -2
- package/contracts/review-integration/v2/fixtures/consent.fixture.json +1 -1
- package/contracts/review-integration/v2/fixtures/start.fixture.json +1 -10
- package/contracts/review-integration/v2/fixtures/status.fixture.json +1 -10
- package/contracts/review-integration/v2/schemas/failure.schema.json +5 -1
- package/contracts/review-integration/v2/schemas/operation.schema.json +6 -1
- package/contracts/review-integration/v2/schemas/repair.schema.json +4 -2
- package/contracts/review-integration/v2/schemas/start.schema.json +5 -2
- package/contracts/review-integration/v2/schemas/status.schema.json +4 -2
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +30 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/README.md +12 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/manifest.json +65 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/lens.schema.json +16 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/refuter.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/targeted-validator.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/lens.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/refuter.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/targeted-validator.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-capabilities.baseline.json +15 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-roles.baseline.json +42 -0
- package/docs/native-authority-architecture.md +5 -5
- package/docs/review-integration.md +22 -2
- package/extensions/gentle-ai.ts +1595 -201
- package/extensions/sdd-init.ts +19 -6
- package/extensions/skill-registry.ts +10 -2
- package/extensions/startup-banner.ts +10 -4
- package/lib/gentle-ai-binary.ts +173 -2
- package/lib/git-commit-transaction.ts +77 -17
- package/lib/native-review-cli.ts +528 -65
- package/lib/provider-contract-bundle.ts +704 -0
- package/lib/review-candidate-view.ts +527 -18
- package/lib/review-compact-contract.ts +59 -248
- package/lib/review-host-relay.ts +436 -0
- package/lib/review-integration-v2.ts +537 -36
- package/lib/review-relay-contract.ts +16 -0
- package/lib/sdd-preflight.ts +53 -1
- package/package.json +5 -2
- package/runtime/gentle-ai-binary.mjs +173 -2
- package/runtime/git-commit-transaction.mjs +75 -15
- package/runtime/native-review-cli.mjs +524 -61
- package/runtime/review-integration-v2.mjs +536 -35
- package/runtime/review-relay-contract.mjs +17 -0
- package/scripts/build-git-commit-transaction-runner.mjs +1 -0
- package/scripts/check-provider-contract.mjs +138 -0
- package/scripts/gentle-ai-installer.mjs +23 -13
- package/scripts/maintainer/provider-relay-matrix.mjs +219 -0
- package/scripts/mirror-provider-contract.mjs +143 -0
- package/scripts/test-packed-runner.mjs +16 -2
- package/scripts/verify-package-files.mjs +110 -33
- package/skills/_shared/review-ledger-contract.md +4 -6
- package/skills/gentle-ai/SKILL.md +4 -4
- package/skills/issue-creation/SKILL.md +94 -168
- package/skills/judgment-day/SKILL.md +7 -1
- package/skills/judgment-day/references/prompts-and-formats.md +2 -0
- package/skills/rdd-defect-workflow/SKILL.md +54 -0
- package/tests/background-subagents.test.ts +771 -0
- package/tests/crosslane/cross-lane.mjs +1169 -0
- package/tests/delegated-key-learnings-contract.test.ts +238 -0
- package/tests/fixtures/devbinary/capabilities-v2.1.derived.json +331 -0
- package/tests/fixtures/devbinary/capabilities-v2.2.captured.json +340 -0
- package/tests/fixtures/devbinary/consent-v3.captured.json +37 -0
- package/tests/fixtures/devbinary/failure-v2-capture-evidence.captured.json +16 -0
- package/tests/fixtures/devbinary/result-artifact-v2-path.captured.json +12 -0
- package/tests/fixtures/devbinary/result-artifact-v2.captured.json +12 -0
- package/tests/fixtures/devbinary/start-v3-consent-declined.captured.json +19 -0
- package/tests/fixtures/devbinary/start-v3-consent-granted.captured.json +109 -0
- package/tests/fixtures/devbinary/status-v5-capture-result-submission.captured.json +184 -0
- package/tests/fixtures/devbinary/status-v5-repository-context.captured.json +138 -0
- package/tests/fixtures/devbinary/status-v5.captured.json +88 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/README.md +12 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/manifest.json +65 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/targeted-validator.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/targeted-validator.json +1 -0
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +195 -0
- package/tests/gentle-ai-dev-binary.test.ts +336 -0
- package/tests/gentle-ai-installer.test.ts +46 -46
- package/tests/git-commit-transaction.test.ts +229 -1
- package/tests/maintainer/provider-relay.maintest.ts +265 -0
- package/tests/native-review-capability-contract.test.ts +48 -2
- package/tests/native-review-cli.test.ts +56 -0
- package/tests/native-review-consent.test.ts +164 -3
- package/tests/native-review-parity-runtime.test.ts +37 -0
- package/tests/native-review-parity.test.ts +218 -15
- package/tests/native-sdd-attempt-authority.test.ts +235 -0
- package/tests/orchestrator-budget.test.ts +30 -5
- package/tests/package-manifest.test.ts +98 -72
- package/tests/provider-contract-bundle.test.ts +385 -0
- package/tests/provider-contract-mirror.test.ts +206 -0
- package/tests/provider-defect-handoff.test.ts +355 -0
- package/tests/review-actor-tool-deny.test.ts +12 -13
- package/tests/review-candidate-view.test.ts +489 -9
- package/tests/review-compact-contract.test.ts +52 -119
- package/tests/review-controller-native-recovery.test.ts +643 -47
- package/tests/review-controller-native-routing.test.ts +1667 -222
- package/tests/review-controller-workspace-root.test.ts +17 -2
- package/tests/review-corrected-finalize-binding.test.ts +175 -0
- package/tests/review-dispatch-hydration-gap.test.ts +197 -0
- package/tests/review-host-relay-routing.test.ts +317 -0
- package/tests/review-host-relay.test.ts +520 -0
- package/tests/review-integration-v2-forward.test.ts +631 -0
- package/tests/review-integration-v2.test.ts +114 -0
- package/tests/review-ledger-contract.test.ts +12 -28
- package/tests/review-recovered-lineage-routing.test.ts +246 -0
- package/tests/review-relay-transport-agent.test.ts +249 -0
- package/tests/runtime-harness.mjs +242 -14
- package/tests/sdd-agent-tools.test.ts +18 -33
- package/tests/skill-collision-prefixes.test.ts +1 -0
- package/tests/skill-registry.test.ts +50 -1
- package/tests/verify-package-files.test.ts +62 -0
- package/assets/agents/review-refuter.md +0 -40
- package/assets/agents/review-validator.md +0 -23
- package/lib/native-review-remediation.ts +0 -49
- package/lib/review-compact.ts +0 -947
- package/lib/review-refuter-adapter.ts +0 -129
- package/lib/review-runtime-contract.ts +0 -68
- package/prompts/gcl.md +0 -54
- package/prompts/gis.md +0 -25
- package/prompts/gpr.md +0 -41
- package/prompts/gwr.md +0 -31
- package/tests/fixtures/native-review-cli/v2.1.2/bind-sdd.json +0 -25
- package/tests/fixtures/native-review-cli/v2.1.2/finalize.json +0 -8
- package/tests/fixtures/native-review-cli/v2.1.2/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.2/sdd-status.json +0 -200
- package/tests/fixtures/native-review-cli/v2.1.2/start.json +0 -12
- package/tests/fixtures/native-review-cli/v2.1.2/validate-allow.json +0 -24
- package/tests/fixtures/native-review-cli/v2.1.2/validate-deny-empty-context.json +0 -20
- package/tests/fixtures/native-review-cli/v2.1.2/validate-deny.json +0 -28
- package/tests/review-compact.test.ts +0 -243
- package/tests/review-refuter-adapter.test.ts +0 -89
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
9
|
+
const AGENTS = join(ROOT, "assets", "agents");
|
|
10
|
+
const ASSETS = join(ROOT, "assets");
|
|
11
|
+
|
|
12
|
+
// SDD phase executors installed to ~/.pi/agent/agents/ by installSddAssets.
|
|
13
|
+
// Each carries its own effective prompt; none inherits the parent workflow.
|
|
14
|
+
const SDD_PREFIX = "sdd-";
|
|
15
|
+
|
|
16
|
+
// Strict-output agents that must remain untouched by Key Learnings: the
|
|
17
|
+
// Judgment Day roles (`jd-*`) and the review lens roles (`review-*`). Derived
|
|
18
|
+
// from the agents actually on disk rather than hardcoded, so retiring one
|
|
19
|
+
// (as #320 did to `review-refuter.md` and `review-validator.md`) cannot break
|
|
20
|
+
// this guard with an ENOENT instead of a real assertion.
|
|
21
|
+
const STRICT_JSON_PREFIX = /^(jd-|review-)/;
|
|
22
|
+
|
|
23
|
+
// Canonical semantics every Key Learnings section must encode.
|
|
24
|
+
const KL_SEMANTICS: Array<[string, RegExp]> = [
|
|
25
|
+
["heading `## Key Learnings`", /`## Key Learnings`/],
|
|
26
|
+
["1–5 numbered items", /1[–-]5 numbered/],
|
|
27
|
+
["standalone factual sentence", /standalone factual sentence/],
|
|
28
|
+
["at least 20 characters", /at least 20 characters/],
|
|
29
|
+
["at least 4 words", /at least 4 words/],
|
|
30
|
+
["final report text only", /final (?:report|response) text only/],
|
|
31
|
+
["Engram extracts and persists", /Engram[^\n]*automatically extracts[^\n]*persists/i],
|
|
32
|
+
["executor does not parse", /do(?:es)? not parse/],
|
|
33
|
+
["passive-capture tool wording", /passive.capture/i],
|
|
34
|
+
["omit when no reusable learning", /[Oo]mit[^\n]*no reusable learning/],
|
|
35
|
+
["separate from mem_save", /separate from[^\n]*mem_save/],
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function readSection(source: string, heading: string): string | null {
|
|
39
|
+
const lines = source.split(/\r?\n/);
|
|
40
|
+
const start = lines.findIndex((l) => /^#{1,6}\s+/.test(l) && l.replace(/^#{1,6}\s+/, "").trim() === heading);
|
|
41
|
+
if (start === -1) return null;
|
|
42
|
+
const level = lines[start].match(/^(#{1,6})/)![1].length;
|
|
43
|
+
let end = lines.length;
|
|
44
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
45
|
+
const m = lines[i].match(/^(#{1,6})\s+/);
|
|
46
|
+
if (m && m[1].length <= level) { end = i; break; }
|
|
47
|
+
}
|
|
48
|
+
return lines.slice(start, end).join("\n").trim();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function agentFiles(): string[] {
|
|
52
|
+
return readdirSync(AGENTS).filter((f) => f.endsWith(".md")).sort();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sddAgents(): string[] {
|
|
56
|
+
return agentFiles().filter((f) => f.startsWith(SDD_PREFIX));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function strictJsonAgents(): string[] {
|
|
60
|
+
return agentFiles().filter((f) => STRICT_JSON_PREFIX.test(f));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
test("every SDD phase executor carries an effective `## Key Learnings Closing` section with full semantics", () => {
|
|
64
|
+
const agents = sddAgents();
|
|
65
|
+
assert.ok(agents.length >= 12, `expected >=12 SDD agents, found ${agents.length}`);
|
|
66
|
+
const missing: string[] = [];
|
|
67
|
+
const failed: string[] = [];
|
|
68
|
+
for (const file of agents) {
|
|
69
|
+
const source = readFileSync(join(AGENTS, file), "utf8");
|
|
70
|
+
const section = readSection(source, "Key Learnings Closing");
|
|
71
|
+
if (section === null) { missing.push(file); continue; }
|
|
72
|
+
for (const [label, regex] of KL_SEMANTICS) {
|
|
73
|
+
if (!regex.test(section)) failed.push(`${file}: ${label}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
assert.deepEqual(missing, [], "every SDD agent must carry a `## Key Learnings Closing` section");
|
|
77
|
+
assert.deepEqual(failed, [], "every section must encode all canonical semantics");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("no SDD phase executor infers Key Learnings through `standard phase envelope` alone", () => {
|
|
81
|
+
for (const file of sddAgents()) {
|
|
82
|
+
const source = readFileSync(join(AGENTS, file), "utf8");
|
|
83
|
+
const section = readSection(source, "Key Learnings Closing");
|
|
84
|
+
assert.ok(section, `${file} must have a direct Key Learnings Closing section`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("SDD executor coverage is exhaustive against actual agent files", () => {
|
|
89
|
+
const actual = sddAgents();
|
|
90
|
+
// Allowlist: the 12 known phase executors. A new sdd-*.md without a
|
|
91
|
+
// Key Learnings Closing section fails the first test; this test proves
|
|
92
|
+
// the allowlist matches reality so coverage cannot silently drift.
|
|
93
|
+
const expected = [
|
|
94
|
+
"sdd-apply.md", "sdd-archive.md", "sdd-design.md", "sdd-explore.md",
|
|
95
|
+
"sdd-init.md", "sdd-onboard.md", "sdd-proposal.md", "sdd-spec.md",
|
|
96
|
+
"sdd-status.md", "sdd-sync.md", "sdd-tasks.md", "sdd-verify.md",
|
|
97
|
+
];
|
|
98
|
+
assert.deepEqual(actual, expected, "SDD agent set must match the known allowlist");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("generic delegation contract instructs the same `## Key Learnings` closing block", () => {
|
|
102
|
+
const delegation = readFileSync(join(ASSETS, "orchestrator-delegation.md"), "utf8");
|
|
103
|
+
const section = readSection(delegation, "Key Learnings closing block");
|
|
104
|
+
assert.ok(section, "orchestrator-delegation.md must have a Key Learnings closing block section");
|
|
105
|
+
for (const [label, regex] of KL_SEMANTICS) {
|
|
106
|
+
assert.match(section, regex, `delegation missing semantic: ${label}`);
|
|
107
|
+
}
|
|
108
|
+
assert.match(section, /native `Agent`/, "must cover native Agent fallback");
|
|
109
|
+
assert.match(section, /strict JSON/i, "must exclude strict-JSON agents");
|
|
110
|
+
assert.match(section, /layers on after/, "must state the block layers on after the envelope");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("sdd-orchestrator-workflow documents routing, not executor authority", () => {
|
|
114
|
+
const workflow = readFileSync(join(ASSETS, "sdd-orchestrator-workflow.md"), "utf8");
|
|
115
|
+
const section = readSection(workflow, "Key Learnings closing block (routing)");
|
|
116
|
+
assert.ok(section, "workflow must document Key Learnings routing");
|
|
117
|
+
assert.match(section, /installed SDD phase executor agent.*carries the effective.*contract/i);
|
|
118
|
+
assert.match(section, /documents routing only and is not the executor authority/);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("provider ownership: no Pi TypeScript runtime parses Key Learnings or invokes passive-capture tools", () => {
|
|
122
|
+
const roots = ["lib", "extensions", "scripts", "runtime"];
|
|
123
|
+
const forbidden: Array<[string, RegExp]> = [
|
|
124
|
+
["Key Learnings parser", /Key Learnings/i],
|
|
125
|
+
["key_learnings token", /key_learnings/i],
|
|
126
|
+
["passive-capture tool invocation", /mem_capture_passive|capture_passive|passive_capture/i],
|
|
127
|
+
];
|
|
128
|
+
const failures: string[] = [];
|
|
129
|
+
for (const root of roots) {
|
|
130
|
+
const absRoot = join(ROOT, root);
|
|
131
|
+
if (!existsSync(absRoot)) continue;
|
|
132
|
+
for (const file of listCodeFiles(absRoot)) {
|
|
133
|
+
const text = readFileSync(file, "utf8");
|
|
134
|
+
for (const [label, regex] of forbidden) {
|
|
135
|
+
if (regex.test(text)) failures.push(`${relative(ROOT, file)}: ${label}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
assert.deepEqual(failures, [], "Pi must not parse Key Learnings or invoke passive-capture tools");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Recursive walker over a provider root. Replaces the previous direct-children
|
|
143
|
+
// scan so nested `.ts`/`.mjs` runtime code cannot evade the ownership guard.
|
|
144
|
+
// Directories are recursed; only regular `.ts`/`.mjs` files are yielded;
|
|
145
|
+
// symlinks (file or directory) are never followed, preventing loops and
|
|
146
|
+
// external traversal. Output is sorted for deterministic failure messages.
|
|
147
|
+
// Fail-closed: traversal/read errors propagate so a forbidden file under an
|
|
148
|
+
// unreadable directory cannot silently evade the guard.
|
|
149
|
+
function listCodeFiles(rootDir: string): string[] {
|
|
150
|
+
const out: string[] = [];
|
|
151
|
+
const stack: string[] = [rootDir];
|
|
152
|
+
while (stack.length > 0) {
|
|
153
|
+
const dir = stack.pop()!;
|
|
154
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
155
|
+
if (e.isSymbolicLink()) continue;
|
|
156
|
+
const full = join(dir, e.name);
|
|
157
|
+
if (e.isDirectory()) stack.push(full);
|
|
158
|
+
else if (e.isFile() && /\.(ts|mjs)$/.test(e.name)) out.push(full);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out.sort();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
test("listCodeFiles recurses nested code, ignores non-code files, and skips symlinks", () => {
|
|
165
|
+
const tmp = mkdtempSync(join(tmpdir(), "kl-walker-"));
|
|
166
|
+
try {
|
|
167
|
+
const nested = join(tmp, "a", "b");
|
|
168
|
+
mkdirSync(nested, { recursive: true });
|
|
169
|
+
writeFileSync(join(nested, "leak.ts"), "Key Learnings leak\n");
|
|
170
|
+
writeFileSync(join(tmp, "a", "leak.mjs"), "key_learnings\n");
|
|
171
|
+
writeFileSync(join(tmp, "a", "notes.md"), "Key Learnings in prose\n");
|
|
172
|
+
writeFileSync(join(tmp, "a", "data.json"), '{"k":"Key Learnings"}\n');
|
|
173
|
+
const files = listCodeFiles(tmp);
|
|
174
|
+
assert.ok(files.some((f) => f.endsWith("leak.ts")), "nested .ts must be discovered");
|
|
175
|
+
assert.ok(files.some((f) => f.endsWith("leak.mjs")), "shallow .mjs must be discovered");
|
|
176
|
+
assert.ok(!files.some((f) => f.endsWith(".md")), "non-code .md must be ignored");
|
|
177
|
+
assert.ok(!files.some((f) => f.endsWith(".json")), "non-code .json must be ignored");
|
|
178
|
+
// Symlink controls: skip when the platform rejects symlink creation.
|
|
179
|
+
const dirLink = join(tmp, "link-dir");
|
|
180
|
+
const fileLink = join(tmp, "link-file.ts");
|
|
181
|
+
let symlinksSupported = true;
|
|
182
|
+
try {
|
|
183
|
+
symlinkSync(join(tmp, "a"), dirLink, "dir");
|
|
184
|
+
symlinkSync(join(tmp, "a", "leak.mjs"), fileLink, "file");
|
|
185
|
+
} catch {
|
|
186
|
+
symlinksSupported = false;
|
|
187
|
+
}
|
|
188
|
+
if (symlinksSupported) {
|
|
189
|
+
const walked = listCodeFiles(tmp);
|
|
190
|
+
assert.ok(!walked.includes(fileLink), "symlinked .ts file must not be followed");
|
|
191
|
+
assert.ok(!walked.some((f) => f === dirLink || f.startsWith(dirLink + sep)), "symlinked directory must not be traversed");
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("strict review and Judgment Day agents do not gain Key Learnings or trailing-prose instruction", () => {
|
|
199
|
+
const strict = strictJsonAgents();
|
|
200
|
+
// A derived list can go empty and pass vacuously, which would assert
|
|
201
|
+
// nothing. Require both strict-output role families to still be covered
|
|
202
|
+
// without pinning an exact count that the next retirement would re-break.
|
|
203
|
+
assert.ok(strict.some((f) => f.startsWith("jd-")), "Judgment Day agents must be covered by this guard");
|
|
204
|
+
assert.ok(strict.some((f) => f.startsWith("review-")), "review lens agents must be covered by this guard");
|
|
205
|
+
const failures: string[] = [];
|
|
206
|
+
for (const file of strict) {
|
|
207
|
+
const source = readFileSync(join(AGENTS, file), "utf8");
|
|
208
|
+
for (const regex of [/Key Learnings/i, /key_learnings/i, /trailing prose/i]) {
|
|
209
|
+
if (regex.test(source)) failures.push(`${file}: ${regex.source}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
assert.deepEqual(failures, [], "strict-JSON/ledger agents must remain untouched");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("the canonical Key Learnings heading has no trailing colon in any asset", () => {
|
|
216
|
+
for (const file of sddAgents()) {
|
|
217
|
+
const source = readFileSync(join(AGENTS, file), "utf8");
|
|
218
|
+
assert.match(source, /`## Key Learnings`/, `${file} must reference the canonical heading`);
|
|
219
|
+
assert.doesNotMatch(source, /`## Key Learnings:`/, `${file} must not use a trailing colon`);
|
|
220
|
+
}
|
|
221
|
+
const delegation = readFileSync(join(ASSETS, "orchestrator-delegation.md"), "utf8");
|
|
222
|
+
assert.doesNotMatch(delegation, /`## Key Learnings:`/);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("modified SDD agents are packaged and installed by the existing installer", () => {
|
|
226
|
+
const verifier = readFileSync(join(ROOT, "scripts", "verify-package-files.mjs"), "utf8");
|
|
227
|
+
const expected = [
|
|
228
|
+
"assets/agents/sdd-apply.md", "assets/agents/sdd-archive.md",
|
|
229
|
+
"assets/agents/sdd-design.md", "assets/agents/sdd-explore.md",
|
|
230
|
+
"assets/agents/sdd-init.md", "assets/agents/sdd-onboard.md",
|
|
231
|
+
"assets/agents/sdd-proposal.md", "assets/agents/sdd-spec.md",
|
|
232
|
+
"assets/agents/sdd-status.md", "assets/agents/sdd-sync.md",
|
|
233
|
+
"assets/agents/sdd-tasks.md", "assets/agents/sdd-verify.md",
|
|
234
|
+
];
|
|
235
|
+
for (const path of expected) {
|
|
236
|
+
assert.match(verifier, new RegExp(path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")), `${path} must be in the package verifier`);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "gentle-ai.review-integration.capabilities/v2.1",
|
|
3
|
+
"contract": "gentle-ai.review-integration/v2",
|
|
4
|
+
"protocol": {
|
|
5
|
+
"major": 2,
|
|
6
|
+
"minor": 1
|
|
7
|
+
},
|
|
8
|
+
"package": {
|
|
9
|
+
"name": "gentle-ai",
|
|
10
|
+
"version": "2.4.0-rc.8+fix.verify-attestation-recovery",
|
|
11
|
+
"release_channel": "prerelease"
|
|
12
|
+
},
|
|
13
|
+
"build": {
|
|
14
|
+
"id": "sha256:4db874cd6a31fc1bc0deb2b94c22ad4668e878ffc5c0bc9acc2c81bcbe03e054",
|
|
15
|
+
"go_version": "go1.26.5",
|
|
16
|
+
"module_version": "(devel)",
|
|
17
|
+
"vcs": "git",
|
|
18
|
+
"vcs_revision": "",
|
|
19
|
+
"vcs_time": "",
|
|
20
|
+
"vcs_modified": "true"
|
|
21
|
+
},
|
|
22
|
+
"executable": {
|
|
23
|
+
"sha256": "sha256:ffc91d8fa79c869aba9aa3d1ec80edebb5b1744e5a06fef75d4c8b73c0e46bc1",
|
|
24
|
+
"evidence": "self-reported",
|
|
25
|
+
"verification": "compare-with-published-manifest"
|
|
26
|
+
},
|
|
27
|
+
"operations": [
|
|
28
|
+
"review.bind_sdd",
|
|
29
|
+
"review.capabilities",
|
|
30
|
+
"review.finalize",
|
|
31
|
+
"review.repair",
|
|
32
|
+
"review.retry_final_verification",
|
|
33
|
+
"review.start",
|
|
34
|
+
"review.status",
|
|
35
|
+
"review.validate"
|
|
36
|
+
],
|
|
37
|
+
"gates": [
|
|
38
|
+
"post-apply",
|
|
39
|
+
"pre-commit",
|
|
40
|
+
"pre-push",
|
|
41
|
+
"pre-pr",
|
|
42
|
+
"release"
|
|
43
|
+
],
|
|
44
|
+
"projections": [
|
|
45
|
+
"staged",
|
|
46
|
+
"workspace"
|
|
47
|
+
],
|
|
48
|
+
"schemas": [
|
|
49
|
+
"gentle-ai.review-admitted-result/v2",
|
|
50
|
+
"gentle-ai.review-artifact-subject/v2",
|
|
51
|
+
"gentle-ai.review-authority-repair-assessment/v1",
|
|
52
|
+
"gentle-ai.review-authority-status/v1",
|
|
53
|
+
"gentle-ai.review-gate-request/v1",
|
|
54
|
+
"gentle-ai.review-integration.capabilities/v2.1",
|
|
55
|
+
"gentle-ai.review-integration.failure/v2",
|
|
56
|
+
"gentle-ai.review-final-verification-incident/v1",
|
|
57
|
+
"gentle-ai.review-integration.operation/v2",
|
|
58
|
+
"gentle-ai.review-integration.projection/v1",
|
|
59
|
+
"gentle-ai.review-integration.repair/v2",
|
|
60
|
+
"gentle-ai.review-integration.start/v3",
|
|
61
|
+
"gentle-ai.review-integration.status/v3",
|
|
62
|
+
"gentle-ai.review-receipt/v1",
|
|
63
|
+
"gentle-ai.review-receipt/v2",
|
|
64
|
+
"gentle-ai.review-result-artifact/v2",
|
|
65
|
+
"gentle-ai.review-targeted-validation-request/v1",
|
|
66
|
+
"gentle-ai.review-verification-evidence/v2",
|
|
67
|
+
"https://gentle-ai.dev/schema/review/refuter/v1",
|
|
68
|
+
"https://gentle-ai.dev/schema/review/reviewer/v1",
|
|
69
|
+
"https://gentle-ai.dev/schema/review/validator/v1",
|
|
70
|
+
"gentle-ai.review-integration.consent/v3"
|
|
71
|
+
],
|
|
72
|
+
"features": {
|
|
73
|
+
"mandatory": [
|
|
74
|
+
{
|
|
75
|
+
"name": "compact_v2_authority",
|
|
76
|
+
"supported": true,
|
|
77
|
+
"requires": []
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "exact_receipt_replay",
|
|
81
|
+
"supported": true,
|
|
82
|
+
"requires": [
|
|
83
|
+
"compact_v2_authority"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "five_delivery_gates",
|
|
88
|
+
"supported": true,
|
|
89
|
+
"requires": [
|
|
90
|
+
"compact_v2_authority"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "immutable_snapshot",
|
|
95
|
+
"supported": true,
|
|
96
|
+
"requires": []
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "legacy_v1_target_scoped_read_only",
|
|
100
|
+
"supported": true,
|
|
101
|
+
"requires": [
|
|
102
|
+
"target_scoped_status"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "repository_independent_capabilities",
|
|
107
|
+
"supported": true,
|
|
108
|
+
"requires": []
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "restart_safe_projection",
|
|
112
|
+
"supported": true,
|
|
113
|
+
"requires": [
|
|
114
|
+
"target_scoped_status"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "sdd_receipt_binding",
|
|
119
|
+
"supported": true,
|
|
120
|
+
"requires": [
|
|
121
|
+
"compact_v2_authority"
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "target_scoped_status",
|
|
126
|
+
"supported": true,
|
|
127
|
+
"requires": [
|
|
128
|
+
"repository_independent_capabilities"
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"name": "uniform_failure_envelope",
|
|
133
|
+
"supported": true,
|
|
134
|
+
"requires": [
|
|
135
|
+
"repository_independent_capabilities"
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"optional": [
|
|
140
|
+
{
|
|
141
|
+
"name": "base_ref_workspace_overlay",
|
|
142
|
+
"supported": true,
|
|
143
|
+
"requires": [
|
|
144
|
+
"immutable_snapshot",
|
|
145
|
+
"restart_safe_projection"
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "bounded_process_waits",
|
|
150
|
+
"supported": true,
|
|
151
|
+
"requires": [
|
|
152
|
+
"uniform_failure_envelope"
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "classified_authority_repair",
|
|
157
|
+
"supported": true,
|
|
158
|
+
"requires": [
|
|
159
|
+
"native_next_transition",
|
|
160
|
+
"uniform_failure_envelope"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "exact_gate_receipt_discovery",
|
|
165
|
+
"supported": true,
|
|
166
|
+
"requires": [
|
|
167
|
+
"five_delivery_gates"
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "native_frozen_candidate_context",
|
|
172
|
+
"supported": true,
|
|
173
|
+
"requires": [
|
|
174
|
+
"immutable_snapshot"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "native_low_risk_verification",
|
|
179
|
+
"supported": true,
|
|
180
|
+
"requires": [
|
|
181
|
+
"compact_v2_authority"
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "native_next_transition",
|
|
186
|
+
"supported": true,
|
|
187
|
+
"requires": [
|
|
188
|
+
"target_scoped_status"
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "one_shot_final_verification_retry",
|
|
193
|
+
"supported": true,
|
|
194
|
+
"requires": [
|
|
195
|
+
"compact_v2_authority",
|
|
196
|
+
"exact_receipt_replay",
|
|
197
|
+
"native_next_transition"
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "opaque_repository_context",
|
|
202
|
+
"supported": true,
|
|
203
|
+
"requires": [
|
|
204
|
+
"compact_v2_authority",
|
|
205
|
+
"native_next_transition"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "outcome_bound_verification_evidence",
|
|
210
|
+
"supported": true,
|
|
211
|
+
"requires": [
|
|
212
|
+
"compact_v2_authority",
|
|
213
|
+
"native_next_transition"
|
|
214
|
+
]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "provider_artifact_admission",
|
|
218
|
+
"supported": true,
|
|
219
|
+
"requires": [
|
|
220
|
+
"compact_v2_authority",
|
|
221
|
+
"native_frozen_candidate_context",
|
|
222
|
+
"opaque_repository_context"
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"name": "provider_targeted_validation_request",
|
|
227
|
+
"supported": true,
|
|
228
|
+
"requires": [
|
|
229
|
+
"compact_v2_authority",
|
|
230
|
+
"native_next_transition"
|
|
231
|
+
]
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "recovered_correction_evidence",
|
|
235
|
+
"supported": true,
|
|
236
|
+
"requires": [
|
|
237
|
+
"compact_v2_authority",
|
|
238
|
+
"provider_targeted_validation_request"
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"name": "risk_reasons",
|
|
243
|
+
"supported": true,
|
|
244
|
+
"requires": [
|
|
245
|
+
"repository_independent_capabilities"
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "scope_change_diagnostics",
|
|
250
|
+
"supported": true,
|
|
251
|
+
"requires": [
|
|
252
|
+
"uniform_failure_envelope"
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "validating_result_reopen",
|
|
257
|
+
"supported": true,
|
|
258
|
+
"requires": [
|
|
259
|
+
"compact_v2_authority",
|
|
260
|
+
"provider_artifact_admission"
|
|
261
|
+
]
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"name": "provider_bound_native_git_context",
|
|
265
|
+
"supported": true,
|
|
266
|
+
"requires": [
|
|
267
|
+
"native_frozen_candidate_context",
|
|
268
|
+
"opaque_repository_context",
|
|
269
|
+
"provider_artifact_admission"
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
"bootstrap": {
|
|
275
|
+
"command": "gentle-ai review status --cwd <repo> --contract gentle-ai.review-integration/v2 --next-transition",
|
|
276
|
+
"target_selector_variants": [
|
|
277
|
+
{
|
|
278
|
+
"target_type": "staged",
|
|
279
|
+
"arguments": [
|
|
280
|
+
"--projection",
|
|
281
|
+
"staged"
|
|
282
|
+
]
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"target_type": "base_ref",
|
|
286
|
+
"arguments": [
|
|
287
|
+
"--base-ref",
|
|
288
|
+
"<ref>"
|
|
289
|
+
]
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"target_type": "workspace_overlay_base_ref",
|
|
293
|
+
"arguments": [
|
|
294
|
+
"--workspace-overlay",
|
|
295
|
+
"--base-ref",
|
|
296
|
+
"<ref>"
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"target_type": "workspace_overlay_base_tree",
|
|
301
|
+
"arguments": [
|
|
302
|
+
"--workspace-overlay",
|
|
303
|
+
"--base-tree",
|
|
304
|
+
"<tree>"
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
],
|
|
308
|
+
"required_feature": "native_next_transition",
|
|
309
|
+
"unsupported_outcome": "unsupported-capability",
|
|
310
|
+
"parent_only": true
|
|
311
|
+
},
|
|
312
|
+
"compatibility": {
|
|
313
|
+
"minimum_protocol_major": 2,
|
|
314
|
+
"maximum_protocol_major": 2,
|
|
315
|
+
"additive_minor_policy": "optional-fields-only",
|
|
316
|
+
"unknown_mandatory": "reject",
|
|
317
|
+
"unknown_optional": "ignore",
|
|
318
|
+
"modes": [
|
|
319
|
+
"compact-v2",
|
|
320
|
+
"legacy-v1"
|
|
321
|
+
],
|
|
322
|
+
"legacy_window": {
|
|
323
|
+
"mode": "legacy-v1",
|
|
324
|
+
"state": "active",
|
|
325
|
+
"read_only": true,
|
|
326
|
+
"deprecation_started": true,
|
|
327
|
+
"removal": "not-scheduled",
|
|
328
|
+
"minimum_compatibility_releases": 1
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|