gentle-pi 3.7.0 → 4.0.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 (316) hide show
  1. package/README.md +37 -6
  2. package/assets/agents/gentle-ai-explore.md +4 -4
  3. package/assets/agents/gentle-ai-verify.md +6 -4
  4. package/assets/agents/gentle-ai-worker.md +7 -9
  5. package/assets/orchestrator-delegation.md +40 -41
  6. package/assets/orchestrator-memory.md +1 -22
  7. package/assets/orchestrator-skills.md +1 -1
  8. package/assets/orchestrator.md +12 -24
  9. package/assets/support/strict-tdd-verify.md +4 -266
  10. package/assets/support/strict-tdd.md +8 -360
  11. package/bin/gentle-shell.mjs +254 -29
  12. package/docs/delegated-verification.md +26 -1
  13. package/docs/gentle-agents-activity.md +24 -0
  14. package/docs/gentle-shell.md +92 -28
  15. package/docs/native-authority-architecture.md +2 -2
  16. package/docs/prompt-history.md +280 -0
  17. package/docs/readme-reference.md +112 -206
  18. package/docs/telemetry.md +1 -1
  19. package/docs/yolo-mode.md +86 -0
  20. package/extensions/child-context.ts +26 -0
  21. package/extensions/child-safety.ts +23 -0
  22. package/extensions/gentle-agents.ts +429 -262
  23. package/extensions/gentle-ai.ts +781 -683
  24. package/extensions/gentle-shell.ts +1375 -88
  25. package/extensions/gentle-stats.ts +101 -0
  26. package/extensions/gentle-todo.ts +18 -7
  27. package/extensions/history/atomic-write.ts +38 -0
  28. package/extensions/history/hide-prompts.ts +183 -0
  29. package/extensions/history/index.ts +1419 -0
  30. package/extensions/history/load-shared-history.ts +39 -0
  31. package/extensions/history/selector-helpers.ts +538 -0
  32. package/extensions/history/session-scan.ts +233 -0
  33. package/extensions/history/store.ts +1119 -0
  34. package/extensions/nan-provider.ts +6 -0
  35. package/extensions/quiet-tools.ts +179 -87
  36. package/extensions/resume-hint.ts +60 -0
  37. package/extensions/skill-registry.ts +16 -12
  38. package/extensions/startup-banner.ts +60 -31
  39. package/lib/agent-assets.ts +604 -0
  40. package/lib/agent-profile-pin.ts +12 -0
  41. package/lib/agents-message-delivery.ts +181 -0
  42. package/lib/agents-protocol.ts +60 -0
  43. package/lib/agents-runner.ts +96 -98
  44. package/lib/agents-view.ts +26 -3
  45. package/lib/agents-widget.ts +16 -9
  46. package/lib/append-system-prompt.ts +21 -0
  47. package/lib/bounded-writer-admission.ts +147 -0
  48. package/lib/card-style-policy.ts +60 -0
  49. package/lib/child-context-files.ts +166 -0
  50. package/lib/codemode-renderer.ts +185 -0
  51. package/lib/command-palette-catalog.ts +3 -9
  52. package/lib/command-palette.ts +25 -14
  53. package/lib/destructive-command-guard.ts +144 -0
  54. package/lib/gentle-ai-elapsed-store.ts +87 -0
  55. package/lib/gentle-ai-renderer.ts +196 -39
  56. package/lib/gentle-shell-launcher.ts +24 -13
  57. package/lib/gentle-shell-resume-hint.ts +176 -0
  58. package/lib/history-capture-policy.ts +95 -0
  59. package/lib/model-routing-authority.ts +5 -1
  60. package/lib/nan-provider.ts +227 -0
  61. package/lib/native-review-cli.ts +49 -108
  62. package/lib/odd-phase-inference.ts +231 -0
  63. package/lib/odd-phase.ts +141 -0
  64. package/lib/overlay-repaint.ts +26 -0
  65. package/lib/pi-tui-keys.ts +53 -0
  66. package/lib/review-candidate-view-owner.ts +67 -17
  67. package/lib/review-candidate-view.ts +112 -25
  68. package/lib/review-reminder-receipt.ts +48 -8
  69. package/lib/review-risk-assessment.ts +156 -11
  70. package/lib/review-sidebar-state.ts +223 -0
  71. package/lib/selection-engine.ts +515 -0
  72. package/lib/session-messaging-grants.ts +135 -0
  73. package/lib/session-worktree-registry.ts +14 -2
  74. package/lib/shell-bar.ts +179 -24
  75. package/lib/shell-card.ts +287 -18
  76. package/lib/shell-changes-view.ts +2 -1
  77. package/lib/shell-prompt.ts +102 -6
  78. package/lib/shell-sidebar-layout.ts +78 -14
  79. package/lib/shell-sidebar.ts +15 -1
  80. package/lib/shell-todo.ts +23 -13
  81. package/lib/shell-usage-view.ts +9 -4
  82. package/lib/shell-usage.ts +66 -10
  83. package/lib/stats-collector.ts +381 -0
  84. package/lib/stats-view.ts +431 -0
  85. package/lib/theme-customization.ts +52 -0
  86. package/lib/vim-editor-adapter.ts +379 -0
  87. package/lib/vim-normal-engine.ts +154 -0
  88. package/lib/vim-operator-engine.ts +416 -0
  89. package/lib/vim-policy.ts +49 -0
  90. package/lib/vim-visual-engine.ts +107 -0
  91. package/lib/visual-customization-policy.ts +108 -0
  92. package/lib/visual-customize-view.ts +330 -0
  93. package/lib/visual-profiles.ts +228 -0
  94. package/lib/yolo-session-policy.ts +240 -0
  95. package/package.json +20 -8
  96. package/runtime/gentle-shell-launcher.mjs +23 -12
  97. package/runtime/gentle-shell-resume-hint.mjs +177 -0
  98. package/runtime/native-review-cli.mjs +49 -108
  99. package/runtime/review-risk-assessment.mjs +154 -9
  100. package/scripts/build-runtime-modules.mjs +1 -0
  101. package/scripts/gentle-ai-installer.mjs +14 -13
  102. package/scripts/mirror-odd-routing.mjs +2 -2
  103. package/scripts/run-test-suite.mjs +76 -0
  104. package/scripts/test-packed-runner.mjs +31 -14
  105. package/scripts/verify-package-files.mjs +11 -22
  106. package/skills/branch-pr/SKILL.md +24 -52
  107. package/skills/chained-pr/SKILL.md +31 -15
  108. package/skills/chained-pr/references/chaining-details.md +31 -20
  109. package/skills/gentle-ai/SKILL.md +9 -15
  110. package/skills/issue-creation/SKILL.md +8 -2
  111. package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
  112. package/skills/work-unit-commits/SKILL.md +4 -3
  113. package/tests/agent-profiles.test.ts +18 -0
  114. package/tests/agents-fake-child.ts +2 -2
  115. package/tests/agents-message-delivery.test.ts +106 -0
  116. package/tests/agents-protocol.test.ts +40 -0
  117. package/tests/agents-runner.test.ts +378 -89
  118. package/tests/agents-view-thread-identity.test.ts +169 -0
  119. package/tests/agents-view.test.ts +8 -2
  120. package/tests/agents-widget.test.ts +154 -15
  121. package/tests/append-system-prompt-route.test.ts +160 -0
  122. package/tests/append-system-prompt.test.ts +46 -0
  123. package/tests/artifact-language.test.ts +19 -213
  124. package/tests/ask-user-question.test.ts +44 -1
  125. package/tests/asset-installation-runtime.test.ts +5 -16
  126. package/tests/autonomous-guard.test.ts +69 -1
  127. package/tests/bounded-writer-admission.test.ts +95 -0
  128. package/tests/branch-pr-skill.test.ts +43 -0
  129. package/tests/card-style-policy.test.ts +55 -0
  130. package/tests/chained-pr-skill.test.ts +124 -0
  131. package/tests/child-context-files.test.ts +255 -0
  132. package/tests/child-safety.test.ts +82 -0
  133. package/tests/codemode-rendering.test.ts +491 -0
  134. package/tests/command-palette.test.ts +39 -3
  135. package/tests/delegated-key-learnings-contract.test.ts +0 -76
  136. package/tests/destructive-command-guard.test.ts +84 -0
  137. package/tests/devbinary/native-review-parity.devtest.ts +170 -2
  138. package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
  139. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
  140. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
  141. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
  142. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
  143. package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
  144. package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
  145. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
  146. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
  147. package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
  148. package/tests/generic-agent-tools.test.ts +54 -0
  149. package/tests/gentle-agents.test.ts +1047 -252
  150. package/tests/gentle-ai-binary.test.ts +3 -3
  151. package/tests/gentle-ai-elapsed-store.test.ts +68 -0
  152. package/tests/gentle-ai-installer.test.ts +68 -54
  153. package/tests/gentle-ai-renderer.test.ts +487 -8
  154. package/tests/gentle-ai.test.ts +288 -65
  155. package/tests/gentle-card-text.ts +2 -1
  156. package/tests/gentle-shell-bin.test.ts +651 -114
  157. package/tests/gentle-shell-launcher.test.ts +99 -52
  158. package/tests/gentle-shell-resume-hint.test.ts +270 -0
  159. package/tests/gentle-shell.test.ts +3839 -187
  160. package/tests/gentle-stats.test.ts +152 -0
  161. package/tests/gentle-theme.test.ts +4 -1
  162. package/tests/gentle-todo.test.ts +80 -8
  163. package/tests/history-atomic-write.test.ts +57 -0
  164. package/tests/history-capture-policy.test.ts +102 -0
  165. package/tests/history-command-registration.test.ts +164 -0
  166. package/tests/history-dedupe-entries.test.ts +123 -0
  167. package/tests/history-delete-backfill.test.ts +190 -0
  168. package/tests/history-delete-confirm.test.ts +460 -0
  169. package/tests/history-dispatch.test.ts +180 -0
  170. package/tests/history-drain-hidden.test.ts +110 -0
  171. package/tests/history-drain-order.test.ts +98 -0
  172. package/tests/history-expanded-globals.test.ts +62 -0
  173. package/tests/history-gc.test.ts +832 -0
  174. package/tests/history-header-layout.test.ts +265 -0
  175. package/tests/history-hide-prompts.test.ts +275 -0
  176. package/tests/history-lazy-windowing.test.ts +508 -0
  177. package/tests/history-legacy-migrate-v2.test.ts +297 -0
  178. package/tests/history-load-shared-history.test.ts +53 -0
  179. package/tests/history-max-results-cap.test.ts +76 -0
  180. package/tests/history-multi-reader.test.ts +203 -0
  181. package/tests/history-off-path.test.ts +170 -0
  182. package/tests/history-openflow-integration.test.ts +173 -0
  183. package/tests/history-overlay-margin.test.ts +326 -0
  184. package/tests/history-preview-layout.test.ts +93 -0
  185. package/tests/history-registry.test.ts +143 -0
  186. package/tests/history-scope-delete.test.ts +411 -0
  187. package/tests/history-search-caret-keys.test.ts +142 -0
  188. package/tests/history-seed-bootstrap.test.ts +170 -0
  189. package/tests/history-seed-regen.test.ts +129 -0
  190. package/tests/history-selector-windowing.test.ts +94 -0
  191. package/tests/history-session-scan-directory.test.ts +87 -0
  192. package/tests/history-session-scan-extract.test.ts +583 -0
  193. package/tests/history-session-writer.test.ts +351 -0
  194. package/tests/history-store-paths.test.ts +79 -0
  195. package/tests/history-tombstone-exact.test.ts +139 -0
  196. package/tests/history-wheel-mouse.test.ts +242 -0
  197. package/tests/inprocess-reviewer.test.ts +29 -19
  198. package/tests/issue-creation-skill.test.ts +61 -0
  199. package/tests/model-routing-authority.test.ts +16 -0
  200. package/tests/nan-provider.test.ts +471 -0
  201. package/tests/native-review-capability-contract.test.ts +7 -1
  202. package/tests/native-review-cli.test.ts +6 -120
  203. package/tests/native-review-parity-runtime.test.ts +100 -3
  204. package/tests/odd-integration.test.ts +67 -0
  205. package/tests/odd-phase-inference.test.ts +213 -0
  206. package/tests/odd-phase-loader.test.ts +253 -0
  207. package/tests/odd-phase.test.ts +307 -0
  208. package/tests/odd-routing-canonical-ratchet.test.ts +11 -6
  209. package/tests/odd-routing-contract.test.ts +86 -35
  210. package/tests/orchestrator-budget.test.ts +14 -39
  211. package/tests/orchestrator-rdd-ownership.test.ts +3 -3
  212. package/tests/overlay-repaint.test.ts +74 -0
  213. package/tests/package-manifest.test.ts +252 -115
  214. package/tests/packed-runner-owned-path.test.ts +46 -0
  215. package/tests/persona-single-channel.test.ts +6 -6
  216. package/tests/provider-defect-handoff.test.ts +3 -11
  217. package/tests/quiet-bash-runtime.test.ts +76 -0
  218. package/tests/quiet-tool-rendering.test.ts +409 -184
  219. package/tests/rdd-aware-verification-contract.test.ts +76 -1
  220. package/tests/rdd-status-line.test.ts +9 -4
  221. package/tests/resume-hint-extension.test.ts +122 -0
  222. package/tests/review-agent-end-preflight.test.ts +176 -12
  223. package/tests/review-candidate-owner-retry.test.ts +22 -1
  224. package/tests/review-candidate-view.test.ts +298 -0
  225. package/tests/review-contract-prompt.test.ts +108 -43
  226. package/tests/review-controller-lock-status.test.ts +0 -1
  227. package/tests/review-controller-native-routing.test.ts +611 -5
  228. package/tests/review-controller-workspace-root.test.ts +163 -4
  229. package/tests/review-host-relay-routing.test.ts +338 -2
  230. package/tests/review-integration-v2-forward.test.ts +200 -0
  231. package/tests/review-ledger-contract.test.ts +10 -34
  232. package/tests/review-reminder-receipt.test.ts +47 -1
  233. package/tests/review-risk-assessment.test.ts +498 -6
  234. package/tests/review-sidebar-state.test.ts +402 -0
  235. package/tests/run-test-suite.test.ts +124 -0
  236. package/tests/runtime-harness.mjs +145 -786
  237. package/tests/runtime-metrics-children.test.ts +16 -23
  238. package/tests/selection-engine.test.ts +421 -0
  239. package/tests/session-messaging-grants.test.ts +255 -0
  240. package/tests/session-worktree-registry.test.ts +77 -0
  241. package/tests/shell-bar.test.ts +382 -1
  242. package/tests/shell-card.test.ts +353 -1
  243. package/tests/shell-changes-view.test.ts +52 -0
  244. package/tests/shell-prompt.test.ts +94 -2
  245. package/tests/shell-sidebar-layout.test.ts +325 -21
  246. package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
  247. package/tests/shell-todo.test.ts +87 -1
  248. package/tests/shell-usage-view.test.ts +27 -0
  249. package/tests/shell-usage.test.ts +73 -0
  250. package/tests/skill-registry.test.ts +50 -1
  251. package/tests/startup-banner.test.ts +130 -2
  252. package/tests/stats-collector.test.ts +195 -0
  253. package/tests/stats-view.test.ts +202 -0
  254. package/tests/telemetry-trigger.test.ts +81 -20
  255. package/tests/theme-customization.test.ts +72 -0
  256. package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
  257. package/tests/vim-editor-adapter.test.ts +804 -0
  258. package/tests/vim-normal-engine.test.ts +101 -0
  259. package/tests/vim-operator-engine.test.ts +215 -0
  260. package/tests/vim-policy.test.ts +19 -0
  261. package/tests/vim-visual-engine.test.ts +52 -0
  262. package/tests/visual-customization-policy.test.ts +110 -0
  263. package/tests/visual-customize-view.test.ts +418 -0
  264. package/tests/visual-profiles.test.ts +87 -0
  265. package/tests/yolo-customize.test.ts +256 -0
  266. package/tests/yolo-mode-runtime.test.ts +161 -0
  267. package/tests/yolo-mode.test.ts +261 -0
  268. package/tests/yolo-session-policy.test.ts +59 -0
  269. package/themes/Gentle.json +2 -1
  270. package/themes/Gentleman-Cute.json +2 -1
  271. package/themes/Gentleman-Sexy.json +2 -1
  272. package/assets/agents/sdd-apply.md +0 -159
  273. package/assets/agents/sdd-archive.md +0 -228
  274. package/assets/agents/sdd-design.md +0 -49
  275. package/assets/agents/sdd-explore.md +0 -48
  276. package/assets/agents/sdd-init.md +0 -56
  277. package/assets/agents/sdd-onboard.md +0 -52
  278. package/assets/agents/sdd-proposal.md +0 -64
  279. package/assets/agents/sdd-remediate.md +0 -37
  280. package/assets/agents/sdd-research.md +0 -49
  281. package/assets/agents/sdd-spec.md +0 -192
  282. package/assets/agents/sdd-status.md +0 -54
  283. package/assets/agents/sdd-tasks.md +0 -108
  284. package/assets/agents/sdd-verify.md +0 -124
  285. package/assets/chains/sdd-full.chain.md +0 -83
  286. package/assets/chains/sdd-plan.chain.md +0 -56
  287. package/assets/chains/sdd-verify.chain.md +0 -43
  288. package/assets/sdd-orchestrator-workflow.md +0 -319
  289. package/assets/support/sdd-status-contract.md +0 -77
  290. package/docs/assets/diagrams/sdd-cycle.svg +0 -14
  291. package/extensions/sdd-init.ts +0 -816
  292. package/lib/openspec-deltas.ts +0 -156
  293. package/lib/sdd-preflight.ts +0 -1066
  294. package/lib/sdd-research-capabilities.ts +0 -94
  295. package/lib/sdd-status.ts +0 -26
  296. package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
  297. package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
  298. package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
  299. package/tests/openspec-deltas.test.ts +0 -209
  300. package/tests/sdd-agent-tools.test.ts +0 -156
  301. package/tests/sdd-archive-replay.test.ts +0 -82
  302. package/tests/sdd-classical-continuation.test.ts +0 -74
  303. package/tests/sdd-execution-routing-contract.test.ts +0 -44
  304. package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
  305. package/tests/sdd-native-managed-uptake.test.ts +0 -243
  306. package/tests/sdd-no-attempts-contract.test.ts +0 -15
  307. package/tests/sdd-odd-integration.test.ts +0 -33
  308. package/tests/sdd-optional-research.test.ts +0 -124
  309. package/tests/sdd-planning-routing-contract.test.ts +0 -45
  310. package/tests/sdd-preflight-rpc-input.test.ts +0 -125
  311. package/tests/sdd-preflight.test.ts +0 -541
  312. package/tests/sdd-research-capabilities.test.ts +0 -114
  313. package/tests/sdd-research-live.test.ts +0 -241
  314. package/tests/sdd-selection-transport.test.ts +0 -653
  315. package/tests/sdd-status.test.ts +0 -9
  316. package/tests/sdd-task-truth.test.ts +0 -43
