gentle-pi 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +336 -137
  2. package/assets/agents/gentle-ai-explore.md +1 -0
  3. package/assets/agents/gentle-ai-verify.md +6 -4
  4. package/assets/agents/gentle-ai-worker.md +7 -1
  5. package/assets/orchestrator-delegation-reference.md +163 -0
  6. package/assets/orchestrator-delegation.md +25 -176
  7. package/assets/orchestrator-tracking.md +1 -1
  8. package/assets/orchestrator-verification.md +14 -8
  9. package/assets/orchestrator-writer.md +5 -1
  10. package/assets/orchestrator.md +9 -9
  11. package/bin/gentle-shell.mjs +170 -22
  12. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
  13. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/README.md +7 -0
  14. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/manifest.json +11 -2
  15. package/contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md +92 -0
  16. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-capabilities.baseline.json +8 -1
  17. package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-roles.baseline.json +1 -1
  18. package/docs/assets/diagrams/agent-orchestration.svg +54 -15
  19. package/docs/assets/diagrams/gentleman-workflow.svg +69 -11
  20. package/docs/assets/diagrams/measured-routing.svg +49 -0
  21. package/docs/assets/diagrams/native-review.svg +52 -12
  22. package/docs/assets/diagrams/odd-workflow.svg +128 -66
  23. package/docs/assets/diagrams/permission-modes.svg +51 -0
  24. package/docs/assets/features/audio-notifications.png +0 -0
  25. package/docs/assets/features/command-palette.png +0 -0
  26. package/docs/bundled-install.md +33 -3
  27. package/docs/delegated-verification.md +10 -8
  28. package/docs/gentle-agents-activity.md +11 -5
  29. package/docs/gentle-shell-uninstall.md +8 -1
  30. package/docs/gentle-shell.md +5 -4
  31. package/docs/install-wizard.md +8 -1
  32. package/docs/permission-modes.md +1 -1
  33. package/docs/readme-reference.md +107 -19
  34. package/docs/yolo-mode.md +21 -8
  35. package/extensions/codegraph-tools.ts +68 -6
  36. package/extensions/gentle-agents.ts +99 -12
  37. package/extensions/gentle-ai.ts +284 -52
  38. package/extensions/gentle-herdr-activity.ts +2 -3
  39. package/extensions/gentle-shell.ts +27 -12
  40. package/extensions/gentle-todo.ts +43 -9
  41. package/extensions/quiet-tools.ts +1 -1
  42. package/extensions/session-scratch.ts +365 -0
  43. package/extensions/startup-banner.ts +2 -3
  44. package/lib/agent-home.ts +8 -3
  45. package/lib/agents-protocol.ts +3 -0
  46. package/lib/agents-runner.ts +55 -6
  47. package/lib/agents-view.ts +3 -1
  48. package/lib/bounded-writer-admission.ts +51 -0
  49. package/lib/command-palette-catalog.ts +0 -4
  50. package/lib/commit-slice-signal.ts +100 -0
  51. package/lib/context-backstop.ts +84 -0
  52. package/lib/delivered-test-lint.ts +80 -0
  53. package/lib/destructive-command-guard.ts +120 -9
  54. package/lib/gauge-theme.ts +146 -0
  55. package/lib/gentle-ai-binary.ts +28 -7
  56. package/lib/gentle-shell-bundled.ts +39 -3
  57. package/lib/gentle-shell-import-pi.ts +233 -0
  58. package/lib/gentle-shell-launcher.ts +145 -11
  59. package/lib/gentle-shell-uninstall.ts +18 -1
  60. package/lib/herdr-activity.ts +24 -0
  61. package/lib/native-review-cli.ts +9 -1
  62. package/lib/orchestrator-discovery.ts +5 -1
  63. package/lib/orchestrator-presence.ts +9 -0
  64. package/lib/orchestrator-scope.ts +21 -3
  65. package/lib/provider-contract-bundle.ts +99 -53
  66. package/lib/review-candidate-view-owner.ts +29 -19
  67. package/lib/review-candidate-view.ts +36 -2
  68. package/lib/review-risk-assessment.ts +43 -38
  69. package/lib/review-session-standing-permission.ts +3 -1
  70. package/lib/session-profile-freeze.ts +30 -1
  71. package/lib/session-scratch.ts +474 -0
  72. package/lib/shell-bar.ts +25 -25
  73. package/lib/shell-changes-view.ts +80 -13
  74. package/lib/shell-gauge.ts +49 -6
  75. package/lib/shell-todo.ts +23 -4
  76. package/lib/shell-usage-view.ts +5 -1
  77. package/lib/shell-usage.ts +7 -7
  78. package/lib/verifier-admission.ts +29 -0
  79. package/lib/yolo-temp-scratch.ts +69 -0
  80. package/package.json +1 -2
  81. package/runtime/gentle-ai-binary.mjs +28 -7
  82. package/runtime/gentle-shell-bundled.mjs +38 -2
  83. package/runtime/gentle-shell-import-pi.mjs +234 -0
  84. package/runtime/gentle-shell-launcher.mjs +143 -9
  85. package/runtime/gentle-shell-uninstall.mjs +18 -1
  86. package/runtime/native-review-cli.mjs +8 -0
  87. package/runtime/review-risk-assessment.mjs +43 -38
  88. package/scripts/build-runtime-modules.mjs +1 -0
  89. package/scripts/bundled-engram-migration.mjs +125 -0
  90. package/scripts/gentle-ai-installer.mjs +10 -10
  91. package/scripts/installer-server.mjs +14 -4
  92. package/scripts/main-channel.mjs +3 -2
  93. package/scripts/run-test-suite.mjs +5 -0
  94. package/scripts/verify-package-files.mjs +19 -13
  95. package/skills/gentle-ai/SKILL.md +3 -3
  96. package/skills/work-unit-commits/SKILL.md +5 -3
  97. package/tests/agent-risk-escalation.test.ts +37 -10
  98. package/tests/agents-runner.test.ts +260 -3
  99. package/tests/autonomous-guard.test.ts +122 -0
  100. package/tests/bounded-writer-admission.test.ts +11 -3
  101. package/tests/card-content-policy.test.ts +19 -9
  102. package/tests/child-safety.test.ts +39 -0
  103. package/tests/codegraph-tools.test.ts +123 -2
  104. package/tests/command-palette.test.ts +14 -4
  105. package/tests/commit-slice-signal.test.ts +162 -0
  106. package/tests/context-backstop.test.ts +159 -0
  107. package/tests/delegate-for-reason-contract.test.ts +91 -24
  108. package/tests/delivered-test-lint.test.ts +105 -0
  109. package/tests/destructive-command-guard.test.ts +85 -1
  110. package/tests/engram-safety.test.ts +1 -1
  111. package/tests/fixtures/primary-session-prompt/gentleman.golden.md +143 -0
  112. package/tests/fixtures/provider-contract-bundle/v1.4.0/README.md +29 -0
  113. package/tests/fixtures/provider-contract-bundle/v1.4.0/manifest.json +83 -0
  114. package/tests/fixtures/provider-contract-bundle/v1.4.0/orchestration/pi.md +70 -0
  115. package/tests/fixtures/provider-contract-bundle/v1.4.0/routing/pi.md +92 -0
  116. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/lens.schema.json +16 -0
  117. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/refuter.schema.json +1 -0
  118. package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/targeted-validator.schema.json +1 -0
  119. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/lens.json +1 -0
  120. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/refuter.json +1 -0
  121. package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/targeted-validator.json +1 -0
  122. package/tests/gauge-theme.test.ts +120 -0
  123. package/tests/gentle-agents.test.ts +282 -3
  124. package/tests/gentle-ai-binary.test.ts +1 -1
  125. package/tests/gentle-ai-child-guards.test.ts +61 -0
  126. package/tests/gentle-ai-doctor-herdr.test.ts +94 -0
  127. package/tests/gentle-ai-installer.test.ts +51 -51
  128. package/tests/gentle-shell-bin.test.ts +163 -0
  129. package/tests/gentle-shell-bundled.test.ts +169 -6
  130. package/tests/gentle-shell-import-pi.test.ts +262 -0
  131. package/tests/gentle-shell-launcher.test.ts +79 -0
  132. package/tests/gentle-shell-uninstall.test.ts +30 -0
  133. package/tests/gentle-shell.test.ts +42 -9
  134. package/tests/gentle-todo.test.ts +179 -0
  135. package/tests/headless-delivery-question.test.ts +58 -0
  136. package/tests/herdr-activity.test.ts +34 -1
  137. package/tests/installer-server.test.ts +10 -2
  138. package/tests/installer-windows-bootstrap.test.ts +33 -4
  139. package/tests/native-review-capability-contract.test.ts +7 -1
  140. package/tests/native-review-parity.test.ts +22 -1
  141. package/tests/no-self-pause-contract.test.ts +5 -2
  142. package/tests/odd-integration.test.ts +5 -1
  143. package/tests/odd-routing-canonical-ratchet.test.ts +127 -162
  144. package/tests/odd-routing-contract.test.ts +115 -30
  145. package/tests/orchestrator-budget.test.ts +19 -8
  146. package/tests/orchestrator-discovery.test.ts +29 -0
  147. package/tests/orchestrator-rdd-ownership.test.ts +3 -1
  148. package/tests/orchestrator-scope.test.ts +66 -0
  149. package/tests/orchestrator-status-publication-contract.test.ts +12 -8
  150. package/tests/package-manifest.test.ts +24 -20
  151. package/tests/pi-update-launcher.test.ts +21 -2
  152. package/tests/primary-session-prompt.test.ts +101 -0
  153. package/tests/provider-contract-bundle.test.ts +118 -0
  154. package/tests/provider-contract-mirror.test.ts +24 -0
  155. package/tests/quiet-tool-rendering.test.ts +23 -0
  156. package/tests/rdd-aware-verification-contract.test.ts +29 -11
  157. package/tests/rdd-status-line.test.ts +79 -0
  158. package/tests/review-agent-end-preflight.test.ts +6 -0
  159. package/tests/review-candidate-view.test.ts +49 -0
  160. package/tests/review-contract-prompt.test.ts +5 -2
  161. package/tests/review-controller-native-routing.test.ts +14 -1
  162. package/tests/review-controller.test.ts +1 -1
  163. package/tests/review-host-relay-routing.test.ts +59 -2
  164. package/tests/review-loadout-rdd-off-sdk.test.ts +237 -0
  165. package/tests/review-owner-identity.test.ts +132 -0
  166. package/tests/review-risk-assessment.test.ts +163 -76
  167. package/tests/review-session-standing-permission-controller.test.ts +14 -1
  168. package/tests/review-session-standing-permission.test.ts +27 -1
  169. package/tests/run-test-suite.test.ts +15 -0
  170. package/tests/runtime-harness.mjs +16 -6
  171. package/tests/session-changes-shell.test.ts +1 -1
  172. package/tests/session-profile-freeze.test.ts +23 -0
  173. package/tests/session-scratch.test.ts +945 -0
  174. package/tests/session-subject-routing-contract.test.ts +2 -1
  175. package/tests/shell-bar.test.ts +7 -0
  176. package/tests/shell-changes-view.test.ts +137 -1
  177. package/tests/shell-gauge.test.ts +42 -0
  178. package/tests/small-task-inline-contract.test.ts +37 -0
  179. package/tests/standalone-config-home.test.ts +324 -0
  180. package/tests/startup-banner.test.ts +5 -4
  181. package/tests/startup-home-repo.test.ts +34 -0
  182. package/tests/support/orchestrator-modules.ts +1 -0
  183. package/tests/task-size-routing-contract.test.ts +69 -9
  184. package/tests/test-scratch-contract.test.ts +102 -0
  185. package/tests/test-strength-contract.test.ts +36 -1
  186. package/tests/tool-batching-contract.test.ts +64 -0
  187. package/tests/verification-proportionality.test.ts +46 -0
  188. package/tests/verifier-admission.test.ts +137 -0
  189. package/tests/verify-agent-contract.test.ts +8 -8
  190. package/tests/windows-hidden-processes.test.ts +5 -0
  191. package/tests/writer-edit-surface-scope.test.ts +3 -0
  192. package/tests/writer-feature-document-admission.test.ts +156 -0
  193. package/tests/yolo-mode.test.ts +18 -0
  194. package/tests/yolo-temp-scratch.test.ts +71 -0
  195. package/scripts/mirror-odd-routing.mjs +0 -242
  196. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/orchestration/pi.md +0 -0
  197. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/lens.schema.json +0 -0
  198. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/refuter.schema.json +0 -0
  199. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/targeted-validator.schema.json +0 -0
  200. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/lens.json +0 -0
  201. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/refuter.json +0 -0
  202. /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/targeted-validator.json +0 -0
