gentle-pi 2.2.0 → 2.4.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.
Files changed (160) hide show
  1. package/README.md +76 -79
  2. package/assets/agents/review-readability.md +0 -2
  3. package/assets/agents/review-reliability.md +0 -2
  4. package/assets/agents/review-resilience.md +0 -2
  5. package/assets/agents/review-risk.md +0 -2
  6. package/assets/agents/sdd-apply.md +5 -3
  7. package/assets/agents/sdd-explore.md +1 -0
  8. package/assets/agents/sdd-proposal.md +2 -0
  9. package/assets/agents/sdd-research.md +54 -0
  10. package/assets/agents/sdd-status.md +4 -4
  11. package/assets/agents/sdd-tasks.md +2 -3
  12. package/assets/agents/sdd-verify.md +21 -1
  13. package/assets/chains/sdd-full.chain.md +1 -1
  14. package/assets/chains/sdd-verify.chain.md +1 -1
  15. package/assets/orchestrator-delegation.md +56 -249
  16. package/assets/orchestrator-memory.md +2 -0
  17. package/assets/orchestrator.md +20 -40
  18. package/assets/sdd-orchestrator-workflow.md +30 -37
  19. package/assets/support/sdd-status-contract.md +7 -7
  20. package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
  21. package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
  22. package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
  23. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
  24. package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/README.md +10 -0
  25. package/contracts/review-provider-contract-mirror/v1.2.0/bundle/manifest.json +74 -0
  26. package/contracts/review-provider-contract-mirror/v1.2.0/bundle/orchestration/pi.md +53 -0
  27. package/contracts/review-provider-contract-mirror/v1.2.0/bundle/schemas/targeted-validator.schema.json +1 -0
  28. package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/generated/provider-capabilities.baseline.json +9 -2
  29. package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/generated/provider-roles.baseline.json +2 -2
  30. package/docs/native-authority-architecture.md +9 -11
  31. package/docs/review-integration.md +27 -373
  32. package/extensions/ask-user-choice.ts +151 -0
  33. package/extensions/gentle-ai.ts +2349 -3785
  34. package/extensions/quiet-tools.ts +515 -32
  35. package/extensions/sdd-init.ts +4 -8
  36. package/lib/agent-home.ts +8 -0
  37. package/lib/gentle-ai-renderer.ts +70 -0
  38. package/lib/model-routing-authority.ts +133 -0
  39. package/lib/native-review-cli.ts +387 -940
  40. package/lib/opaque-pi-reviewer-adapter.ts +284 -0
  41. package/lib/provider-contract-bundle.ts +88 -6
  42. package/lib/review-candidate-view.ts +341 -132
  43. package/lib/review-host-relay.ts +324 -96
  44. package/lib/review-integration-v2.ts +868 -307
  45. package/lib/review-last-event-controller.ts +35 -0
  46. package/lib/sdd-preflight.ts +179 -68
  47. package/lib/sdd-status.ts +66 -111
  48. package/lib/terminal-theme.ts +1 -1
  49. package/package.json +83 -82
  50. package/runtime/gentle-ai-binary.mjs +1 -1
  51. package/runtime/native-review-cli.mjs +337 -890
  52. package/runtime/review-integration-v2.mjs +816 -255
  53. package/runtime/review-relay-contract.mjs +1 -1
  54. package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +4 -5
  55. package/scripts/gentle-ai-installer.mjs +75 -22
  56. package/scripts/maintainer/provider-relay-matrix.mjs +195 -11
  57. package/scripts/test-packed-runner.mjs +4 -7
  58. package/scripts/verify-package-files.mjs +23 -23
  59. package/skills/_shared/review-ledger-contract.md +14 -14
  60. package/skills/chained-pr/SKILL.md +3 -0
  61. package/skills/cognitive-doc-design/SKILL.md +1 -1
  62. package/skills/comment-writer/SKILL.md +1 -1
  63. package/skills/gentle-ai/SKILL.md +7 -74
  64. package/skills/issue-creation/SKILL.md +53 -93
  65. package/skills/judgment-day/SKILL.md +5 -7
  66. package/skills/rdd-defect-workflow/SKILL.md +3 -3
  67. package/skills/release/SKILL.md +3 -3
  68. package/skills/skill-registry/SKILL.md +1 -1
  69. package/skills/work-unit-commits/SKILL.md +3 -1
  70. package/tests/artifact-language.test.ts +24 -11
  71. package/tests/ask-user-choice.test.ts +264 -0
  72. package/tests/codegraph-tools.test.ts +3 -3
  73. package/tests/crosslane/cross-lane.mjs +15 -1168
  74. package/tests/delegated-key-learnings-contract.test.ts +8 -6
  75. package/tests/devbinary/native-review-parity.devtest.ts +167 -250
  76. package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
  77. package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
  78. package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
  79. package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
  80. package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
  81. package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
  82. package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
  83. package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
  84. package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
  85. package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
  86. package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
  87. package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
  88. package/tests/fixtures/provider-contract-bundle/v1.2.0/README.md +22 -0
  89. package/{contracts/review-provider-contract-mirror/v1.1.0/bundle → tests/fixtures/provider-contract-bundle/v1.2.0}/manifest.json +11 -2
  90. package/tests/fixtures/provider-contract-bundle/v1.2.0/orchestration/pi.md +97 -0
  91. package/tests/fixtures/provider-contract-bundle/v1.2.0/schemas/lens.schema.json +16 -0
  92. package/tests/fixtures/provider-contract-bundle/v1.2.0/schemas/refuter.schema.json +1 -0
  93. package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/lens.json +1 -0
  94. package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/refuter.json +1 -0
  95. package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/targeted-validator.json +1 -0
  96. package/tests/gentle-ai-binary.test.ts +83 -13
  97. package/tests/gentle-ai-dev-binary.test.ts +51 -228
  98. package/tests/gentle-ai-installer.test.ts +137 -47
  99. package/tests/gentle-ai.test.ts +821 -131
  100. package/tests/gentle-theme.test.ts +133 -0
  101. package/tests/issue-creation-skill.test.ts +103 -0
  102. package/tests/maintainer/provider-relay.maintest.ts +340 -4
  103. package/tests/model-routing-authority.test.ts +257 -0
  104. package/tests/native-review-capability-contract.test.ts +45 -1
  105. package/tests/native-review-cli.test.ts +586 -962
  106. package/tests/native-review-consent.test.ts +150 -66
  107. package/tests/native-review-parity-runtime.test.ts +80 -359
  108. package/tests/native-review-parity.test.ts +674 -925
  109. package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
  110. package/tests/orchestrator-budget.test.ts +132 -88
  111. package/tests/orchestrator-rdd-ownership.test.ts +103 -0
  112. package/tests/package-manifest.test.ts +152 -36
  113. package/tests/provider-contract-bundle.test.ts +76 -0
  114. package/tests/provider-contract-mirror.test.ts +19 -0
  115. package/tests/provider-defect-handoff.test.ts +15 -118
  116. package/tests/quiet-tool-rendering.test.ts +1055 -28
  117. package/tests/review-agent-end-preflight.test.ts +408 -0
  118. package/tests/review-authority-recovery-docs.test.ts +1 -2
  119. package/tests/review-candidate-view.test.ts +409 -3
  120. package/tests/review-compact-contract.test.ts +4 -30
  121. package/tests/review-contract-prompt.test.ts +139 -0
  122. package/tests/review-controller-lock-status.test.ts +2 -2
  123. package/tests/review-controller-native-recovery.test.ts +363 -1450
  124. package/tests/review-controller-native-routing.test.ts +1251 -5642
  125. package/tests/review-controller-retired-ops.test.ts +1 -1
  126. package/tests/review-controller-workspace-root.test.ts +222 -71
  127. package/tests/review-controller.test.ts +26 -816
  128. package/tests/review-corrected-finalize-binding.test.ts +112 -153
  129. package/tests/review-dispatch-hydration-gap.test.ts +1 -53
  130. package/tests/review-gate.test.ts +0 -45
  131. package/tests/review-host-relay-restart-parity.test.ts +360 -0
  132. package/tests/review-host-relay-routing.test.ts +328 -91
  133. package/tests/review-host-relay.test.ts +436 -14
  134. package/tests/review-integration-v2-forward.test.ts +623 -553
  135. package/tests/review-integration-v2.test.ts +239 -165
  136. package/tests/review-last-event-closure.test.ts +408 -0
  137. package/tests/review-ledger-contract.test.ts +97 -35
  138. package/tests/review-recovered-lineage-routing.test.ts +0 -47
  139. package/tests/review-relay-transport-agent.test.ts +119 -46
  140. package/tests/review-snapshot.test.ts +3 -2
  141. package/tests/runtime-harness.mjs +362 -196
  142. package/tests/sdd-agent-tools.test.ts +36 -0
  143. package/tests/sdd-preflight.test.ts +81 -15
  144. package/tests/sdd-status.test.ts +109 -110
  145. package/tests/skill-collision-prefixes.test.ts +5 -8
  146. package/tests/writer-edit-surface-scope.test.ts +366 -0
  147. package/themes/Gentleman-Cute.json +94 -0
  148. package/themes/Gentleman-Sexy.json +92 -0
  149. package/lib/git-commit-transaction.ts +0 -861
  150. package/runtime/git-commit-transaction.mjs +0 -862
  151. package/scripts/run-git-commit-transaction.mjs +0 -35
  152. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
  153. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
  154. package/tests/git-commit-transaction.test.ts +0 -530
  155. /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/lens.schema.json +0 -0
  156. /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/refuter.schema.json +0 -0
  157. /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/lens.json +0 -0
  158. /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/refuter.json +0 -0
  159. /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/targeted-validator.json +0 -0
  160. /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