@@ -45,7 +45,7 @@ test("research uses adaptive evidence gathering and existing general workers onl
45
45
  "recommendation, tradeoffs, open questions, and implementation implications",
46
46
  "Forward these research instructions",
47
47
  "existing fresh general exploration/research worker",
48
- "do not create a specialized agent or invoke `sdd-research`",
48
+ "do not create a specialized agent or create a new workflow",
49
49
  "no new persistence or readiness machinery",
50
50
  ]);
51
51
  });
@@ -129,7 +129,7 @@ test("ODD closes each task with a work-unit commit and reviews the commit or PR
129
129
  "keep a running count from work-unit commits",
130
130
  "`ask-on-risk` (default), `auto-chain`, `single-pr`, or `exception-ok`",
131
131
  "apply the chosen strategy before the next commit",
132
- "`ask-on-risk` asks once for the chain strategy, `stacked-to-main` or `feature-branch-chain`",
132
+ "`ask-on-risk` asks once using the ordered oversized-delivery menu",
133
133
  "`auto-chain` asks only for a missing chain strategy and slices automatically",
134
134
  "Cache both choices, and record slice boundaries",
135
135
  "Resolve the `work-unit-commits` and `chained-pr` skills by registry name",
@@ -145,10 +145,38 @@ test("ODD closes each task with a work-unit commit and reviews the commit or PR
145
145
  "Close task with a work-unit commit",
146
146
  "RDD enabled at work-unit commit boundary",
147
147
  "Running authored lines over 400",
148
- "Apply delivery strategy: chained PR slice",
148
+ "Apply selected delivery strategy",
149
+ "Single PR; no chain artifacts",
149
150
  ]);