@@ -2,7 +2,9 @@
2
2
 
3
3
  Parent Pi session only. Load it when the Writer rule fires or an explicitly activated Judgment Day fix batch is dispatched; small tasks never need it. The Writer rule fires on a named reason (parallel units, the context backstop), never on size alone or a price ratio: a large task without a reason stays inline, following its logbook. Configured per-agent models (`subagents.json` `model_profiles`) still apply to every writer it launches. Each writer prompt requires the self-review of the Parallel review protocol in `orchestrator-verification.md`. A reason holds only when the launch delivers it:
4
4
 
5
- - **Parallelism**: with the Background subagent policy on, launch every disjoint unit in the same turn via `subagent_run` with `mode: "background"`, then wait for all completions before the Seam check; serial launches void the reason: if units cannot launch together, work inline.
5
+ - **Invariants**: each writer prompt names the existing helpers its unit must reuse for shared state (for example the function that adds an entry other commands also add) and the invariants they keep; units built in parallel cannot see each other, so this is the only place they learn them.
6
+ - **Size threshold**: a subagent costs one to two minutes of reading and setup before it writes anything; delegate a unit only when it needs several minutes of work (several files or roughly 100+ changed lines). Several small independent changes are faster inline, one after another.
7
+ - **Parallelism**: a unit is one independent piece of behavior with its own code, tests, and docs; never split one behavior into a test writer, a docs writer, and inline code, and never delegate a unit that depends on another unit still in progress. With the Background subagent policy on, launch every independent unit in the same turn via `subagent_run` with `mode: "background"`, then wait for all completions before the Seam check; serial launches void the reason: if units cannot launch together, work inline.
6
8
 