@@ -0,0 +1,139 @@
1
+ import assert from "node:assert/strict";
2
+ import { createHash } from "node:crypto";
3
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import test from "node:test";
7
+ import { pathToFileURL } from "node:url";
8
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
+ import { createGentleAiExtension } from "../extensions/gentle-ai.ts";
10
+ import type { NativeReviewCli } from "../lib/native-review-cli.ts";
11
+
12
+ // gentle-pi#560 / gentle-ai#4056, #4057: since 2026-08-01 Gentle AI stopped
13
+ // writing a runtime-specific review execution contract into Pi's generated
14
+ // APPEND_SYSTEM composition. This package now injects the mirrored provider
15
+ // contract bundle's own `orchestration/pi.md` text at session start instead.
16
+ // These tests exercise the real committed mirror (contracts/review-provider-contract-mirror/)
17
+ // rather than a fake one: the mirror IS the package under test.
18
+
19
+ type BeforeAgentStartResult = { systemPrompt: string };
20
+ type BeforeAgentStartHandler = (event: unknown, ctx: ExtensionContext) => Promise<BeforeAgentStartResult>;
21
+
22
+ const REPO_ROOT = join(import.meta.dirname, "..");
23
+ const MIRROR_LOCK_PATH = join(REPO_ROOT, "contracts", "review-provider-contract-mirror", "provider-contract.lock.json");
24
+
25
+ function mirroredPiOrchestrationText(): string {
26
+ const lock = JSON.parse(readFileSync(MIRROR_LOCK_PATH, "utf8")) as { contract_semver: string };
27
+ return readFileSync(
28
+ join(REPO_ROOT, "contracts", "review-provider-contract-mirror", `v${lock.contract_semver}`, "bundle", "orchestration", "pi.md"),
29
+ "utf8",
30
+ ).trim();
31
+ }
32
+
33
+ function harness(nativeReviewCli: NativeReviewCli | null): { beforeAgentStart: BeforeAgentStartHandler } {
34
+ const handlers = new Map<string, BeforeAgentStartHandler>();
35
+ const pi = {
36
+ on(name: string, handler: BeforeAgentStartHandler) {
37
+ handlers.set(name, handler);
38
+ },
39
+ events: { emit() {} },
40
+ registerCommand() {},
41
+ registerTool() {},
42
+ } as unknown as ExtensionAPI;
43
+ createGentleAiExtension({ nativeReviewCli })(pi);
44
+ const beforeAgentStart = handlers.get("before_agent_start");
45
+ assert.equal(typeof beforeAgentStart, "function");
46
+ return { beforeAgentStart: beforeAgentStart as BeforeAgentStartHandler };
47
+ }
48
+
49
+ function ctx(overrides: Record<string, unknown> = {}): ExtensionContext {
50
+ return {
51
+ cwd: process.cwd(),
52
+ hasUI: true,
53
+ ui: { notify() {} },
54
+ sessionManager: { getSessionId: () => "review-contract-prompt-session" },
55
+ ...overrides,
56
+ } as unknown as ExtensionContext;
57
+ }
58
+
59
+ const primaryEvent = { systemPrompt: "base" };
60
+
61
+ test("before_agent_start injects the mirrored review execution contract for the primary session", async () => {
62
+ const { beforeAgentStart } = harness({} as NativeReviewCli);
63
+ const result = await beforeAgentStart(primaryEvent, ctx());
64
+ const expected = mirroredPiOrchestrationText();
65
+ assert.match(result.systemPrompt, /## Gentle AI review execution contract \(mirrored provider bundle 1\.2\.0\)/);
66
+ assert.ok(result.systemPrompt.includes(expected), "the mirrored orchestration/pi.md text must appear verbatim");
67
+ assert.match(result.systemPrompt, /call `gentle_review` with {"operation":"inspect"}/);
68
+ assert.match(result.systemPrompt, /call `gentle_review` with operation `status`, the exact retained `lineageId`, and `workspaceRoot`/);
69
+ assert.match(result.systemPrompt, /Use `gentle_review_capture` for one current returned slot or `gentle_review_capture_group` for the complete current reviewer group/);
70
+ assert.match(result.systemPrompt, /An approved capture awaits acknowledgement; it is not burned\. On `approved`, use bound facade STATUS to obtain or replay the exact provider-issued `acknowledge-approved` continuation, then execute it unchanged\. Only its successful returned envelope burns authority; do not issue STATUS after that burn\./);
71
+ let previousLifecycleIndex = result.systemPrompt.indexOf("## Gentle AI review execution contract");
72
+ for (const marker of [
73
+ 'call `gentle_review` with {"operation":"inspect"}',
74
+ "2. **Freeze once.**",
75
+ "call `gentle_review` with operation `status`",
76
+ "Use `gentle_review_capture` for one current returned slot",
77
+ "5. **Acknowledge exactly.**",
78
+ ]) {
79
+ const markerIndex = result.systemPrompt.indexOf(marker, previousLifecycleIndex + 1);
80
+ assert.ok(markerIndex > previousLifecycleIndex, `${marker} must follow the previous lifecycle step`);
81
+ previousLifecycleIndex = markerIndex;
82
+ }
83
+ assert.doesNotMatch(result.systemPrompt, /authority is already burned/);
84
+ assert.doesNotMatch(result.systemPrompt, /gentle-ai review status\b.*--agent pi/);
85
+ });
86
+
87
+ test("before_agent_start does not inject the review execution contract for a named agent session", async () => {
88
+ const { beforeAgentStart } = harness({} as NativeReviewCli);
89
+ const result = await beforeAgentStart({ agentName: "review-readability", systemPrompt: "base" }, ctx());
90
+ assert.doesNotMatch(result.systemPrompt, /Gentle AI review execution contract/);
91
+ });
92
+
93
+ test("before_agent_start does not inject the review execution contract for an SDD executor session", async () => {
94
+ const { beforeAgentStart } = harness({} as NativeReviewCli);
95
+ const result = await beforeAgentStart({ systemPrompt: "SDD apply executor body" }, ctx());
96
+ assert.doesNotMatch(result.systemPrompt, /Gentle AI review execution contract/);
97
+ });
98
+
99
+ test("before_agent_start injects nothing when nativeReviewCli is null", async () => {
100
+ const { beforeAgentStart } = harness(null);
101
+ const result = await beforeAgentStart(primaryEvent, ctx());
102
+ assert.doesNotMatch(result.systemPrompt, /Gentle AI review execution contract/);
103
+ });
104
+
105
+ // gentle-ai R1/R3: a tampered mirrored orchestration/pi.md must never be spliced into the system prompt.
106
+ function tempMirror(text: string, entrySha256: string): string {
107
+ const root = mkdtempSync(join(tmpdir(), "gentle-pi-mirror-"));
108
+ const bundleDir = join(root, "v9.9.9", "bundle", "orchestration");
109
+ mkdirSync(bundleDir, { recursive: true });
110
+ writeFileSync(join(bundleDir, "pi.md"), text, "utf8");
111
+ const lock = JSON.stringify({ contract_semver: "9.9.9", entries: { "orchestration/pi.md": entrySha256 } });
112
+ writeFileSync(join(root, "provider-contract.lock.json"), lock, "utf8");
113
+ return root;
114
+ }
115
+
116
+ test("rejects a tampered mirror, accepts a matching one, and warns once", async () => {
117
+ const tampered = tempMirror("tampered contract text", "0".repeat(64));
118
+ const matchingText = "matching contract text";
119
+ const matching = tempMirror(matchingText, createHash("sha256").update(Buffer.from(matchingText, "utf8")).digest("hex"));
120
+ try {
121
+ // Cache-bust: isolate this module's fragment cache from the real-mirror tests above.
122
+ const cacheBustedUrl = `${pathToFileURL(join(import.meta.dirname, "..", "extensions", "gentle-ai.ts")).href}?tamper=${Math.random()}`;
123
+ type Testing = {
124
+ readMirroredReviewContractFragment: (mirrorRoot?: string) => string | null;
125
+ loadReviewContractPromptFragment: (ctx: ExtensionContext, mirrorRoot?: string) => string | null;
126
+ };
127
+ const fresh = (await import(cacheBustedUrl)) as { __testing: Testing };
128
+ assert.equal(fresh.__testing.readMirroredReviewContractFragment(tampered), null);
129
+ assert.ok(fresh.__testing.readMirroredReviewContractFragment(matching)?.includes(matchingText));
130
+ let notifyCount = 0;
131
+ const spyCtx = { hasUI: true, ui: { notify: () => { notifyCount += 1; } } } as unknown as ExtensionContext;
132
+ fresh.__testing.loadReviewContractPromptFragment(spyCtx, tampered);
133
+ fresh.__testing.loadReviewContractPromptFragment(spyCtx, tampered);
134
+ assert.equal(notifyCount, 1);
135
+ } finally {
136
+ rmSync(tampered, { recursive: true, force: true });
137
+ rmSync(matching, { recursive: true, force: true });
138
+ }
139
+ });
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { execFileSync } from "node:child_process";
3
- import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import test from "node:test";
@@ -47,7 +47,7 @@ function context(cwd: string): ExtensionContext {
47
47
  }
48
48
 
49
49
  function repository(t: test.TestContext): string {
50
- const cwd = mkdtempSync(join(tmpdir(), "gentle-pi-lock-status-"));
50
+ const cwd = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-lock-status-")));
51
51
  t.after(() => rmSync(cwd, { recursive: true, force: true }));
52
52
  execFileSync("git", ["init", "-b", "main"], { cwd });
53
53
  writeFileSync(join(cwd, "app.ts"), "export const value = 1;\n");