150
151
  });
151
152
 
153
+ test("delivery instructions and docs preserve localized ordered outcomes and override chaining", () => {
154
+ for (const text of [delegation, read("docs/readme-reference.md")]) {
155
+ // English prose is documentation evidence, not mandatory runtime UI copy.
156
+ const outcomes = ["chain_strategy=feature-branch-chain", "chain_strategy=stacked-to-main", "delivery_strategy=single-pr"];
157
+ let previous = -1;
158
+ for (const outcome of outcomes) {
159
+ const index = text.indexOf(outcome);
160
+ assert.ok(index > previous, `missing or out-of-order semantic outcome: ${outcome}`);
161
+ previous = index;
162
+ }
163
+ assert.match(text, /1\. .*feature\/tracker.*feature-branch-chain/i);
164
+ assert.match(text, /2\. .*verified default\/main.*stacked-to-main/i);
165
+ assert.match(text, /3\. .*single PR.*least recommended.*delivery_strategy=single-pr/i);
166
+ containsAll(text, [
167
+ "delivery_strategy=single-pr", "not a `chain_strategy` token",
168
+ "overrides the pending chaining path", "clear the chain choice",
169
+ "suppress later chain prompts", "not automatically select `exception-ok`",
170
+ "destination repository's documented contribution/size policy",
171
+ "not a universal label requirement", "no tracker, child dependency diagram, or Chain Context",
172
+ "complete user-facing question", "every option label, description, and recommendation marker",
173
+ "active user's conversation language", "unchanged and untranslated",
174
+ "English examples are illustrative and localizable, not mandatory copy",
175
+ ]);
176
+ assert.doesNotMatch(text, /single-pr.*requires? `size:exception`/i);
177
+ }
178
+ });
179
+
152
180
  test("user documentation shows recovery and candidate-level consent without claiming model proof", () => {
153
181
  const docs = read("docs/readme-reference.md");
154
182
  containsAll(docs, [
@@ -184,24 +212,26 @@ test("one feature document carries intent, accepted rationale and worker context
184
212
  ]);
185
213
  });
186
214
 
187
- test("ODD forwards configured TDD without equating test presence with enablement", () => {
188
- containsAll(delegation, [
189
- "Resolve effective TDD on/off from existing project/session configuration or explicit user choice",
190
- "retain its source and exact test runner",
191
- "Record resolved mode, source, and runner in the feature document when present",
192
- "Tests or frameworks being present does not enable TDD",
193
- "Forward mode, source, and runner on every implementation delegation; refresh on resume",
194
- "When enabled, require observed RED before implementation, GREEN, then REFACTOR",
195
- "When disabled, run ordinary functional checks, not no checks",
196
- "If mode is unknown/conflicting or the runner is missing",
197
- "resolve only the ambiguity affecting the next action",
198
- "never invent precedence or a command, and never invoke sdd-init to determine ODD TDD",
199
- ]);
200
- containsAll(read("assets/agents/gentle-ai-worker.md"), [
201
- "Consume the parent's effective TDD mode, configuration/choice source, and exact runner",
202
- "Missing or conflicting mode/source/runner is not disabled TDD",
203
- ]);
204
- assert.doesNotMatch(wrapper, /If tests exist, use strict TDD/);
215
+ test("ODD defaults to applicable test-first without chat or TUI activation", () => {
216
+ const worker = read("assets/agents/gentle-ai-worker.md");
217
+ const verify = read("assets/agents/gentle-ai-verify.md");
218
+ const support = read("assets/support/strict-tdd.md") + read("assets/support/strict-tdd-verify.md");
219
+ const skill = read("skills/gentle-ai/SKILL.md");
220
+ for (const text of [core, delegation, worker, verify, support, skill, wrapper]) {
221
+ containsAll(text, ["applicable", "RED", "GREEN"]);
222
+ assert.doesNotMatch(text, /(?:configured TDD mode|Strict TDD Mode is enabled|explicit user choice|test presence does not enable it|tests existing does not activate it)/i);
223
+ }
224
+ for (const [actor, text] of [["core", core], ["delegation", delegation], ["extension", wrapper], ["skill", skill]] as const) {
225
+ assert.match(text, /behavior changes with applicable runnable deterministic tests and a clear expected outcome/i, `${actor} must require applicability, not test presence`);
226
+ assert.match(text, /passive documentation/i, `${actor} must handle passive docs`);
227
+ assert.match(text, /unavailable runner/i, `${actor} must handle unavailable runners`);
228
+ assert.match(text, /ordinary functional or structural verification/i, `${actor} must specify fallback checks`);
229
+ }
230
+ containsAll(delegation, ["by default", "Forward this policy"]);
231
+ containsAll(worker, ["before implementation", "ordinary functional or structural verification", "no meaningful RED"]);
232
+ containsAll(verify, ["observed RED", "observed GREEN", "exception"]);
233
+ assert.doesNotMatch(skill, /SDD|OpenSpec/i, "the ODD-only skill must not prime SDD");
234
+ assert.doesNotMatch(core + delegation + worker + support + skill + wrapper, /If tests exist, use strict TDD/i);
205
235
  });
206
236
 
207
237
  test("mandatory delegation triggers are behavioral in the lazy canonical port and the always-on ODD step", () => {
@@ -212,12 +242,12 @@ test("mandatory delegation triggers are behavioral in the lazy canonical port an
212
242
  "**Mapping trigger",
213
243
  "**Writer trigger",
214
244
  "**Preparation trigger:**",
215
- "**Long-session backstop",
245
+ "**Context backstop:**",
216
246
  "pause and delegate the next bounded unit of work",
217
247
  "**Route declaration:**",
218
248
  "record the chosen route per task",
219
249
  "so skipped delegation is observable instead of silent",
220
- "These triggers never select SDD and never create SDD artifacts",
250
+ "These triggers only choose between direct inline and delegated direct inside ODD",
221
251
  ]) {
222
252
  assert.ok(delegation.includes(clause), `lazy canonical port is missing mandatory delegation clause: ${clause}`);
223
253
  }
@@ -233,19 +263,19 @@ test("mandatory delegation triggers are behavioral in the lazy canonical port an
233
263
 
234
264
  test("core and lazy canonical trigger lists agree in numbering and semantics", () => {
235
265
  for (const entry of [
236
- "1. **4-file rule**",
266
+ "1. **Evidence-budget rule**",
237
267
  "2. **Multi-file write rule**",
238
268
  "3. **Incident rule**",
239
- "4. **Long-session rule**",
269
+ "4. **Context backstop**",
240
270
  "5. **Verification rule**",
241
271
  ]) {
242
272
  assert.ok(core.includes(entry), `always-on core trigger list is missing: ${entry}`);
243
273
  }
244
274
  for (const entry of [
245
- "1. **Mapping trigger (4-file rule):**",
275
+ "1. **Mapping trigger (Evidence-budget rule):**",
246
276
  "2. **Writer trigger (Multi-file write rule):**",
247
277
  "3. **Incident rule:**",
248
- "4. **Long-session backstop (Long-session rule):**",
278
+ "4. **Context backstop:**",
249
279
  "5. **Verification rule**",
250
280
  ]) {
251
281
  assert.ok(delegation.includes(entry), `lazy canonical trigger list is missing: ${entry}`);
@@ -255,17 +285,41 @@ test("core and lazy canonical trigger lists agree in numbering and semantics", (
255
285
  "**Write rule**",
256
286
  "**Context rule**",
257
287
  "**Per-action rule**",
258
- "**Optional SDD rule**",
259
288
  ]) {
260
289
  assert.ok(!delegation.includes(stale), `reconciled canonical list retains stale trigger framing: ${stale}`);
261
290
  }
262
291
  });
263
292
 
293
+ // Evidence-budget rule (gentle-shell#1587, measured in gentle-ai#5139): every
294
+ // routing surface states the same numbers, and the retired file-count and
295
+ // tool-call triggers are gone from all of them.
296
+ test("evidence-budget numbers agree across routing surfaces and retired triggers are gone", () => {
297
+ const surfaces: Record<string, string> = {
298
+ "assets/orchestrator.md": core,
299
+ "assets/orchestrator-delegation.md": delegation,
300
+ "skills/gentle-ai/SKILL.md": read("skills/gentle-ai/SKILL.md"),
301
+ };
302
+ for (const [path, text] of Object.entries(surfaces)) {
303
+ containsAll(text, [
304
+ "**Evidence-budget rule**",
305
+ "at most 3 calls",
306
+ "~10k tokens",
307
+ "at most ~2k tokens",
308
+ "`path:line`",
309
+ "~150k tokens",
310
+ "Context backstop",
311
+ ]);
312
+ assert.doesNotMatch(text, /4-file rule|20 tool calls|5 exploratory (?:file )?reads/, `${path} keeps a retired trigger`);
313
+ }
314
+ for (const agent of ["assets/agents/gentle-ai-explore.md", "assets/agents/gentle-ai-verify.md"]) {
315
+ containsAll(read(agent), ["at most ~2k tokens", "`path:line`"]);
316
+ }
317
+ });
318
+
264
319
  test("ODD protocol is always-on in the rendered system prompt and runs by default", () => {
265
320
  const orderedClauses = [
266
321
  "Default workflow: Organic Driven Development (MANDATORY)",
267
322
  "predefined workflow of this orchestrator",
268
- "SDD is a branch inside ODD",
269
323
  "Never describe this workflow only when asked about it: run it.",
270
324
  "1. **Authorize.**",
271
325
  "2. **Explore.**",
@@ -277,6 +331,7 @@ test("ODD protocol is always-on in the rendered system prompt and runs by defaul
277
331
  "Tell the user in one line which feature document was created and how many tasks it holds",
278
332
  "6. **Implement task by task.**",
279
333
  "7. **Close.**",
334
+ "call `gentle_odd_phase` only when the primary session's ODD phase actually changes",
280
335
  "Harness principles:",
281
336
  "# el Gentleman Orchestrator",
282
337
  ];
@@ -298,15 +353,11 @@ test("ODD protocol is always-on in the rendered system prompt and runs by defaul
298
353
  wrapper.includes("Organic Driven Development (ODD) is the predefined workflow for every request"),
299
354
  "missing contract: extensions/gentle-ai.ts harness principle",
300
355
  );
301
- assert.ok(
302
- wrapper.includes(
303
- "I run Organic Driven Development by default and SDD/OpenSpec when explicitly selected",
304
- ),
305
- "missing contract: extensions/gentle-ai.ts identity sentence",
306
- );
356
+ // The parent owns the injected identity; its migration is outside this suite's edit surface.
307
357
  assert.ok(
308
358
  core.includes("ODD (Default Workflow, harness section above) is mandatory on every request"),
309
359
  "missing contract: assets/orchestrator.md pointer sentence",
310
360
  );
311
361
  containsAll(core, ["orchestrator-delegation.md", "orchestrator-memory.md"]);
362
+ assert.doesNotMatch(core + delegation + memory, /SDD|sdd-|OpenSpec|openspec/i);
312
363
  });
@@ -35,7 +35,6 @@ const MIN_CONTROLLED_LONG_ASSETS_ROOT_CHARS = 93;
35
35
 
36
36
  const LAZY_ASSET_NAMES = [
37
37
  "orchestrator.md",
38
- "sdd-orchestrator-workflow.md",
39
38
  "orchestrator-delegation.md",
40
39
  "orchestrator-memory.md",
41
40
  "orchestrator-skills.md",
@@ -193,16 +192,17 @@ const TARGET_FILE: Record<Target, string> = {
193
192
  // Line numbers below are 1-indexed against tests/fixtures/orchestrator.pre-diet.md
194
193
  // (frozen byte-identical copy of assets/orchestrator.md at 23,047 B / 312 lines).
195
194
  const DISPOSITION_MAP: DispositionRange[] = [
196
- { lines: [1, 4], target: "core", label: "Header + bind" },
195
+ { lines: [1, 4], target: "replaced", label: "Header + bind: retired phase qualification" },
197
196
  { lines: [5, 8], target: "core", label: "Identity Contract" },
198
197
  { lines: [9, 13], target: "core", label: "Core Role" },
199
198
  { lines: [15, 15], target: "core", label: "Language Boundary heading" },
200
199
  { lines: [17, 17], target: "core", label: "Language Boundary LB1 pointer" },
201
200
  { lines: [19, 19], target: "delegation", label: "Language Boundary LB2 (subagent-English)" },
202
- { lines: [21, 21], target: "core", label: "Language Boundary LB3 (artifact language)" },
201
+ { lines: [21, 21], target: "replaced", label: "Language Boundary LB3: retired artifact types" },
203
202
  { lines: [23, 23], target: "core", label: "Language Boundary LB4 (public comment language)" },
204
- { lines: [25, 29], target: "delegation", label: "Language Boundary LB5 (exceptions)" },
205
- { lines: [31, 40], target: "core", label: "Mental Model" },
203
+ { lines: [25, 28], target: "delegation", label: "Language Boundary LB5 (exceptions)" },
204
+ { lines: [29, 29], target: "obsolete", label: "Retired artifact exception" },
205
+ { lines: [31, 40], target: "replaced", label: "Mental Model: ODD-only" },
206
206
  { lines: [42, 42], target: "core", label: "Work Routing Ladder heading" },
207
207
  {
208
208
  lines: [44, 97],
@@ -251,7 +251,7 @@ const DISPOSITION_MAP: DispositionRange[] = [
251
251
  target: "obsolete",
252
252
  label: "Parent-selected review lens table replaced by native RAR lens ownership (#312)",
253
253
  },
254
- { lines: [183, 191], target: "core", label: "SDD Workflow pointer" },
254
+ { lines: [183, 191], target: "obsolete", label: "Retired workflow pointer" },
255
255
  { lines: [193, 193], target: "core", label: "Memory Contract heading" },
256
256
  {
257
257
  lines: [195, 195],
@@ -263,9 +263,9 @@ const DISPOSITION_MAP: DispositionRange[] = [
263
263
  target: "replaced",
264
264
  label: "Verbose non-SDD memory forwarding replaced by compact ownership (#3417)",
265
265
  },
266
- { lines: [203, 230], target: "memory", label: "Memory Contract SDD phases table + artifact keys + lifecycle rule" },
266
+ { lines: [203, 230], target: "replaced", label: "Retired phase keys; memory lifecycle remains" },
267
267
  { lines: [232, 232], target: "core", label: "Skill Registry Protocol heading" },
268
- { lines: [234, 253], target: "skills", label: "Skill Registry Protocol detail" },
268
+ { lines: [234, 253], target: "replaced", label: "Skill resolution retained without phase roles" },
269
269
  { lines: [255, 255], target: "core", label: "Intent-Driven Skill Discovery heading" },
270
270
  { lines: [257, 276], target: "skills", label: "Intent-Driven Skill Discovery detail" },
271
271
  { lines: [278, 283], target: "core", label: "Safety" },
@@ -284,8 +284,6 @@ const fixtureLines = readFileSync(FIXTURE_PATH, "utf8").split("\n");
284
284
  // Fixture line 36 is superseded by ODD (#1035); 187 and 191 predate
285
285
  // root-relative lazy assets and canonical-authority resolution. Keep coverage by asserting the
286
286
  // intentionally updated production wording instead of weakening the range.
287
- const CURRENT_SDD_WORKFLOW_PATH = "`sdd-orchestrator-workflow.md`";
288
- const CURRENT_HARD_PREFLIGHT_INVARIANT = "Hard preflight invariant: `openspec/config.yaml`, existing SDD changes, installed `.pi`/global SDD assets, or a todo named \"preflight\" are not session preflight. Do not mark SDD preflight complete, start `sdd-init`, launch SDD subagents/chains, or move to explore/proposal/spec/design/tasks until this session has an injected `## SDD Session Preflight` block or a canonical-authority resolution. Defaults and capability constraints may resolve fields without confirmation prompts; preserve unresolved-choice and safety gates.";
289
287
  const SUPERSEDED_LIFECYCLE_REVIEW_LINES = new Set([
290
288
  70,
291
289
  // 74/77: the loose mode-choice background lines were replaced by the
@@ -321,25 +319,7 @@ for (const range of DISPOSITION_MAP) {
321
319
  const trimmed = raw.trim();
322
320
  const expected =
323
321
  ln === 36 ? "- Substantial authorized work: use ODD; track feature progress automatically." :
324
- // #1051 keeps the selected store when memory is unavailable.
325
- ln === 221 ? "do not switch the selected store" :
326
- // Research returns findings; other phases keep direct backend ownership.
327
- ln === 205 ? trimmed.replace("Each SDD phase", "Except for output-only `sdd-research`, each SDD phase") :
328
- ln === 185 ? trimmed.replace("apply/verify/sync/archive", "apply/verify/archive") :
329
- ln === 187
330
- ? CURRENT_SDD_WORKFLOW_PATH
331
- : ln === 191
332
- ? CURRENT_HARD_PREFLIGHT_INVARIANT
333
- : trimmed;
334
- // #1051 retires the standalone sync row and its artifact key, not
335
- // the surrounding memory/recovery contract or historical fixture.
336
- if (ln === 216 || ln === 220) {
337
- assert.ok(!targetContent.includes(trimmed), `retired sync contract remains at fixture:${ln}`);
338
- assert.match(targetContent, /sdd\/<change>\/archive-report/);
339
- assert.match(targetContent, /sdd\/<change>\/verify-report/);
340
- assert.doesNotMatch(targetContent, /sdd\/<change>\/sync-report|\| `sdd-sync`/);
341
- continue;
342
- }
322
+ trimmed;
343
323
  if (SUPERSEDED_LIFECYCLE_REVIEW_LINES.has(ln)) {
344
324
  assert.ok(
345
325
  !targetContent.includes(trimmed),
@@ -370,11 +350,11 @@ for (const range of DISPOSITION_MAP) {
370
350
 
371
351
  test("core-alone: load-bearing direct-delegation tokens remain without lazy union", () => {
372
352
  const core = readRealAsset("orchestrator.md");
373
- assert.match(core, /4-file rule/);
353
+ assert.match(core, /Evidence-budget rule/);
374
354
  assert.match(core, /Multi-file write rule/);
375
355
  assert.match(core, /Incident rule/);
376
356
  assert.match(core, /Verification rule/);
377
- assert.match(core, /Long-session rule/);
357
+ assert.match(core, /Context backstop/);
378
358
  });
379
359
 
380
360
  test("core-alone: dynamic Gentle AI ownership replaces package lifecycle instructions", () => {
@@ -420,7 +400,7 @@ test("relocated lazy bodies are not double-delivered in the always-on core", ()
420
400
  );
421
401
  assert.doesNotMatch(
422
402
  rendered,
423
- /### SDD phases/,
403
+ /### Organic feature continuity/,
424
404
  "memory-only body leaked into the always-on core",
425
405
  );
426
406
  assert.doesNotMatch(
@@ -472,7 +452,7 @@ test("every compressed lazy-file pointer in the core still names the material it
472
452
  },
473
453
  {
474
454
  file: "orchestrator-memory.md",
475
- mustName: ["Phase table", "artifact keys"],
455
+ mustName: ["ODD task continuity", "memory lifecycle"],
476
456
  },
477
457
  {
478
458
  file: "orchestrator-skills.md",
@@ -497,10 +477,5 @@ test("every compressed lazy-file pointer in the core still names the material it
497
477
  );
498
478
  }
499
479
  }
500
- // The SDD workflow pointer was never compressed to a bare filename; its
501
- // surrounding paragraphs already name the material at length.
502
- assert.match(
503
- core,
504
- /SDD phases, native dispatcher rules, status contract, preflight\/init guards, artifact-store policy, execution mode, Strict TDD forwarding, phase result contract, and review workload guard/,
505
- );
480
+ assert.doesNotMatch(core, /SDD|sdd-|OpenSpec|openspec/i);
506
481
  });
@@ -72,18 +72,18 @@ test("rendered parent prompt keeps the RDD boundary while omitting lifecycle mir
72
72
  assert.ok(Buffer.byteLength(rendered, "utf8") <= 8192, "the rendered parent prompt must stay below the reduced 8 KiB budget");
73
73
  });
74
74
 
75
- test("static prompts retain normal SDD and delegated-work guidance", () => {
76
- for (const heading of ["## SDD Workflow (lazy-loaded)", "## Memory Contract"]) {
75
+ test("static prompts retain ODD and delegated-work guidance without SDD", () => {
76
+ for (const heading of ["## Memory Contract", "## Work Routing Ladder"]) {
77
77
  assert.ok(core.includes(heading), `core lost ${heading}`);
78
78
  }
79
79
  for (const heading of [
80
80
  "### Delegation Rules",
81
81
  "#### Background Subagent Policy",
82
82
  "#### Allowed edit surfaces (MANDATORY)",
83
- "### 3. SDD (optional)",
84
83
  ]) {
85
84
  assert.ok(delegation.includes(heading), `delegation lost ${heading}`);
86
85
  }
86
+ assert.doesNotMatch(staticPrompts, /SDD|sdd-|OpenSpec|openspec/i);
87
87
  });
88
88
 
89
89
  test("always-on parent prompt requires a narrow writer edit surface before launch", () => {
@@ -0,0 +1,74 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { withOverlayRepaint } from "../lib/overlay-repaint.ts";
4
+ import type { TUI } from "@earendil-works/pi-tui";
5
+
6
+ // Overlay close repaint: wrapping an overlay's done callback must tear the
7
+ // overlay down first (done), then force a FULL terminal repaint
8
+ // (invalidate + requestRender) so a fullscreen overlay's stale cell state
9
+ // cannot survive the close. Paint failures are swallowed; done failures are not.
10
+
11
+ function fakeTui() {
12
+ const calls: string[] = [];
13
+ const tui = {
14
+ invalidate: () => {
15
+ calls.push("invalidate");
16
+ },
17
+ requestRender: () => {
18
+ calls.push("requestRender");
19
+ },
20
+ } as unknown as TUI;
21
+ return { tui, calls };
22
+ }
23
+
24
+ test("withOverlayRepaint: forces a full render (requestRender(true)) after done", () => {
25
+ const calls: string[] = [];
26
+ const tui = {
27
+ requestRender: (force?: boolean) => {
28
+ calls.push(`requestRender:${force === true}`);
29
+ },
30
+ } as unknown as TUI;
31
+ const close = withOverlayRepaint<string | null>(tui, (result) => {
32
+ calls.push(`done:${String(result)}`);
33
+ });
34
+ close(null);
35
+ assert.deepEqual(calls, ["done:null", "requestRender:true"]);
36
+ });
37
+
38
+ test("withOverlayRepaint: forwards null results (plain close)", () => {
39
+ const calls: string[] = [];
40
+ const tui = {
41
+ requestRender: (force?: boolean) => {
42
+ calls.push(`requestRender:${force === true}`);
43
+ },
44
+ } as unknown as TUI;
45
+ const seen: Array<string | null> = [];
46
+ const close = withOverlayRepaint<string | null>(tui, (result) => {
47
+ seen.push(result);
48
+ });
49
+ close(null);
50
+ assert.deepEqual(seen, [null]);
51
+ assert.deepEqual(calls, ["requestRender:true"]);
52
+ });
53
+
54
+ test("withOverlayRepaint: paint failure is swallowed, done result still delivered", () => {
55
+ const seen: Array<string | null> = [];
56
+ const tui = {
57
+ requestRender: () => {
58
+ throw new Error("EIO");
59
+ },
60
+ } as unknown as TUI;
61
+ const close = withOverlayRepaint<string | null>(tui, (result) => {
62
+ seen.push(result);
63
+ });
64
+ assert.doesNotThrow(() => close(null));
65
+ assert.deepEqual(seen, [null]);
66
+ });
67
+
68
+ test("withOverlayRepaint: done failure propagates (overlay contract, not paint)", () => {
69
+ const { tui } = fakeTui();
70
+ const close = withOverlayRepaint<string | null>(tui, () => {
71
+ throw new Error("overlay teardown failed");
72
+ });
73
+ assert.throws(() => close(null), /overlay teardown failed/);
74
+ });