7
9
  #### Allowed edit surfaces (MANDATORY)
8
10
 
@@ -15,6 +17,8 @@ Before launching a bounded writer (`gentle-ai-worker`, a user-configured `worker
15
17
  - pre-existing untracked targets the writer may write, listed explicitly;
16
18
  - nothing beyond the delegated task — a surface wider than the task is the same defect as no surface at all.
17
19
 
20
+ Test scratch stays outside the worktree (`mktemp -d`), never in the repository (nor caches or `TMPDIR`); state needed inside uses an excluded path the test removes. The runtime owns session scratch; `/gentle:scratch` lists or cleans recorded clones and worktrees.
21
+
18
22
  If the surface genuinely cannot be derived, do not launch the writer or ask the human to author paths. Derive a candidate set first — the exact paths this task would touch — and present that enumerated list as an approve/decline choice under the Lossless Blocking Prompts rules in `orchestrator-prompts.md`. A free-text question asking which paths or globs to authorize is never a valid escalation: computing it is the parent's job.
19
23
 
20
24
  Relay a writer's `interaction_required` payload about edit surfaces the same way: present its derived candidate paths as the choice, and add or drop paths only on the human's explicit instruction.
@@ -14,6 +14,8 @@ You are a COORDINATOR, not the default executor for substantial work. Maintain o
14
14
 
15
15
  Keep synthesis short by default: decision, outcome, next action. Expand only when the user asks or the situation requires detail.
16
16
 
17
+ Batch independent tool calls in one turn (parallel calls or one multi-file command). Never spend a turn only on a `todo` or status update.
18
+
17
19
  ## Language Boundary
18
20
 
19
21
  Reply-language style and the active persona's Spanish variant are defined once in the identity/harness section above (its `Current persona mode:` line). The rules below are delegation/artifact-scoped and not restated there:
@@ -32,7 +34,7 @@ A task is **small** when all three hold:
32
34
  2. **Contained risk** — no high-risk item below.
33
35
  3. **Resumable from the diff** — resume test: if the session stopped now, someone could finish from the original request and `git diff` alone.
34
36
 
35
- The number of files, commands or tests, fixes, or a requested `todo` list never decides size. A task is **large** only when the resume test fails (several sessions, external waits, separate deliverables, requirements compaction could lose); large tasks get ODD tracking and workers only by the Writer rule, else inline.
37
+ The number of files, commands or tests, fixes, or a requested `todo` list never decides size. A task is **large** only when the resume test fails (several sessions, external waits, deliverables the user wants apart (never several features asked at once), requirements compaction could lose); large tasks get ODD tracking and workers only by the Writer rule, else inline.
36
38
 
37
39
  Small path: inline (parent; `bash_background` policy); observe RED inline before the fix; run the focused test and the suite inline, once each. No explore, worker, or verifier; no feature document, mirror, or commits unless the user asks; `todo` optional. It needs no lazy asset.
38
40
 
@@ -40,8 +42,6 @@ Small path: inline (parent; `bash_background` policy); observe RED inline before
40
42
 
41
43
  **Risk line**: close every code change, small path or delegated, with `Risk: item N (reason)` or `Risk: none` per the list; any item → Verification rule.
42
44
 
43
- ODD (Default Workflow, harness section above) is mandatory on every request, with the harness's applicable test-first policy (RED, GREEN, refactor); detail: `orchestrator-delegation.md`, `orchestrator-memory.md`.
44
-
45
45
  ## Delegation Rules
46
46
 
47
47
  Core question: does this inflate parent context without need?
@@ -50,19 +50,19 @@ Before launching bounded writer (`gentle-ai-worker` or `worker`), derive nonempt
50
50
 
51
51
  ## Mechanisms
52
52
 
53
- Mandatory Delegation Triggers — each mechanism turns on only by its own trigger and is then mandatory (prefer `subagent_run`; role missing/unusable: native `Agent`, same read-only constraints; report fallback). When it resolves, re-evaluate task size.
53
+ Delegation Triggers — each mechanism turns on only when its own trigger condition truly holds, and only then is it mandatory; no trigger holds by default, and when in doubt work inline (prefer `subagent_run`; role missing/unusable: native `Agent`, same read-only constraints; report fallback). When it resolves, re-evaluate task size.
54
54
 
55
55
  1. **Ask** — open product or design decision → one focused question; stop and wait.
56
56
  2. **Evidence-budget rule** — understanding needs more than one read batch or >~5 sequential lookups → one `gentle-ai-explore`, handoff at most ~2k tokens with `path:line` evidence; one spot check only; never for reading before an inline write.
57
- 3. **Verification rule** — high risk → independent `gentle-ai-verify` after the change's own checks (`orchestrator-verification.md`); otherwise checks run inline.
57
+ 3. **Verification rule** — only a named high-risk item (assess `escalate`) → independent `gentle-ai-verify`; else inline; runtime admission, gates/overengineering: `orchestrator-verification.md`.
58
58
  4. **Track** — large task → feature document, Engram mirror, `todo`, work-unit commits (`orchestrator-tracking.md`, `orchestrator-memory.md`).
59
- 5. **Writer rule** — never by file count or a large task alone; only for a reason (`orchestrator-writer.md`): 2+ independent units, disjoint files, each heavier than a subagent start, launched together in background, else inline; Context backstop.
59
+ 5. **Writer rule** — never by file count or a large task alone; only for a reason (`orchestrator-writer.md`): 2+ independent units of behavior (separate features or fixes that do not depend on each other), each heavier than a subagent start (a subagent spends one to two minutes reading before it writes, so a unit qualifies only when it needs several minutes of work: several files or roughly 100+ changed lines; a small change to one module or one command is done inline even when several such changes are independent), launched together in background, else inline; Context backstop. Tests, docs, and code of the same behavior are one unit, never separate writers; a delivery that depends on an earlier one is not independent.
60
60
  6. **Incident rule** — diagnose wrong cwd/worktree/git/tooling incidents separately before resuming.
61
- 7. **Context backstop** — parent context past ~150k tokens → delegate the next bounded unit. Bound command output (counts, `--stat`, `tail`).
61
+ 7. **Context backstop** — when the runtime signals it → delegate the next unit needing several steps. Bound command output (counts, `--stat`, `tail`).
62
62
 
63
63
  {{GENTLE_PI_BACKGROUND_POLICY}}; rules: delegation background-subagents block.
64
64
 
65
- Per-action table, Work Routing Ladder, Canonical Workflows: `orchestrator-delegation.md`; blocking-prompt relays and provider defects: `orchestrator-prompts.md`.
65
+ Per-action table, Work Routing Ladder, Canonical Workflows: `orchestrator-delegation-reference.md`; blocking-prompt relays and provider defects: `orchestrator-prompts.md`.
66
66
 
67
67
  ## Memory Contract
68
68
 
@@ -83,7 +83,7 @@ This package injects the mirrored provider-bundle review execution contract into
83
83
  ## Safety
84
84
 
85
85
  - An eligible interactive Pi host may resolve `gentle-ai.review-integration.consent/v3` before the envelope reaches the model. Permission: host-owned. If `gentle_review` returns the envelope unresolved, it is still the original provider-owned two-choice contract. Use `ask_user_choice` exactly or relay losslessly and stop. Never add the host action to a decoded or relayed provider envelope.
86
- - Never commit unless the user explicitly asks.
86
+ - Never commit unless the user explicitly asks, except the work-unit commits authorized large ODD implementation makes on its feature branch (ODD step 6).
87
87
  - Ask before destructive git operations, publishing, or irreversible file changes.
88
88
  - Parallel writers only with disjoint Allowed edit surfaces (runtime-enforced) or isolated worktrees.
89
89
  - Keep session work inside the project root and registered same-clone worktrees; ask before any read or write outside it, naming the absolute path. Grants are per-target, per-session, never blanket: in-project scripts naming outside paths are not standing consent.
@@ -9,6 +9,7 @@ import {
9
9
  chmodSync,
10
10
  closeSync,
11
11
  constants as fsConstants,
12
+ copyFileSync,
12
13
  existsSync,
13
14
  fstatSync,
14
15
  lstatSync,
@@ -50,22 +51,31 @@ import {
50
51
  parseRawLauncherConfig,
51
52
  planSpawn,
52
53
  POST_INSTALL_REMOVAL_SOURCES,
54
+ withLauncherPackagesExcluded,
53
55
  postInstallRemovals,
54
56
  provisionedEntry,
57
+ migrateStandaloneConfig,
55
58
  recordProvisioned,
56
59
  resolveHome,
57
60
  resolvePiRuntime,
58
61
  restoreJsonField,
62
+ separateHomeHint,
59
63
  shellQuote,
64
+ standaloneConfigHome,
65
+ userPiHome,
60
66
  } from "../runtime/gentle-shell-launcher.mjs";
67
+ import { formatImportPiReport, importPi } from "../runtime/gentle-shell-import-pi.mjs";
61
68
  import {
62
69
  BUNDLED_VERIFY_ENV,
63
70
  bundledEngramSchema,
64
71
  classifyEngramStartup,
65
72
  ENGRAM_COMPANION_SOURCE,
73
+ ENGRAM_CORE_CHANGED_NOTICE,
74
+ ENGRAM_PRIVATE_REQUIRED_NOTICE,
66
75
  ENGRAM_PROVISION_FAILED_NOTICE,
67
76
  ENGRAM_RECOVERY_NOTICE,
68
77
  engramHeaderAllows,
78
+ GENTLE_SHELL_CONFIG_HOME_ENV,
69
79
  engramProbeAllowed,
70
80
  engramProvenanceMatches,
71
81
  engramProvenancePath,
@@ -74,6 +84,7 @@ import {
74
84
  probeEngramEndpoint,
75
85
  sqliteHeaderUserVersion,
76
86
  supportsEngramStartupPolicy,
87
+ supportsEngramSessionOwned,
77
88
  bundledChildEnv,
78
89
  bundledPi,
79
90
  bundledSetupEnv,
@@ -95,6 +106,7 @@ import {
95
106
  import { bundledGentleAiPaths, GENTLE_AI_VERSION, gentleAiBinaryPath, gentleAiIsolationEnv, PackageLocalGentleAiBinaryMissingError } from "../runtime/gentle-ai-binary.mjs";
96
107
  import { DEFAULT_THEME_NAME, installIsolatedTuiModeSetting } from "../scripts/install-tui-mode-setting.mjs";
97
108
  import { artifactFor, engramPinVersion } from "../scripts/installer-downloads.mjs";
109
+ import { migrateBundledEngram, migrationPending, supportsBundledSessionOwned, MIGRATION_PENDING_NOTICE, MIGRATION_USAGE } from "../scripts/bundled-engram-migration.mjs";
98
110
  import { shouldCheckStartupUpdate, findStartupUpdate } from "../runtime/gentle-shell-startup-update.mjs";
99
111
  import { promptStartupUpdate } from "../runtime/gentle-shell-update-panel.mjs";
100
112
  import { applyStartupChoice, omitStartupVersion, resolveStartupRelaunch } from "../runtime/gentle-shell-startup-flow.mjs";
@@ -268,12 +280,24 @@ function ownEngramStore(observed, core) {
268
280
  // of the default endpoint. No Engram executable, SQLite, identity repair, or
269
281
  // contact with an explicitly selected endpoint.
270
282
  async function classifyBundledMemory() {
283
+ if (migrationPending(bundledInstall.root)) return { policy: "disabled", notice: MIGRATION_PENDING_NOTICE };
271
284
  const core = verifiedBundledCore();
272
285
  const decision = { env: process.env, core: core !== undefined, coreVersion: engramPinVersion };
273
286
  if (!engramProbeAllowed(process.env)) return classifyEngramStartup({ ...decision, store: "unknown", endpoint: { kind: "unknown" } });
274
287
  const observed = observeEngramStore();
275
288
  const endpoint = await probeEngramEndpoint();
276
- return classifyEngramStartup({ ...decision, store: observed.store, endpoint, provenance: ownEngramStore(observed, core) });
289
+ const result = classifyEngramStartup({ ...decision, store: observed.store, endpoint, provenance: ownEngramStore(observed, core) });
290
+ if (result.policy === "disabled" && endpoint.kind === "refused" && observed.store === "present" && core) {
291
+ // Wording only: an unreadable record or core keeps the generic notice and
292
+ // must never abort startup.
293
+ try {
294
+ const record = parseEngramProvenance(readSmallRegularFile(engramProvenancePath(bundledInstall.root, process.platform), 4096));
295
+ if (record?.dataDir === observed.dataDir && record.instanceId === observed.instanceId && record.coreSha256 !== sha256File(core)) {
296
+ return { ...result, notice: ENGRAM_CORE_CHANGED_NOTICE };
297
+ }
298
+ } catch { /* Keep the generic disabled notice. */ }
299
+ }
300
+ return result;
277
301
  }
278
302
 
279
303
  // Verified with the published Engram v3.4.0 darwin-arm64 binary in an isolated
@@ -333,6 +357,7 @@ async function ensureEngramCompanion(home, runtime, stdio, timeoutMs) {
333
357
  if (!Array.isArray(packages)) return { ok: false, exitCode: 1, message: "gentle-shell: cannot verify Engram startupPolicy from non-array packages" };
334
358
  let pinned = false;
335
359
  let local = false;
360
+ let localOwned = true;
336
361
  let npmSelected = false;
337
362
  for (const entry of packages) {
338
363
  const source = typeof entry === "string" ? entry : entry?.source;
@@ -346,9 +371,10 @@ async function ensureEngramCompanion(home, runtime, stdio, timeoutMs) {
346
371
  return { ok: false, exitCode: 1, message: `gentle-shell: selected companion ${source} lacks engram.startupPolicy=1; update it and recheck (selection preserved)` };
347
372
  }
348
373
  local = true;
374
+ localOwned &&= supportsEngramSessionOwned(readJsonIfExists(join(selected, "package.json")));
349
375
  }
350
- if (local && !npmSelected) return { ok: true };
351
- if (pinned && supportsEngramStartupPolicy(readJsonIfExists(metadata))) return { ok: true };
376
+ if (local && !npmSelected) return { ok: true, sessionOwned: localOwned };
377
+ if (pinned && supportsEngramStartupPolicy(readJsonIfExists(metadata))) return { ok: true, sessionOwned: localOwned && supportsEngramSessionOwned(readJsonIfExists(metadata)) };
352
378
  process.stderr.write(`gentle-shell: installing policy-capable companion ${ENGRAM_COMPANION_SOURCE} through Pi\n`);
353
379
  const result = await spawnAndWait(runtime.command, [...runtime.args, "install", ENGRAM_COMPANION_SOURCE], buildSetupEnv(home, runtime), stdio, timeoutMs);
354
380
  let declared = false;
@@ -359,7 +385,7 @@ async function ensureEngramCompanion(home, runtime, stdio, timeoutMs) {
359
385
  if (!result.ok || result.timedOut || !declared || !supportsEngramStartupPolicy(readJsonIfExists(metadata))) {
360
386
  return { ok: false, exitCode: 1, message: "gentle-shell: companion installation did not establish engram.startupPolicy=1; the Engram guard still keeps unverified stores closed. Repair gentle-engram, then restart Shell." };
361
387
  }
362
- return { ok: true };
388
+ return { ok: true, sessionOwned: localOwned && supportsEngramSessionOwned(readJsonIfExists(metadata)) };
363
389
  }
364
390
 
365
391
  // The bundled children's environment (bundledChildEnv), with the pinned Engram
@@ -627,6 +653,42 @@ function handleHomeCommand(commandArgs) {
627
653
  process.exit(0);
628
654
  }
629
655
 
656
+ // `gentle-shell [home selectors] home import-pi [--dry-run]`: copies the
657
+ // user's pi sign-ins, MCP servers and default model into the resolved
658
+ // separate home (lib/gentle-shell-import-pi.ts). A home that is new or empty
659
+ // is first bootstrapped exactly as a first launch would, so the files this
660
+ // adds never make it look foreign to auto-provisioning (homeIsForeign).
661
+ async function handleImportPiCommand(args) {
662
+ const options = args.commandArgs.slice(1);
663
+ if (options.some((option) => option !== "--dry-run")) fail("gentle-shell home import-pi accepts only --dry-run. Run 'gentle-shell --help'.", 2);
664
+ const dryRun = options.includes("--dry-run");
665
+ let home = resolveHome({ args, env: process.env, homedir: homedir(), config: loadConfig() });
666
+ if (home.mode === "path") home = { ...home, dir: resolvePath(home.dir) };
667
+ const source = resolvePath(userPiHome(process.env, homedir()));
668
+ const refusal = "Choose a separate home with --isolated or --home <path>.";
669
+ if (home.mode === "link") {
670
+ fail(`gentle-shell home import-pi: the selected home is your pi home itself (--link), so there is nothing to import. ${refusal}`, 1);
671
+ }
672
+ if ([source, join(homedir(), ".pi", "agent")].some((piHome) => safeRealpath(piHome) === safeRealpath(home.dir))) {
673
+ fail(`gentle-shell home import-pi: ${home.dir} is your pi home itself, so there is nothing to import. ${refusal}`, 1);
674
+ }
675
+ if (homeNeedsBootstrap(home.dir)) {
676
+ if (dryRun) {
677
+ process.stdout.write(`Would prepare ${home.dir} as a new Gentle Shell home\n`);
678
+ } else {
679
+ try {
680
+ await bootstrapOwnedHome(home);
681
+ } catch (error) {
682
+ fail(`gentle-shell home import-pi: could not prepare ${home.dir} (${error.message}); nothing was imported`, 1);
683
+ }
684
+ process.stdout.write(`Prepared ${home.dir} as a new Gentle Shell home\n`);
685
+ }
686
+ }
687
+ const result = importPi({ source, target: home.dir, dryRun });
688
+ for (const line of formatImportPiReport(result, { source, target: home.dir, dryRun })) process.stdout.write(`${line}\n`);
689
+ process.exitCode = result.failed ? 1 : 0;
690
+ }
691
+
630
692
  // Test/development-only override for the setup subcommand's gentle-ai
631
693
  // executable path. Lets a test point at a stub script (or a deliberately
632
694
  // missing path) without touching the real pinned .gentle-ai/v<version>/gentle-ai
@@ -799,12 +861,18 @@ function buildSetupEnv(home, runtime, piLauncherDir) {
799
861
  const env = bundledSetupEnv({ ...bundledEnv, PI_CODING_AGENT_DIR: home.dir, GENTLE_PI_AGENT_HOME: home.dir }, { dirs: bundledSetupDirs, platform: process.platform });
800
862
  return gentleAiIsolationEnv(env, { packageRoot });
801
863
  }
802
- return {
864
+ const env = {
803
865
  ...process.env,
804
866
  PI_CODING_AGENT_DIR: home.dir,
805
867
  GENTLE_PI_AGENT_HOME: home.dir,
806
868
  PATH: [...(piLauncherDir ? [piLauncherDir] : []), dirname(runtime.command), process.env.PATH ?? ""].join(delimiter),
807
869
  };
870
+ // #2201: the isolated home's own config folder, given to Gentle AI the
871
+ // bundled way (gentleAiIsolationEnv); never an outer session's marker.
872
+ const configHome = standaloneConfigHome(home);
873
+ if (configHome === undefined) delete env[GENTLE_SHELL_CONFIG_HOME_ENV];
874
+ else env[GENTLE_SHELL_CONFIG_HOME_ENV] = configHome;
875
+ return gentleAiIsolationEnv(env, { packageRoot });
808
876
  }
809
877
 
810
878
  // Gentle AI requires a `pi` on its PATH and runs `pi install npm:gentle-pi`
@@ -847,15 +915,34 @@ function sharedPersonaPath() {
847
915
  return join(homedir(), ".pi", "gentle-ai", "persona.json");
848
916
  }
849
917
 
850
- // The persona files a setup run may rewrite. Bundled (S9): the prefix's own,
851
- // which a Gentle AI with GENTLE_AI_PI_CONFIG_DIR (gentle-ai #5462) writes, and
852
- // still the user's, which a pinned Gentle AI without it writes; restoring an
853
- // unchanged file does nothing.
854
- function setupPersonaPaths() {
855
- if (!bundledInstall) return [sharedPersonaPath()];
856
- return [join(bundledGentleAiPaths(bundledInstall.root, process.platform).piConfig, "persona.json"), sharedPersonaPath()];
918
+ // The persona files a setup run may rewrite. Bundled (S9) and the standalone
919
+ // isolated home (#2201): its own config folder's, which a Gentle AI with
920
+ // GENTLE_AI_PI_CONFIG_DIR (gentle-ai #5462) writes, and still the user's,
921
+ // which a pinned Gentle AI without it writes; restoring an unchanged file
922
+ // does nothing.
923
+ function setupPersonaPaths(home) {
924
+ if (bundledInstall) return [join(bundledGentleAiPaths(bundledInstall.root, process.platform).piConfig, "persona.json"), sharedPersonaPath()];
925
+ const configHome = standaloneConfigHome(home);
926
+ return configHome === undefined ? [sharedPersonaPath()] : [join(configHome, "persona.json"), sharedPersonaPath()];
857
927
  }
858
928
 
929
+ // The real filesystem behind migrateStandaloneConfig (#2201).
930
+ const configMigrationFs = {
931
+ kind: (path) => {
932
+ try {
933
+ const stat = lstatSync(path);
934
+ return stat.isDirectory() ? "dir" : stat.isFile() ? "file" : "other";
935
+ } catch {
936
+ return "missing";
937
+ }
938
+ },
939
+ list: (path) => readdirSync(path),
940
+ mkdirPrivate: (path) => {
941
+ mkdirSync(path, { recursive: true, mode: 0o700 });
942
+ },
943
+ copyNew: (from, to) => copyFileSync(from, to, fsConstants.COPYFILE_EXCL),
944
+ };
945
+
859
946
  // Bundled setup (S9): the pinned Engram GENTLE_AI_ENGRAM_BIN names, published
860
947
  // once in runtime/ (verified, never brew or the user's PATH). A failure stops
861
948
  // setup rather than let Gentle AI acquire another Engram.
@@ -1134,8 +1221,10 @@ async function runSetupFlow(home, runtime, { dryRun, stdio, timeoutMs }) {
1134
1221
 
1135
1222
  const setupArgs = ["install", "--agent", "pi", "--scope", "global", ...(dryRun ? ["--dry-run"] : [])];
1136
1223
  const piLauncherDir = writeSetupPiLauncher(runtime);
1137
- const env = buildSetupEnv(home, runtime, piLauncherDir);
1138
- const personaSnapshots = setupPersonaPaths().map((path) => [path, safely("snapshot your Pi persona file", path, undefined, () => snapshotFile(path))]);
1224
+ // #2143: a Gentle AI with package exclusion never installs the launcher-owned
1225
+ // gentle-pi; an older one ignores this and runPostInstallCleanup removes it.
1226
+ const env = withLauncherPackagesExcluded(buildSetupEnv(home, runtime, piLauncherDir));
1227
+ const personaSnapshots = setupPersonaPaths(home).map((path) => [path, safely("snapshot your Pi persona file", path, undefined, () => snapshotFile(path))]);
1139
1228
  const statePath = sharedGentleAiStatePath();
1140
1229
  const originalStateText = safely("read your Gentle AI state file", statePath, undefined, () => readParsableJsonText(statePath));
1141
1230
  // Theme restore (never for --link — that home is the user's own
@@ -1387,6 +1476,25 @@ function writeHomeOwnershipMarker(home) {
1387
1476
  writeFileSync(homeOwnershipMarkerPath(home), `${content}\n`, "utf8");
1388
1477
  }
1389
1478
 
1479
+ // The isolated/--home bootstrap: the first launch's own (main, below) and
1480
+ // `home import-pi`'s before it adds files to a new or empty home.
1481
+ async function bootstrapOwnedHome(home) {
1482
+ // 0700: under umask 002 a default mode would leave ~/.gentle-shell group-writable,
1483
+ // which the bundled install's prefix claim must tighten or refuse.
1484
+ mkdirSync(home.dir, { recursive: true, mode: 0o700 });
1485
+ await installIsolatedTuiModeSetting(home.dir);
1486
+ writeHomeOwnershipMarker(home); // R3-001: lets a failed first auto-provision attempt still be retried later
1487
+ }
1488
+
1489
+ function homeNeedsBootstrap(dir) {
1490
+ if (!existsSync(dir)) return true;
1491
+ try {
1492
+ return isDirectory(dir) && readdirSync(dir).length === 0;
1493
+ } catch {
1494
+ return false;
1495
+ }
1496
+ }
1497
+
1390
1498
  // `homeHadContentBeforeBootstrap` must be read by the caller (main, below)
1391
1499
  // before the isolated/--home bootstrap runs: that bootstrap itself creates
1392
1500
  // and seeds a brand-new directory (settings.json with tuiMode, and now the
@@ -1782,12 +1890,27 @@ async function main() {
1782
1890
  process.exit(0);
1783
1891
  }
1784
1892
  if (args.command === "home") {
1785
- handleHomeCommand(args.commandArgs);
1893
+ if (args.commandArgs[0] === "import-pi") await handleImportPiCommand(args);
1894
+ else handleHomeCommand(args.commandArgs);
1786
1895
  return;
1787
1896
  }
1788
1897
  // The verification input applies to `--version` only (scripts/bundled-install.mjs upgradeBundled).
1789
1898
  const verifying = args.version && args.command === undefined ? process.env[BUNDLED_VERIFY_ENV] : undefined;
1790
1899
  const bundled = resolveBundledInstall({ packageRoot, platform: process.platform, fs: bundledFs, verifying });
1900
+ if (args.command === "memory") {
1901
+ if (args.commandArgs.length !== 2 || args.commandArgs[0] !== "migrate" || args.commandArgs[1] !== "--confirm-quiescent") {
1902
+ process.stderr.write(MIGRATION_USAGE);
1903
+ process.exitCode = 2;
1904
+ return;
1905
+ }
1906
+ if (!bundled) fail("gentle-shell: memory migrate requires a prefix-bundled installation", 1);
1907
+ bundledInstall = bundled;
1908
+ const result = migrateBundledEngram({ prefix: bundled.root, core: verifiedBundledCore(), home: homedir(), env: process.env });
1909
+ process.stdout.write(result.stdout);
1910
+ process.stderr.write(result.stderr);
1911
+ process.exitCode = result.code;
1912
+ return;
1913
+ }
1791
1914
  if (args.command === "upgrade") {
1792
1915
  if (bundled) process.exitCode = await handleBundledUpgrade(bundled, args.commandArgs);
1793
1916
  else await handleUpgradeCommand(args.commandArgs);
@@ -1843,7 +1966,11 @@ async function main() {
1843
1966
  });
1844
1967
  if (versionProbe.error) fail(`Could not run the pi runtime at "${runtime.command}": ${versionProbe.error.message}`, 1);
1845
1968
  const versionCheck = checkPiVersion(versionProbe.stdout ?? "");
1846
- if (!versionCheck.ok) fail(versionCheck.message, 1);
1969
+ // A managed self update is how an old Pi gets replaced, so the minimum-version
1970
+ // gate must not block it; everything else still requires the minimum.
1971
+ const managedSelfUpdate = !bundled && args.piSubcommand === "update" && versionCheck.version !== undefined
1972
+ && (await import("../scripts/pi-update.mjs")).managedUpdatePlan(args.passthrough).self;
1973
+ if (!versionCheck.ok && !managedSelfUpdate) fail(versionCheck.message, 1);
1847
1974
 
1848
1975
  if (args.version) {
1849
1976
  process.stdout.write(`${describeVersion({ gentlePiVersion: ownPackageVersion(), piVersion: versionCheck.version, home })}\n`);
@@ -1893,12 +2020,22 @@ async function main() {
1893
2020
  // Isolated-home bootstrap: only on a home gentle-shell has not seen before
1894
2021
  // (link never bootstraps — it reuses the user's own pi agent home as-is).
1895
2022
  if ((home.mode === "isolated" || home.mode === "path") && !existsSync(home.dir)) {
1896
- // 0700: under umask 002 a default mode would leave ~/.gentle-shell group-writable,
1897
- // which the bundled install's prefix claim must tighten or refuse.
1898
- mkdirSync(home.dir, { recursive: true, mode: 0o700 });
1899
- await installIsolatedTuiModeSetting(home.dir);
1900
- writeHomeOwnershipMarker(home); // R3-001: lets a failed first auto-provision attempt still be retried later
1901
- process.stderr.write(`gentle-shell: using a separate home at ${home.dir}. Run 'gentle-shell --link' to reuse your pi sign-ins and chats.\n`);
2023
+ await bootstrapOwnedHome(home);
2024
+ const piSignIns = existsSync(join(userPiHome(process.env, homedir()), "auth.json"));
2025
+ process.stderr.write(`${separateHomeHint(home, { piSignIns })}\n`);
2026
+ }
2027
+
2028
+ // #2201: the standalone isolated home's own config folder, seeded once from
2029
+ // the shared ~/.pi/gentle-ai. Not when an explicit GENTLE_PI_CONFIG_HOME
2030
+ // decides the config home; never blocks the launch.
2031
+ const configHome = bundled || process.env.GENTLE_PI_CONFIG_HOME ? undefined : standaloneConfigHome(home);
2032
+ if (configHome !== undefined) {
2033
+ try {
2034
+ const migration = migrateStandaloneConfig({ source: join(homedir(), ".pi", "gentle-ai"), target: configHome, fs: configMigrationFs });
2035
+ for (const line of migration.lines) process.stderr.write(`${line}\n`);
2036
+ } catch (error) {
2037
+ process.stderr.write(`gentle-shell: could not prepare ${configHome} (${error.message}); continuing\n`);
2038
+ }
1902
2039
  }
1903
2040
 
1904
2041
  if (bundled) {
@@ -2048,6 +2185,16 @@ async function main() {
2048
2185
  // could not be verified is reported, never a reason to stop Shell.
2049
2186
  const companion = await ensureEngramCompanion(home, runtime, ["ignore", 2, 2], 10000);
2050
2187
  if (!companion.ok) process.stderr.write(`${companion.message}\n`);
2188
+ if (bundledMemory.policy === "allow" && !bundledMemory.url) {
2189
+ const mode = process.env.ENGRAM_SERVER_MODE;
2190
+ if (mode !== undefined && mode !== "session-owned") {
2191
+ bundledMemory = { policy: "disabled", notice: "Engram memory disabled: explicit ENGRAM_SERVER_MODE is invalid for a local bundled server; unset it or select exactly session-owned. Your selection has not been overridden." };
2192
+ } else {
2193
+ bundledMemory = companion.ok && companion.sessionOwned && supportsBundledSessionOwned(verifiedBundledCore(), process.env)
2194
+ ? { ...bundledMemory, owned: true }
2195
+ : { policy: "disabled", notice: ENGRAM_PRIVATE_REQUIRED_NOTICE };
2196
+ }
2197
+ }
2051
2198
  // A fresh store's identity and provenance exist before Pi starts, or
2052
2199
  // memory stays off: Pi never runs with allow without a written record.
2053
2200
  if (bundledMemory.fresh && !provisionEngramStore()) bundledMemory = { policy: "disabled", notice: ENGRAM_PROVISION_FAILED_NOTICE };
@@ -2066,6 +2213,7 @@ async function main() {
2066
2213
  passthrough: [...managedHerdrExtensionArgs(home, args), ...passthrough],
2067
2214
  piSubcommand: args.piSubcommand,
2068
2215
  baseEnv: bundledEnv ?? process.env,
2216
+ bundled: Boolean(bundled),
2069
2217
  homedir: homedir(),
2070
2218
  // The spawn below sets no cwd, so pi runs in the launcher's own.
2071
2219
  cwd: process.cwd(),
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "schema": "gentle-pi.review-provider-contract-mirror-lock/v1",
3
3
  "acquisition": "field-test-local",
4
- "contract_semver": "1.3.0",
4
+ "contract_semver": "1.4.0",
5
5
  "source": {
6
6
  "kind": "archive",
7
- "archive_sha256": "b04b6b75d1a446c942a816fbf92932d16f8c3d3001f93159dee2a92bbf2ebe44"
7
+ "archive_sha256": "9d2927a84ba9a596f69029e3f854b16f86ca4c8b32cc5d63683764ccf2fb394b"
8
8
  },
9
- "tree_sha256": "a2b95892ec8f10340d8d1bb4fc805f9c92db000f58b60b382ff859d312fdc023",
9
+ "tree_sha256": "aab5ab7fe7ee6bb180d8bbce21c034cebb0c0d284afcc8664a9b3bb57323c35d",
10
10
  "entries": {
11
- "README.md": "18baab5ee79aefd0bc62a28da0dadcf2162544f57a940c5b859cd6bc4932a085",
12
- "manifest.json": "4e335a987e053dac42425ffe31628253eb43dd26f1235085fa90b200bce13d13",
11
+ "README.md": "3a655c2f19aee02636da062f0cae7a189b7e5f691c8ba8010f2ed50e5d8e8d70",
12
+ "manifest.json": "d2b5765e28df5b2459848379e91a27beb806916fabc96207060ad1cc5e911972",
13
13
  "orchestration/pi.md": "2d7ddd99f1c739a97f4a6b3929e5e2bdddcbf69aed69356395038d90bca25b2f",
14
+ "routing/pi.md": "1c7466e2333c08e386b4b5afb081531146d181030692463431e7695df7f90690",
14
15
  "schemas/lens.schema.json": "9b8a2354a570888486015434501b66b51441a3baf5bf858635e4fff56b2e8298",
15
16
  "schemas/refuter.schema.json": "36f8267c6c3f04600b8f1cb77df70d90858611af822cc81922efd6b3502fb646",
16
17
  "schemas/targeted-validator.schema.json": "40e397195bdfbc6782fc3090f20c5be082e4d14d2855314f99ecb8506133eddc",
@@ -19,8 +20,8 @@
19
20
  "vectors/targeted-validator.json": "7b5a5165e3a913863fb98d2ba5387811860c21333b5c37e6ed3b4e0d121cf2fa"
20
21
  },
21
22
  "generated": {
22
- "generated/provider-capabilities.baseline.json": "f439a3f83394219dae5257a88f112eba5e67f9d4e566eb6d9f3f5608b727c1f7",
23
- "generated/provider-roles.baseline.json": "71982937962834120b8304c795bf0e90c04b262a4293c6b96bf423b68a5781e0"
23
+ "generated/provider-capabilities.baseline.json": "bca708f5f67f506b4acb2cfdcb775b02a51468579fc7d6b4d169785ff411a215",
24
+ "generated/provider-roles.baseline.json": "aa36ec6f7469ac6faab756eec681f68f234cbadc1e12b345a93c6c34aa01f7aa"
24
25
  },
25
26
  "runtimes": [
26
27
  "claude-code",
@@ -19,4 +19,11 @@ It carries no executable content, same as every other file in this bundle: a
19
19
  runtime mirrors and delivers the text as-is and still relies on Go for every
20
20
  review decision, prompt, receipt, and delivery gate.
21
21
 
22
+ ## Routing
23
+
24
+ manifest.json's routing array lists, for closed runtimes only, one
25
+ routing/<runtime>.md file: the exact ODD implementation-routing block Gentle
26
+ AI renders for that runtime, whose prompt Gentle AI does not install. It is
27
+ data only as well: a runtime mirrors and delivers the text as-is.
28
+
22
29
  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.0",
3
+ "contract_semver": "1.4.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": "18baab5ee79aefd0bc62a28da0dadcf2162544f57a940c5b859cd6bc4932a085"
13
+ "sha256": "3a655c2f19aee02636da062f0cae7a189b7e5f691c8ba8010f2ed50e5d8e8d70"
14
14
  },
15
15
  "roles": [
16
16
  {
@@ -70,5 +70,14 @@
70
70
  "sha256": "2d7ddd99f1c739a97f4a6b3929e5e2bdddcbf69aed69356395038d90bca25b2f"
71
71
  }
72
72
  }
73
+ ],
74
+ "routing": [
75
+ {
76
+ "runtime": "pi",
77
+ "file": {
78
+ "path": "routing/pi.md",
79
+ "sha256": "1c7466e2333c08e386b4b5afb081531146d181030692463431e7695df7f90690"
80
+ }
81
+ }
73
82
  ]
74
83
  }