geneseed 2.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 (337) hide show
  1. package/CHANGELOG.md +938 -0
  2. package/DESIGN.md +1423 -0
  3. package/LICENSE +21 -0
  4. package/QUICKSTART.md +48 -0
  5. package/README.md +336 -0
  6. package/SETUP.md +1027 -0
  7. package/SHIPPED.md +70 -0
  8. package/adapters/bob/README.md +100 -0
  9. package/adapters/claude-code/README.md +101 -0
  10. package/adapters/claude-code/settings.json +47 -0
  11. package/adapters/claude-code/skills/geneseed/SKILL.md +72 -0
  12. package/adapters/copilot/README.md +76 -0
  13. package/adapters/opencode/GLOBAL-HARNESS-SPEC.md +434 -0
  14. package/adapters/opencode/HOW-OPENCODE-LOADS.md +158 -0
  15. package/adapters/opencode/README.md +582 -0
  16. package/adapters/opencode/agents/orchestrator.md +25 -0
  17. package/adapters/opencode/opencode.json +44 -0
  18. package/adapters/opencode/plugins/geneseed-activity.js +430 -0
  19. package/adapters/opencode/plugins/geneseed-context.js +876 -0
  20. package/adapters/opencode/plugins/geneseed-guard.js +290 -0
  21. package/adapters/opencode/plugins/geneseed-learn.js +396 -0
  22. package/adapters/opencode/plugins/geneseed-notify.js +142 -0
  23. package/adapters/opencode/plugins/geneseed-ponytail.js +148 -0
  24. package/adapters/opencode/plugins/geneseed-workflow.js +183 -0
  25. package/adapters/opencode/workflows/_runtime.js +277 -0
  26. package/adapters/opencode/workflows/council.js +64 -0
  27. package/adapters/opencode/workflows/dispatch.js +89 -0
  28. package/adapters/opencode/workflows/research-plan-implement.js +43 -0
  29. package/adapters/opencode/workflows/review.js +57 -0
  30. package/bin/geneseed-cli.mjs +412 -0
  31. package/bin/geneseed-hook.mjs +129 -0
  32. package/bin/geneseed.mjs +1086 -0
  33. package/docs/declined.md +149 -0
  34. package/docs/limits.md +270 -0
  35. package/docs/opencode-plugin-setup.md +64 -0
  36. package/docs/token-footprint.md +68 -0
  37. package/docs/web/_groups.json +45 -0
  38. package/docs/web/about.md +6 -0
  39. package/docs/web/adapters-claude-code.md +8 -0
  40. package/docs/web/adapters-opencode-loads.md +8 -0
  41. package/docs/web/adapters-opencode-spec.md +8 -0
  42. package/docs/web/adapters-opencode.md +8 -0
  43. package/docs/web/agents.md +8 -0
  44. package/docs/web/authoring.md +11 -0
  45. package/docs/web/autostart.md +9 -0
  46. package/docs/web/claude-hooks.md +36 -0
  47. package/docs/web/cli.md +6 -0
  48. package/docs/web/collaboration.md +27 -0
  49. package/docs/web/configure-wiki.md +9 -0
  50. package/docs/web/design.md +7 -0
  51. package/docs/web/env-knobs.md +9 -0
  52. package/docs/web/exclusions.md +26 -0
  53. package/docs/web/first-session.md +28 -0
  54. package/docs/web/footprint.md +31 -0
  55. package/docs/web/foreman-mode.md +33 -0
  56. package/docs/web/glossary.md +6 -0
  57. package/docs/web/harnesses.md +28 -0
  58. package/docs/web/headless.md +9 -0
  59. package/docs/web/ingest-docs.md +9 -0
  60. package/docs/web/install-paths.md +9 -0
  61. package/docs/web/install-quick.md +35 -0
  62. package/docs/web/lsp-overview.md +37 -0
  63. package/docs/web/mcp-claude-code.md +10 -0
  64. package/docs/web/mcp-filesystem.md +9 -0
  65. package/docs/web/mcp-gitlab.md +9 -0
  66. package/docs/web/mcp-markitdown.md +9 -0
  67. package/docs/web/mcp-overview.md +34 -0
  68. package/docs/web/mcp-trouble.md +9 -0
  69. package/docs/web/mcp-verify.md +9 -0
  70. package/docs/web/memory.md +7 -0
  71. package/docs/web/migrate.md +39 -0
  72. package/docs/web/model.md +20 -0
  73. package/docs/web/notebook.md +7 -0
  74. package/docs/web/pipelines.md +24 -0
  75. package/docs/web/plugin-activity.md +23 -0
  76. package/docs/web/plugin-context.md +26 -0
  77. package/docs/web/plugin-guard.md +24 -0
  78. package/docs/web/plugin-learn.md +27 -0
  79. package/docs/web/plugin-notify.md +24 -0
  80. package/docs/web/plugin-ponytail.md +23 -0
  81. package/docs/web/plugin-setup.md +7 -0
  82. package/docs/web/plugin-workflow.md +24 -0
  83. package/docs/web/plugins.md +17 -0
  84. package/docs/web/project-context.md +9 -0
  85. package/docs/web/rules.md +8 -0
  86. package/docs/web/run-anywhere.md +9 -0
  87. package/docs/web/self-improve.md +8 -0
  88. package/docs/web/setup-choices.md +39 -0
  89. package/docs/web/skills.md +8 -0
  90. package/docs/web/themes-author.md +8 -0
  91. package/docs/web/themes.md +9 -0
  92. package/docs/web/token-footprint.md +7 -0
  93. package/docs/web/trouble.md +38 -0
  94. package/docs/web/upgrade.md +9 -0
  95. package/docs/web/verify.md +30 -0
  96. package/docs/web/wiki.md +7 -0
  97. package/docs/web/worktree.md +10 -0
  98. package/docs/web-ui.md +144 -0
  99. package/docs/wiki.md +43 -0
  100. package/geneseed +39 -0
  101. package/geneseed.cmd +34 -0
  102. package/harness.config.json +6 -0
  103. package/js/anim.mjs +326 -0
  104. package/js/catalog.mjs +112 -0
  105. package/js/checkout.mjs +88 -0
  106. package/js/cli-table.json +1034 -0
  107. package/js/cli.mjs +498 -0
  108. package/js/diff.mjs +384 -0
  109. package/js/doctor.mjs +1364 -0
  110. package/js/emit.mjs +1373 -0
  111. package/js/excludes.mjs +271 -0
  112. package/js/generate.mjs +379 -0
  113. package/js/hooks.mjs +1026 -0
  114. package/js/hosts.mjs +253 -0
  115. package/js/installs.mjs +378 -0
  116. package/js/inventory.mjs +175 -0
  117. package/js/lib/fs.mjs +913 -0
  118. package/js/lib/proc.mjs +28 -0
  119. package/js/lib/udiff.mjs +275 -0
  120. package/js/link.mjs +286 -0
  121. package/js/mcp.mjs +335 -0
  122. package/js/memory.mjs +108 -0
  123. package/js/menu.mjs +118 -0
  124. package/js/migrate.mjs +251 -0
  125. package/js/native.mjs +426 -0
  126. package/js/opencode.mjs +325 -0
  127. package/js/registry.mjs +110 -0
  128. package/js/render.mjs +282 -0
  129. package/js/settings.mjs +924 -0
  130. package/js/setup.mjs +492 -0
  131. package/js/status.mjs +281 -0
  132. package/js/themes.mjs +195 -0
  133. package/js/tui.mjs +444 -0
  134. package/js/uninstall.mjs +1137 -0
  135. package/js/update.mjs +910 -0
  136. package/js/web/actions.mjs +868 -0
  137. package/js/web/activity.mjs +284 -0
  138. package/js/web/api.mjs +873 -0
  139. package/js/web/docs.mjs +531 -0
  140. package/js/web/graph.mjs +0 -0
  141. package/js/web/jobs.mjs +480 -0
  142. package/js/web/server.mjs +1135 -0
  143. package/package.json +54 -0
  144. package/registry.json +474 -0
  145. package/src/AGENT.md.tmpl +390 -0
  146. package/src/agents/_template.md +76 -0
  147. package/src/agents/advocate.md +36 -0
  148. package/src/agents/architect.md +55 -0
  149. package/src/agents/developer.md +66 -0
  150. package/src/agents/docs.md +54 -0
  151. package/src/agents/empiricist.md +39 -0
  152. package/src/agents/explorer.md +54 -0
  153. package/src/agents/framer.md +36 -0
  154. package/src/agents/historian.md +51 -0
  155. package/src/agents/operator.md +37 -0
  156. package/src/agents/pragmatist.md +37 -0
  157. package/src/agents/reviewer.md +52 -0
  158. package/src/agents/security.md +57 -0
  159. package/src/agents/skeptic.md +48 -0
  160. package/src/agents/steward.md +36 -0
  161. package/src/agents/tester.md +54 -0
  162. package/src/agents/user-advocate.md +36 -0
  163. package/src/agents/visionary.md +36 -0
  164. package/src/laws/universal.md +387 -0
  165. package/src/memory/README.md +109 -0
  166. package/src/memory/gitignore +6 -0
  167. package/src/modes/README.md +26 -0
  168. package/src/modes/direct.md +2 -0
  169. package/src/modes/foreman.md +19 -0
  170. package/src/notebook/README.md +39 -0
  171. package/src/notebook/gitignore +7 -0
  172. package/src/postures/README.md +31 -0
  173. package/src/postures/artisan.md +14 -0
  174. package/src/postures/assistant.md +12 -0
  175. package/src/postures/expert.md +11 -0
  176. package/src/postures/mentor.md +11 -0
  177. package/src/postures/peer.md +12 -0
  178. package/src/skills/_template.md +47 -0
  179. package/src/skills/brainstorm.md +28 -0
  180. package/src/skills/clarify.md +28 -0
  181. package/src/skills/codebase-design.md +44 -0
  182. package/src/skills/commit.md +48 -0
  183. package/src/skills/council.md +34 -0
  184. package/src/skills/crash-course.md +28 -0
  185. package/src/skills/daydream/.claude-plugin/plugin.json +9 -0
  186. package/src/skills/daydream/SKILL.md +74 -0
  187. package/src/skills/daydream/VENDOR.md +25 -0
  188. package/src/skills/daydream/critic-prompt.md +38 -0
  189. package/src/skills/daydream/instructions.md +422 -0
  190. package/src/skills/daydream/synthesizer-prompt.md +41 -0
  191. package/src/skills/debug.md +37 -0
  192. package/src/skills/decode.md +28 -0
  193. package/src/skills/develop.md +29 -0
  194. package/src/skills/document-project.md +79 -0
  195. package/src/skills/domain-modeling.md +38 -0
  196. package/src/skills/drill.md +28 -0
  197. package/src/skills/feynman.md +26 -0
  198. package/src/skills/forge-mcp.md +48 -0
  199. package/src/skills/fresh-eyes.md +29 -0
  200. package/src/skills/frontend-design.md +58 -0
  201. package/src/skills/gap-detector.md +27 -0
  202. package/src/skills/geneseed-code-review.md +33 -0
  203. package/src/skills/geneseed.md +29 -0
  204. package/src/skills/git-archaeology.md +47 -0
  205. package/src/skills/git-rescue.md +68 -0
  206. package/src/skills/handoff.md +29 -0
  207. package/src/skills/herdr.md +164 -0
  208. package/src/skills/ingest.md +49 -0
  209. package/src/skills/learning-path.md +26 -0
  210. package/src/skills/migrate.md +45 -0
  211. package/src/skills/opencode-theme.md +52 -0
  212. package/src/skills/parallel-agents.md +28 -0
  213. package/src/skills/pipeline.md +52 -0
  214. package/src/skills/plan.md +45 -0
  215. package/src/skills/ponytail.md +29 -0
  216. package/src/skills/profile.md +58 -0
  217. package/src/skills/prose.md +42 -0
  218. package/src/skills/prototype.md +44 -0
  219. package/src/skills/react-view-transitions/AGENTS.md +955 -0
  220. package/src/skills/react-view-transitions/README.md +42 -0
  221. package/src/skills/react-view-transitions/SKILL.md +320 -0
  222. package/src/skills/react-view-transitions/VENDOR.md +16 -0
  223. package/src/skills/react-view-transitions/metadata.json +12 -0
  224. package/src/skills/react-view-transitions/references/css-recipes.md +242 -0
  225. package/src/skills/react-view-transitions/references/implementation.md +182 -0
  226. package/src/skills/react-view-transitions/references/nextjs.md +176 -0
  227. package/src/skills/react-view-transitions/references/patterns.md +262 -0
  228. package/src/skills/refactor.md +32 -0
  229. package/src/skills/release.md +43 -0
  230. package/src/skills/repo-map.md +36 -0
  231. package/src/skills/research.md +42 -0
  232. package/src/skills/review-response.md +36 -0
  233. package/src/skills/roast-me.md +28 -0
  234. package/src/skills/rule.md +100 -0
  235. package/src/skills/ship.md +49 -0
  236. package/src/skills/tdd.md +31 -0
  237. package/src/skills/tickets.md +44 -0
  238. package/src/skills/token-report/SKILL.md +76 -0
  239. package/src/skills/token-report/VENDOR.md +23 -0
  240. package/src/skills/token-report/scripts/token_report.mjs +1301 -0
  241. package/src/skills/wayfinder.md +49 -0
  242. package/src/skills/wiki.md +68 -0
  243. package/src/skills/workflow.md +38 -0
  244. package/themes/README.md +69 -0
  245. package/themes/_TEMPLATE.json +147 -0
  246. package/themes/biker.json +147 -0
  247. package/themes/commentator.json +147 -0
  248. package/themes/cyberpunk.json +147 -0
  249. package/themes/gamer.json +147 -0
  250. package/themes/imperial.json +147 -0
  251. package/themes/joker.json +147 -0
  252. package/themes/marvin.json +147 -0
  253. package/themes/mean.json +147 -0
  254. package/themes/military.json +147 -0
  255. package/themes/neutral.json +147 -0
  256. package/themes/opencode/README.md +58 -0
  257. package/themes/opencode/catppuccin.json +27 -0
  258. package/themes/opencode/dracula.json +27 -0
  259. package/themes/opencode/everforest.json +27 -0
  260. package/themes/opencode/gruvbox.json +27 -0
  261. package/themes/opencode/kanagawa.json +27 -0
  262. package/themes/opencode/neon.json +27 -0
  263. package/themes/opencode/nord.json +27 -0
  264. package/themes/opencode/rosepine.json +27 -0
  265. package/themes/opencode/tokyonight.json +27 -0
  266. package/themes/pirate.json +147 -0
  267. package/themes/sports.json +147 -0
  268. package/themes/verstappen.json +147 -0
  269. package/themes/wizard.json +147 -0
  270. package/web/dist/assets/About-DX1-VTBP.js +1 -0
  271. package/web/dist/assets/Activity-Dp9NaXOn.js +1 -0
  272. package/web/dist/assets/ActivityDetail-HpOBtrXS.js +1 -0
  273. package/web/dist/assets/Diff-Ct4G3Se8.js +5 -0
  274. package/web/dist/assets/Doctor-DGjkZFuA.js +1 -0
  275. package/web/dist/assets/Graph-b8o-S6kv.js +1 -0
  276. package/web/dist/assets/Harnesses-gr3-aHBF.js +1 -0
  277. package/web/dist/assets/Laws-DTwF5tTC.js +1 -0
  278. package/web/dist/assets/Library-BEZscyaQ.js +1 -0
  279. package/web/dist/assets/Markdown-k_LSLBGq.js +1 -0
  280. package/web/dist/assets/Profile-DydukTxP.js +1 -0
  281. package/web/dist/assets/Rules-CIRG4Ldt.js +1 -0
  282. package/web/dist/assets/Skills-Buoi1IBr.js +1 -0
  283. package/web/dist/assets/StatusBadge-CE_AJttl.js +1 -0
  284. package/web/dist/assets/Themes-CI4o3gJC.js +2 -0
  285. package/web/dist/assets/activity-BCW7e6Cy.js +1 -0
  286. package/web/dist/assets/audiowide-latin-400-normal-6GFCX7ni.woff2 +0 -0
  287. package/web/dist/assets/geist-mono-latin-400-normal-DKaoCDn5.woff2 +0 -0
  288. package/web/dist/assets/geist-mono-latin-500-normal-C3sF8Y1B.woff2 +0 -0
  289. package/web/dist/assets/geist-mono-latin-600-normal-BHzjB6_C.woff2 +0 -0
  290. package/web/dist/assets/hanken-grotesk-latin-400-italic-Bserb8X5.woff2 +0 -0
  291. package/web/dist/assets/hanken-grotesk-latin-400-normal-BG6hkEXj.woff2 +0 -0
  292. package/web/dist/assets/hanken-grotesk-latin-500-italic-BGfwwhek.woff2 +0 -0
  293. package/web/dist/assets/hanken-grotesk-latin-500-normal-DrDcrrxK.woff2 +0 -0
  294. package/web/dist/assets/hanken-grotesk-latin-600-normal-CIXX6EOa.woff2 +0 -0
  295. package/web/dist/assets/hanken-grotesk-latin-700-normal-CeQ8H3UY.woff2 +0 -0
  296. package/web/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  297. package/web/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  298. package/web/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  299. package/web/dist/assets/ibm-plex-serif-latin-400-normal-BIGslYFI.woff2 +0 -0
  300. package/web/dist/assets/ibm-plex-serif-latin-500-normal-REKAfBVG.woff2 +0 -0
  301. package/web/dist/assets/ibm-plex-serif-latin-600-normal-DAxitmSu.woff2 +0 -0
  302. package/web/dist/assets/index-B1PIx7RM.js +1 -0
  303. package/web/dist/assets/index-BnTQUSUz.js +12 -0
  304. package/web/dist/assets/index-Ck2iCdaC.js +2 -0
  305. package/web/dist/assets/index-DUCsDv7z.css +1 -0
  306. package/web/dist/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  307. package/web/dist/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  308. package/web/dist/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  309. package/web/dist/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
  310. package/web/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  311. package/web/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  312. package/web/dist/assets/jetbrains-mono-latin-600-normal-C8RAYTDA.woff2 +0 -0
  313. package/web/dist/assets/marked.esm-JKvyS0Pp.js +46 -0
  314. package/web/dist/assets/oswald-latin-400-normal-BAFo8sP8.woff2 +0 -0
  315. package/web/dist/assets/oswald-latin-500-normal-B-eyT0Dw.woff2 +0 -0
  316. package/web/dist/assets/oswald-latin-600-normal-plFmSr5g.woff2 +0 -0
  317. package/web/dist/assets/oswald-latin-700-normal-vy6hmKSr.woff2 +0 -0
  318. package/web/dist/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  319. package/web/dist/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  320. package/web/dist/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  321. package/web/dist/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  322. package/web/dist/assets/react-DOpiTn5v.js +32 -0
  323. package/web/dist/assets/roman-DkYN0LX9.js +1 -0
  324. package/web/dist/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
  325. package/web/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  326. package/web/dist/assets/space-grotesk-latin-600-normal-DjKNqYRj.woff2 +0 -0
  327. package/web/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
  328. package/web/dist/assets/space-mono-latin-400-italic-YylcN9Ay.woff2 +0 -0
  329. package/web/dist/assets/space-mono-latin-400-normal-Rg4St2Dn.woff2 +0 -0
  330. package/web/dist/assets/space-mono-latin-700-italic-vNvENeTh.woff2 +0 -0
  331. package/web/dist/assets/space-mono-latin-700-normal-mWgeinG7.woff2 +0 -0
  332. package/web/dist/icon-maskable.svg +10 -0
  333. package/web/dist/icon.svg +8 -0
  334. package/web/dist/index.html +21 -0
  335. package/web/dist/manifest.webmanifest +14 -0
  336. package/web/dist/sw.js +40 -0
  337. package/web/src/pages/Laws.jsx +230 -0
@@ -0,0 +1,149 @@
1
+ # Declined divergences
2
+
3
+ Places where this CLI deliberately does **not** reproduce the Python reference it was ported
4
+ from, with the reason written down. A divergence that is not on this page is a bug; one that is,
5
+ is a decision.
6
+
7
+ The reference has been deleted, so "the reference does X" is no longer an argument anyone can
8
+ re-run. What is recorded in `tests/__snapshots__/` is the reference's answer, frozen; what is
9
+ listed here is where this implementation answers differently on purpose.
10
+
11
+ ---
12
+
13
+ ## `--help`: the prog is `geneseed`, not `harness`
14
+
15
+ **Status:** declined (the port's wording is kept), narrow and gated.
16
+
17
+ **What differs.** The reference opened `usage: harness status` for `status --help`; this CLI
18
+ opens `usage: geneseed status`. The same substitution applies to `bin/geneseed-hook.mjs`, which
19
+ spells itself `geneseed-hook`.
20
+
21
+ **Why.** `harness` named the Python file this migration deleted. It was not a command anyone
22
+ could type, and now it names nothing at all. This CLI already spells itself `geneseed` in every
23
+ error it prints (`geneseed: error: …`, `bin/geneseed-cli.mjs`'s `die`), so reproducing `harness`
24
+ here would make the help text the only surface still pointing at the old name.
25
+
26
+ **How it stays honest.** `prog` is an ARGUMENT of `js/cli.mjs`'s `formatHelp`, not a constant.
27
+ `tests/snapshot/cli_help.test.mjs` replays all 26 recorded fixtures through it with the reference's own
28
+ prog and requires them byte for byte. The rename is the only thing that moves, and a separate
29
+ assertion pins the shipped spelling so a renderer that ignored `prog` could not pass both.
30
+
31
+ That the formatter agrees at every WIDTH — argparse's whole layout: usage assembly, the wrapped
32
+ continuation indent, the two-column help position, the wrap column — was established by a live
33
+ sweep over all 26 verbs at every `COLUMNS` from 20 to 200 while the reference still ran. It
34
+ retired with the reference, and it is the reason the fixtures can be trusted: the 26 are recorded
35
+ at ONE width (`COLUMNS=80`), and at that width two real divergences were invisible — a mutually
36
+ exclusive group emitted as one unsplittable usage part, and `_max_help_position` baked as a
37
+ constant where argparse derives it from the width. Both were found by the sweep and fixed. A
38
+ claim about "the entire layout" that rests on a corpus at one width is a claim about one width,
39
+ so a change to the wrapping arithmetic now needs its own argument rather than a green corpus.
40
+
41
+ Note that the divergence could not have been applied to a recorded text after the fact: the
42
+ continuation indent of a wrapped usage line is `len('usage: ') + len(prog) + 1`, so four of the
43
+ 26 usage blocks re-flow when the prog changes length.
44
+
45
+ ---
46
+
47
+ ## `update --help` names `update`, where the reference names `upgrade`
48
+
49
+ **Status:** reference behaviour, deliberately not reproduced.
50
+
51
+ **What differs.** The reference printed `usage: harness upgrade [-h] [theme]` for `update --help`
52
+ — the canonical verb, for a command the user spelled `update`. This CLI prints `usage: geneseed
53
+ update`.
54
+
55
+ **Why.** argparse implements an alias by registering a second key in `_SubParsersAction.choices`
56
+ pointing at the SAME parser object, and `parser.prog` was fixed when that object was built. The
57
+ leaked name is an implementation detail of the reference's alias mechanism, not a statement about
58
+ the CLI. Recorded verbatim in `tests/__snapshots__/help/update.txt` and replayed with the
59
+ reference's prog, so the reference's answer is preserved; the shipped call passes the verb the
60
+ user typed.
61
+
62
+ ---
63
+
64
+ ## `link` bakes the interpreter into a written shim, where the Unix arm used to symlink
65
+
66
+ **Status:** ACCEPTED, both consequences below. This is a change to the PRODUCT, not a port
67
+ divergence — since Task 2b both implementations write the same shim — and it is recorded here
68
+ because this is the page that exists for "what did we decide to live with".
69
+
70
+ **What changed.** `./geneseed link` used to symlink `ROOT/geneseed` into `~/.local/bin`; the
71
+ launcher then resolved the symlink at RUN TIME and found its own interpreter. It now writes a
72
+ `#!/bin/sh` shim carrying a `# GENESEED_LINK_SHIM` marker that `exec`s the runtime named by
73
+ absolute path (`process.execPath`), mirroring what the Windows arm has always done. The reason it
74
+ moved is in `.superpowers/sdd/progress.md` Task 2b: the two implementations disagreed on four
75
+ cells, and this repo has no deliberate-divergence mechanism, so the reference moved to the port's
76
+ shape rather than the corpus recording a split.
77
+
78
+ **Consequence 1 — durability.** The baked path can go away. A shim written under `nvm` names
79
+ `~/.nvm/versions/node/v22.x/bin/node`; switch or clean up, and `geneseed` on `PATH` points at a
80
+ runtime that no longer exists. The old symlink survived that, because it deferred the choice to the
81
+ launcher. Accepted: the fix is one `./geneseed link` re-run, the failure is loud and immediate
82
+ (`no such file or directory`), and the Windows arm has carried the same property since it
83
+ shipped. Prefer the old behaviour? A shell function in your rc does exactly that, and SETUP.md
84
+ offers it beside `link`.
85
+
86
+ **Consequence 2 — which CLI is on `PATH`.** The shim names the implementation that WROTE it, and
87
+ while two implementations existed that decided whether `geneseed tui` and `geneseed menu` opened
88
+ the full-screen panel or refused it. This is now settled rather than a consequence: there is one
89
+ implementation, and the panel it does not carry no longer exists anywhere to be reached. Both
90
+ verbs cross and both refuse the panel by name, stating what this entry does carry (the TUI's
91
+ layout half, not its screens) and offering `setup`, `doctor` and `build` instead. The refusal
92
+ names no file, deliberately — an earlier one pointed at the panel's source, and a pointer to
93
+ something a migration is deleting is worse than a plain statement that the screen is not there.
94
+
95
+ ---
96
+
97
+ ## argparse's ERROR wording is not reproduced
98
+
99
+ **Status:** declined, pre-existing (P5a, P10c), restated here because it is the neighbour of the
100
+ entry above and readers keep finding it separately.
101
+
102
+ argparse printed a usage block around every parse failure and prefixed it `harness: error:`; both
103
+ entries state the fault on one line prefixed `geneseed: error:` / `geneseed-hook: error:`. See
104
+ `bin/geneseed-cli.mjs`'s `parse` docblock. What IS gated is every error a command's own body
105
+ raises — those are in the recorded CLI corpus, replayed by `tests/cli_golden.mjs`.
106
+
107
+ ---
108
+
109
+ ## Top-level `--help` is still a refusal on the Node entries
110
+
111
+ **Status:** open, out of scope for Task 10, stated so it is not mistaken for done.
112
+
113
+ `geneseed --help` with no verb prints
114
+ `geneseed: error: the following arguments are required: cmd (one of …)` on stderr and exits 2,
115
+ where the reference printed the whole parser's help with a subcommand table. Task 10's surface
116
+ was the 26 per-verb help texts and only those are recorded.
117
+
118
+ Rendering the top-level help needs argparse's `_iter_indented_subactions` path — the subcommand
119
+ choices as a metavar in the usage line, and a nested, differently-indented action list whose
120
+ invocations feed the same `_action_max_length` — which is a distinct piece of the formatter with
121
+ no fixture behind it. Whoever writes it should record it the same way first.
122
+
123
+ ---
124
+
125
+ ## `--help` anywhere on the line wins, where argparse would fail on an earlier option
126
+
127
+ **Status:** declined (the port's behaviour is kept), and the gate that pins it has been corrected
128
+ to stop calling it argparse's rule.
129
+
130
+ **What differs.** Both Node entries scan the whole argument list for `-h`/`--help` BEFORE parsing
131
+ (`bin/geneseed-cli.mjs`, `bin/geneseed-hook.mjs`), so `geneseed version --target --help` prints
132
+ the help and exits 0. argparse consumes optionals left to right, so `--target` swallows `--help`
133
+ as its value and the reference exits 2 with `argument --target: expected one argument`. Three
134
+ error classes reach it — a missing value, an invalid `type=int` value (`web --port abc -h`), and a
135
+ mutex conflict (`theme --solid-only --transparent-only --help`) — across 19 mandatory-value
136
+ options on 13 verbs, plus `--memory` and `--root` on the hook entry.
137
+
138
+ **Why declined.** On a malformed command line the two answers are "help, exit 0" and "the error,
139
+ exit 2". Neither takes an action; nothing is written, nothing is destroyed. A scan that stopped at
140
+ the first option expecting a value would have to reproduce argparse's consumption order to be
141
+ right, and that order is the piece of the parser this port deliberately does not carry (see
142
+ argparse's ERROR wording above). Printing help for a line that contains `--help` is the answer a
143
+ user is more likely to have wanted, and it is the same answer for every shape of malformed line.
144
+
145
+ **How it stays honest.** `tests/snapshot/cli_help.test.mjs`'s `a --help anywhere in the line wins` used to
146
+ docblock this as "argparse's own rule". It is not — that generalised from `diff --nope --help`,
147
+ which works only because an UNKNOWN option is deferred to the end of `parse_known_args`. Two of
148
+ that test's four cases are cases where the reference errors instead, so the test pins a
149
+ divergence; its docblock now says which cases those are and points here.
package/docs/limits.md ADDED
@@ -0,0 +1,270 @@
1
+ # Limits — what this tool does not prove about itself
2
+
3
+ Geneseed is one implementation: `bin/`, `js/`, `adapters/`. It used to be two. Until the 2.0
4
+ re-layout there was a second, older implementation beside it, and three cell harnesses ran both
5
+ and compared every byte either one wrote plus both streams and the exit code. That second party
6
+ is deleted. What survives it is a **recording**: the answers it gave, frozen under
7
+ `tests/__snapshots__/`, replayed by `tests/golden.mjs`, `tests/cli_golden.mjs` and
8
+ `tests/web_golden.mjs` against the code that ships.
9
+
10
+ This file is the standing answer to *"what does this tool not prove about itself?"* It is kept
11
+ beside the code rather than in a phase note, because phase notes are per-machine and this
12
+ outlives them.
13
+
14
+ Each row says whether the limit is **ASSERTED** — there is a test that fails if it closes or
15
+ widens without this file changing — or **STATED**, meaning the claim rests on prose and nothing
16
+ enforces it. Prefer asserting. A structural gate must read *code*, never the paragraph that
17
+ admits the limit, or deleting the paragraph is a way to pass it.
18
+
19
+ **New in the 2.0 re-layout**, and each is checked against the tree rather than inherited from the
20
+ previous version of this document:
21
+
22
+ * **Limit zero** below — the corpus proves *unchanged*, never *correct* — promoted from a bullet
23
+ to the frame every other row sits inside, because with the second party gone it now governs all
24
+ of them.
25
+ * **Row 1** — there is no full-screen panel at all any more, on any path. Not "unported": absent.
26
+ * **Row 3** — `/api/pick-folder` still exists as a declared decline and a live 501, but there is
27
+ no longer a handler anywhere for it to be declined *against*.
28
+ * **Row 12** — the width tables are pinned by a recorded hash and a declared Unicode version, not
29
+ by a live Unicode oracle.
30
+
31
+ ---
32
+
33
+ ## ⚠ Limit zero — the corpus proves UNCHANGED, never CORRECT
34
+
35
+ Every replay in this repository compares today's answer against a recorded one. That is a
36
+ regression gate and it is a good one. It is **not** a correctness gate, and the difference is the
37
+ whole subject of this file.
38
+
39
+ The recording was taken from an implementation that **agreed with this one at the time it was
40
+ taken**. Where the two agreed and were both right, the corpus freezes a right answer. Where the
41
+ two agreed and were both wrong, the corpus freezes the agreement — and there is no longer a
42
+ second party who could disagree. A cross-implementation comparison was already structurally blind
43
+ to a defect both sides shared; the recording inherited that blindness and made it permanent.
44
+
45
+ So: a green replay says *"this code still answers what it answered on 2026-08-17."* It says
46
+ nothing about whether that answer was ever the right one. Everything below is the standing list of
47
+ where that matters and of the branches nothing reaches at all.
48
+
49
+ ## ⚠ The corpus is frozen — recorded 2026-08-17, replay-only from here
50
+
51
+ **The recordings under `tests/__snapshots__/` can never be taken again.** They were produced by the
52
+ reference implementation and by nothing else; every program that has ever written a cell is
53
+ deleted, and the three Node replayers reject `--record` by design. The dispatch-only CI job that
54
+ produced the `lf` halves is gone from `.github/workflows/ci.yml` in the same history, so the
55
+ workflow did not outlive the reference it drove.
56
+
57
+ The last recording was taken deliberately, immediately after the edits that had to be made while
58
+ an oracle still existed — the emitted prose that named files the deletion removes, the removal of
59
+ the web API's interpreter field, the three verbs the reference never had, and the release-label
60
+ destamp that stops a version bump from reddening a corpus nobody can re-bless. Everything after
61
+ that point may only be checked against those bytes.
62
+
63
+ **The consequence, ASSERTED by the replayers themselves:** a change that moves a recorded byte is
64
+ a **finding, not a step**. There is no re-bless. If a replay goes red the choice is to revert the
65
+ change or to argue, in writing, that the recording was wrong — and nothing can settle that
66
+ argument any more.
67
+
68
+ **What the last recording fixed, and what a reader should know it cost.** The two platform halves
69
+ had drifted apart for a reason that had nothing to do with either implementation: the `crlf` half
70
+ had captured a stale `Harness/` — this repository's own dogfooded install, which is **gitignored
71
+ build output** — so `doctor` reported seven stale renders on the machine that recorded it and none
72
+ on CI, and the two platforms disagreed permanently. The final recording was taken with that
73
+ directory absent, which is the state a fresh clone has. **A stale local build tree moves recorded
74
+ bytes; an absent or freshly built one does not.** Anyone comparing the halves should know they now
75
+ agree because that input was removed, not because it was ever normalised.
76
+
77
+ ### The one hand-edit ever made to a recorded file
78
+
79
+ **2026-08-17 — `tests/__snapshots__/primitives/{win32,posix}.json`, the `note` field of three cases
80
+ each, edited by hand to repoint `docs/port-ledger.md` at `docs/limits.md`.** It was safe for a
81
+ reason that has to be re-checked before anyone ever does it again, not quoted as a precedent:
82
+ `note` is **never compared as an answer**. `tests/snapshot/pure_snapshot.test.mjs`'s replay loop compares
83
+ `result` and `result_ascii` and reads nothing else from a case; the only claim on `note` is the
84
+ assertion that every noted case's note names this document, and that assertion was updated to the
85
+ new filename rather than dropped — *a note that quietly stops being emitted is a bug that quietly
86
+ becomes an unremarked-on fact.* Nothing that any replay treats as an answer was touched, and the
87
+ edit was verified to move only `note` lines.
88
+
89
+ **This is an exception, not a precedent.** The next person who wants to hand-edit a recording
90
+ should assume the answer is no and re-derive the whole argument above for their own field.
91
+
92
+ ### The second thing the comparison was taught to ignore, and why that is not the same as a hand-edit
93
+
94
+ **2026-08-17 — `destamp()` in `tests/helpers/golden.mjs` now blanks `"_version"` inside
95
+ `agent-overrides.json`, as it already blanked `[release <REL>]` inside `.geneseed-version`.**
96
+
97
+ The configured release version is stamped into emitted bundles in **two** places, and only the first
98
+ had ever been blanked before comparison. The second was found by attempting the 2.0.0 bump: **224 of
99
+ 259 emit cells went red**, every one of them the same file, `CHANGED (558 -> 558 bytes)`. It had
100
+ stayed invisible for two reasons worth remembering — the sweep that added the first arm hunted the
101
+ **bracketed** spelling, which this site does not use; and `1.0.0` and `2.0.0` are the same length, so
102
+ the size never moved and only the hash could see it.
103
+
104
+ **Left unfixed, the frozen corpus would have pinned this project's version at `1.0.0` permanently** —
105
+ not merely 2.0.0 but every possible bump, in 448 recorded files, with no recorder left to re-bless
106
+ one. That is not a limit anyone chose, and it is the reason this is a fix rather than a concession.
107
+
108
+ **The distinction that matters:** this narrows what the replay COMPARES; it does not touch a recorded
109
+ byte. The other ~553 bytes of that stub, and every other file in all 259 cells, stay asserted exactly
110
+ as before. **But it is still a real loss of coverage** — nothing now checks that the emitted
111
+ `_version` matches the configured one, and nothing can, because verifying it would need the oracle
112
+ that recorded these bytes. If that mapping ever breaks, this corpus will be silent about it.
113
+
114
+ ## Frozen residue — true statements that are now permanently unfixable
115
+
116
+ Four things in this repository are wrong, known to be wrong, and **cannot be corrected**, because
117
+ correcting them would move a byte in a corpus with no recorder left. They are listed so a future
118
+ reader finds a decision here rather than concluding nobody looked.
119
+
120
+ - **`js/tui.mjs` and `js/menu.mjs` end a refusal with ``Use `harness setup`, `doctor`, or `build`.``**
121
+ `harness` was the reference's argparse program name and now names nothing. Both strings are
122
+ recorded in the CLI corpus. Changing either reddens cells that can never be re-recorded.
123
+ - **`tests/__snapshots__/primitives/{win32,posix}.json` freeze `[authoring] _web_core prose says …`**
124
+ across five cases, naming a module the cut deleted. Frozen evidence about a program that no
125
+ longer exists — accurate as a record of what the reference answered, misleading as a description
126
+ of today.
127
+ - **`js/cli-table.json`'s `tui` help still promises a "full-screen curses control panel"** for a
128
+ verb that returns 1 on every path. The help corpus is frozen at the 26 texts argparse rendered,
129
+ so this string has no oracle to be corrected against. The verb's actual output is honest; only
130
+ its help is not.
131
+ - **`tests/helpers/matrix/*.json` — 6 files — are permanently un-regenerable.** Their only producer
132
+ was the reference's matrix exporter. The committed copies are the last export, and
133
+ `tests/unit/hook_cli.test.mjs` reads both halves of them to derive the platform union.
134
+ - **`tests/workflow_runtime.test.mjs` sits at `tests/` top level, alone among the plugin suites.**
135
+ `adapters/opencode/workflows/_runtime.js` names that path in a comment, and that file is EMITTED,
136
+ so its bytes are hashed into the emit corpus. Moving the suite to `tests/plugins/` and updating the
137
+ pointer grew the emitted file by 8 bytes and turned the corpus red. **The layout bent around the
138
+ frozen recording, rather than the shipped comment being left to rot.** This is the one place where
139
+ the freeze is visible in the directory structure, and it is deliberate.
140
+ - **`tests/__snapshots__/token_report_primitives.json` names `js/lib/pyfs.mjs`** in one recorded
141
+ `why` field, and this release renamed that file to `js/lib/fs.mjs`. **This one was left alone on
142
+ purpose, and the reasoning is the point:** `why` is never compared — it is interpolated into a
143
+ failure message — so editing it would have been *safe*. Nothing *forced* it. The one hand-edit of a
144
+ recording this release did make (the `note` fields, below) was forced: an assertion matched on
145
+ those strings and would have gone stale. **Reserve editing a recording for when an assertion
146
+ demands it, not for when it is merely harmless**, or the exception becomes the rule and the corpus
147
+ stops being evidence.
148
+
149
+ The first three share one shape: **a string is cheap to fix right up until it is recorded, and then
150
+ it is not fixable at all.** That is the cost of freezing a corpus, paid knowingly.
151
+
152
+ ## What IS covered
153
+
154
+ All 25 subcommands, 29 of 29 web paths, 5 of 5 docs kinds. Every `NOT_PORTED*` table in `js/web/`
155
+ is empty and stays declared as the empty half of a partition — the shape that makes the next
156
+ missing thing visible instead of silently absent.
157
+
158
+ ## Declared, with an assertion
159
+
160
+ | # | The limit | Where it is asserted |
161
+ |---|---|---|
162
+ | 1 | **There is no full-screen panel.** Not unported — absent. `js/tui.mjs`'s `cmdTui` has two arms and **both print and return 1**: off a TTY it says so, on one it says the entry carries the TUI's layout half and not its screens. The reference's panel — its loop, its eighteen screens, its `(stdscr, curses, pal)` drawing helpers, its Windows console window, its menus, the curses arms of `setup` and `bootstrap`, and `doctor`'s progress callback — is deleted with the implementation that had it. **The comparison half of this row is history**: there is no panel to declare a gap against. What is live is the claim about the shipping code, and it is a claim about ABSENCE, which is the kind a structural gate can hold. (This row used to say the refusal *names* the interpreter-and-script invocation of the panel. It has not since P2, which removed the pointer rather than re-aiming it: a refusal naming a file this migration deletes is worse than one that simply says the screen does not exist. The row was factually stale for two phases, which is the argument for reading the gate out of the code rather than out of the prose.) | **`tests/unit/no_panel.test.mjs`**, which needs nothing but the port: behavioural (faked `isTTY`, exit 1, no panel marks on stdout, and the refusal is the only line it writes) and structural — it fails if `js/tui.mjs` grows any of `?1049h`, `?1049l`, `?25l`, a literal ESC, `\x1b[`, `[`. **What is NOT asserted and cannot be**: that a panel, if one were written, would work. The positive control that used to prove the reference panel really painted — the thing that stopped the mark list being a set of strings nothing produces — retired with the reference. The Node file replaces it with a control of its own (see row 6), which proves the marks still match how *this* codebase spells an escape, not that any panel exists. Landing a real panel means deleting the structural test **first** and updating this row second. |
163
+ | 2 | **`_wrap_lines`, the panel's line wrapper.** Its only caller was the panel loop, so it travels with row 1 and is now history along with it. **`textwrap.wrap` itself CROSSED** — this row said "a stdlib payload with no Node twin … port it *with* the panel or not at all" until P1 made `--help` the caller that required it: `pyTextWrap` (`js/cli.mjs`) is a real port of it, driven by `formatHelp`. | `tests/__snapshots__/textwrap.json` freezes the CPython answers (38 cases × widths 11..198, sha256 over the matrix) and `tests/snapshot/pure_snapshot.test.mjs` replays them with no interpreter anywhere on the machine. The live comparison against a running `textwrap` retired with the reference, so **limit zero applies here in its sharpest form**: the matrix is 38 cases wide and one CPython version deep, and a wrap CPython itself later changes is a divergence this repository cannot see. The corpus declares which side of gh-139065 it was recorded on, precisely so it cannot be mistaken for one frozen against the behaviour CPython already fixed. |
164
+ | 3 | **`/api/pick-folder` will never exist here.** It opens an OS-native folder dialog on the daemon host, which is a GUI dependency and a sixth allowed-spawn row for a modal window. `js/web/server.mjs` carries it in `DECLINED_POST`, a set kept distinct from `NOT_PORTED_POST` because "will never cross" is a different claim from "not yet". **What changed in this phase**: there is no longer anything on the other side to decline. The reference's own handler is deleted, so this stopped being a comparison between an endpoint that exists over there and one that does not exist here, and became simply a documented, permanent 501 — and it is the ONLY member of either set, which makes it the last live target the dispatcher's 501 arm has (see row 9). | Probed live, not merely declared: `tests/unit/web_server.test.mjs` drives the real dispatcher and requires POST → 501 and GET → *not* 501 (it must fall through to the SPA). That pair is what refuses the one-line collapse of the POST declarations back into the GET set, which would leave every exported set exactly as written and every set-reading test green. The collapse is planted as **M23** in `tests/mutate.mjs` and killed, so the row's own argument is gated rather than asserted. |
165
+ | 4 | **`_win_user_path`'s registry-write success arm.** `link`/`unlink` edit the persistent user Path through PowerShell. No test in this repo may run that — the edit would outlive the suite, the checkout and the machine. | The *failure* arm is reached honestly: the `unlink` cells hand the verb a PATH with no `powershell` on it, so the code takes the "said nothing about PATH" branch with the registry untouched. Everything that can be *wrong* lives in the pure `winUserPathScript` half, and `tests/__snapshots__/win_user_path.json` is a recorded corpus over it, replayed by `tests/snapshot/pure_snapshot.test.mjs` on every platform (the builder reads nothing host-shaped, and a non-Windows run is what measures that). That the spawn half carries no logic used to be **STATED**, in a test docstring; `tests/unit/win_user_path.test.mjs`'s last test now checks it — one `spawnSync`, the builder's output passed through unchanged, no string work on the directory, and a statement ceiling. If logic drifts INTO the spawn the ungated region grows, and this is what refuses that. |
166
+ | 5 | ~~**`link`/`unlink`'s Unix arm.**~~ **CLOSED at P11.** It writes a `#!/bin/sh` shim into `~/.local/bin` carrying a `# GENESEED_LINK_SHIM` marker and chmod 0755, and it has six cells of its own. (It *symlinked* `ROOT/geneseed` until P0/P1 Task 2b; the `is_symlink()` branch survives so `unlink` still removes a legacy symlink install, and **no cell covers that branch** — the one piece of this row still open.) The six include the `PATH` split (a substring test passes every other cell and fails `a-path-entry-that-merely-contains-the-dir`), the **marker check** that leaves a foreign `geneseed` alone — the decoy is a regular file, not a symlink, so the marker is what has to reject it — and an explicit dir argument with a trailing slash. That last one found a live divergence: the reference built a `Path` and the port kept the raw string, so `str(Path('/x/bin/'))` and `/x/bin/` disagreed in two messages and in the PATH comparison. Fixed in `js/link.mjs` with `pyPathStr`. | The group is a UNION across the two platforms. `tests/cli_golden.mjs`'s `platformDeclarationProblems` travels with the matrix and asserts, on every replay and on both operating systems in CI, that every id declared for this platform is built, every id declared for the other is absent, and neither half is empty; it also PRINTS the half it did not run, so a run says out loud what it skipped. The two directions that need to read **both** halves at once live in `tests/unit/hook_cli.test.mjs`. |
167
+ | 6 | **The boot animation's call site.** `playLine` itself is exercised; what is declared is that `js/setup.mjs` and `js/update.mjs` reach no panel. **Nothing about this row ever needed a second implementation** — its subject is two JavaScript files — and until P3 its only gate was a test that retired with the reference. | **`tests/unit/no_panel.test.mjs`**. Escape sequences, not the word "curses": the first draft looked for the word and failed on a *docblock*, and a structural gate that reads prose is a gate on the documentation — it would have been satisfied by deleting the paragraph that admits the limit. It carries two things its predecessor had not. A **positive control**: the same scan run over `js/anim.mjs`, which paints by design and is excluded by name, must find a hit — otherwise a mark list that has stopped matching how this codebase spells an escape retires all three gates green. And a **literal ESC byte** among the marks, found by accident while falsifying the gate: the falsifying edit was appended with `printf`, which ate the backslash and wrote a real ESC, and every scan stayed green over a module that had just grown a screen-clear. A gate that knows the spellings of a character but not the character is a gate on a coding style. |
168
+
169
+ ## Ungated, and honest about it
170
+
171
+ | # | The limit | Why nothing reaches it |
172
+ |---|---|---|
173
+ | 7 | **`npmBuild`** (`npm install && npm run build`). | A first-run-from-a-partial-checkout path; no cell can be in that state, because `web/dist/` is tracked. `tests/unit/web_daemon.test.mjs` asserts *that* — it re-derives the unreachability from `git ls-files`, so untracking `web/dist/index.html` fails the test instead of quietly making the arm live. It also asserts the file must EXIST in the working tree, because tracked-but-absent is a real state in which the `ask` arm is reachable here and now while the tracking claim still holds. The two branches only reachable through it — the prompt's wording and EOF handling, and the `npm install` → `npm run build` order — are asserted as source and gated as **M25**. |
174
+ | 8 | **The stale-factory probe.** *(HISTORY, not a live limit.)* | Its subject was a reference CLI too old to know `upgrade`, dead-ending on argparse's `invalid choice` — a program that no longer exists. `js/update.mjs` argues the absence out at the call site: the update step is an **import, not a spawn**, so there is no second program to be stale, no argparse to refuse, and no `invalid choice` to recognise. The half that DID have a live subject is the durable trace a failed step leaves, and it is asserted absolutely by `tests/unit/update_steps.test.mjs` — persisted before it is printed, appended rather than overwritten, still returned when the log itself cannot be written. |
175
+ | 9 | **The web dispatcher's 501 arm, for anything but one path.** | `NOT_PORTED_ACTIONS`, `NOT_PORTED_KINDS` and `NOT_PORTED_POST` are all empty, so `/api/pick-folder` (row 3) is the only target the arm has left, and no second one can be borrowed: every remaining action either starts a job in the developer's checkout or writes a shim and edits the real user PATH. The action-side claim therefore rests on `tests/unit/web_jobs.test.mjs` reading the set out of the module — **a gate on a declaration, which is weaker than a probe**, and is declared as such at both sites. A new row in that set makes the arm reachable again and obliges this row to become a probe. What is stronger is the sibling declaration beside it: each of the three `INLINE_ACTIONS` names a recorded cell that really drives the dispatcher, and the cell's existence is asserted on both corpus halves. |
176
+ | 10 | **`pyStrPath` / `pyPathStr`.** Two spellings of one translation in `js/generate.mjs`. | Ungated; the comment at the call site says so. The recorded CLI cells read stderr through a universal-newline decode and cannot see the difference the split is about. |
177
+ | 11 | **The Linux browser lookup answering FALSE.** The reference walked `webbrowser`'s registry (`$BROWSER`, `xdg-open`, `gio`, `gvfs-open`, a dozen browser binaries); `js/menu.mjs`'s `browserAvailable` asks only whether `xdg-open` — the binary `openUrl` will actually run — is on PATH. On a box with `gio` and no `xdg-open` they disagreed, which is what a WSL run measured. | The corpus in `tests/unit/web_first.test.mjs` SUPPLIES the input instead of inheriting it: an `xdg-open` stub on PATH plus `DISPLAY`, so the True arm is reached identically on every host, and a `no-display` row gates the Linux-only display refusal absolutely. Reaching the FALSE arm needs a PATH with no `xdg-open` at all, which is not something a probe on Windows survives — so it stays ungated, here, rather than in a comment. **The divergence it names is now permanent** and belongs in `docs/declined.md`'s territory rather than this file's: with the reference gone, `browserAvailable`'s answer is simply the answer, and nothing can say it is the wrong one. |
178
+ | 12 | **The width tables are pinned by a HASH, not by a live oracle.** `js/tui.mjs`'s `WIDE`/`COMBINING` tables are a snapshot of unidata **15.1.0** (declared as `DWIDTH_UNIDATA`). Nothing in this repository consults a live Unicode database any more — there is none to consult — so what the sweep proves is that the tables have not moved since they were recorded, and **not** that they encode any Unicode version correctly. This was already true of the CPython comparison in one direction (3.14 carries 16.0.0 and assigned U+0897 a combining class, so the old live sweep reported a divergence that was neither implementation's); it is now true in both. | `tests/__snapshots__/dwidth.json` carries all 527 runs, a sha256 over them, and the unidata version they were measured at; `tests/snapshot/pure_snapshot.test.mjs` checks `DWIDTH_UNIDATA` against that declaration, asserts the constant NAMES a Unicode version at all — read out of `js/tui.mjs` by regex **as well as** imported, so the gate cannot be satisfied by deleting the declaration and supplying the value some other way — and replays the sweep. The document self-checks against its own hash first, so a hand-edited `rle` is not accepted as an oracle. The interpreter pin that used to hold this together retired with the live sweep. Regenerating the tables means moving the constant and re-recording the corpus **together**, and re-recording is exactly what is no longer possible: **new tables would have to arrive with a new corpus recorded from the port itself, which is a determinism check and not a regression gate.** |
179
+ | 13 | **`minuteStamp`'s recorded answers carry the recording machine's clock.** `%Y-%m-%d %H:%M` off a naive local-time conversion, so the frozen answers hold one UTC offset. There is no token to substitute — the offset is the answer's arithmetic, not a substring of it. | Each of the five cases carries a `guard` naming the UTC offset **at that instant** (so DST is part of the match, not an approximation), and `tests/snapshot/pure_snapshot.test.mjs` recomputes it from `Date` before asserting. A replay in another zone SKIPS those cases and PRINTS how many it skipped — the width sweep's rule that a skip is not a pass. The live comparison that used to gate them everywhere is retired, so they are gated only where the offsets agree, which is not many machines. That is the cost of the cut, taken deliberately: a case asserted in one zone and declared in the others beats one silently re-blessed to whatever zone last ran it. |
180
+
181
+ **On row 8, and the shape it is an example of.** A row whose only gate was a retiring test silently
182
+ demotes from ASSERTED to prose at a deletion — which is the exact failure this file exists to
183
+ prevent. Row 8 was the first to be settled the other way instead: its *stale-factory* half
184
+ describes machinery this tool cannot have, so it is recorded as history and the design argument
185
+ lives at the call site in `js/update.mjs`; its *durable trace* half had a live subject, so it landed
186
+ absolutely on the Node side **before** the code that used to carry it went. Rows 1, 2 and 12 are
187
+ now settled the same way, and row 3's history half with them.
188
+
189
+ ## Not proven anywhere, and worth knowing before a release
190
+
191
+ * ~~**The three cell harnesses have only ever run on Windows.**~~ **FIXED at P11**, and widened in
192
+ the 2.0 re-layout: the `node-cells` job in `.github/workflows/ci.yml` replays all three corpora
193
+ on **`ubuntu-latest` AND `windows-latest`** on every push, and `validate` (doctor plus the whole
194
+ Node suite) runs on both as well. The budget argument that kept the old comparison job
195
+ Linux-only does not carry over — a replay is a single spawn per cell — and the `crlf` half of
196
+ the corpus has no other replayer at all. What the first Linux run found, recorded because it is
197
+ the honest measure of what one-platform testing hides:
198
+ * The POSIX shim's `"$@"` was read as a path, so every healthy install's hooks were reported
199
+ dead — **a user-visible `doctor` bug on every Linux and macOS install**, in both
200
+ implementations, plus 112 of 259 golden cells and four `--validate-only` tests. Fixed at the
201
+ single owner.
202
+ * A POSIX re-exec called `execv` without flushing, so `bootstrap` discarded everything it had
203
+ printed since the last spawn — a redirected `bootstrap` lost its completion line on every Unix
204
+ run. Windows has no `exec` and never reaches the line; a terminal hides it because line
205
+ buffering had already written it.
206
+ * A cell driver left the interpreter a bare name, which `CreateProcess` resolves against the
207
+ PARENT's PATH and `execvpe` does not — so any cell that replaces PATH could not spawn the
208
+ candidate at all off Windows.
209
+ * A wizard decline was POSITIONAL (six newlines, then `n`), and the wizard does not ask a fixed
210
+ number of questions. Off Windows the `n` landed one prompt late, the wizard proceeded, and it
211
+ emitted a whole install into the sandbox HOME.
212
+ * Two web `expect_re`s named Windows' case-folded catalog order as if it were the only one.
213
+ * The `link`/`unlink` Unix arm (row 5) and the browser step (row 11).
214
+ * **What the first WINDOWS CI run found, and what the harnesses now guarantee.** The same argument,
215
+ one platform over: PR #78's first run was green on a Windows laptop and in WSL, and red on
216
+ `windows-latest`. Every finding was an environment the developer's machine could not produce, so
217
+ each fix carries a gate that MANUFACTURES the environment rather than one that waits for a runner
218
+ to supply it.
219
+ * **Sandbox paths are CANONICAL.** GitHub's Windows runner spells `TEMP` as the 8.3 alias
220
+ `C:\Users\RUNNER~1\…`; the generator resolves it, so every destamp root matched nothing and the
221
+ raw sandbox name leaked into 45 byte comparisons — and `git-gate/sovereign-bypass` went VACUOUS
222
+ because the excludes entry it seeds stopped matching a resolved cwd. `tests/helpers/sandbox.mjs`
223
+ is the one owner for all three replayers. POSIX reaches the same duality through symlinks and
224
+ the gates build one there.
225
+ * **`resolveOut` was `path.resolve` where the reference resolved** — a defect the aliasing exposed
226
+ and a canonicalising fixture would have hidden. Now `pyResolve`, the twin that already existed
227
+ for this and is used at nine other sites.
228
+ * **`pyWhich` did not reproduce the current-directory rule.** `NoDefaultCurrentDirectoryInExePath`
229
+ is an INPUT, Git Bash defines it and the runner does not. The corpus reached the branch and
230
+ never varied the variable; `tests/snapshot/pure_snapshot.test.mjs` now drives both states and asserts
231
+ that exactly one of fifteen answers moves, and which.
232
+ * **A locale-decoded child stdout** in a Windows-path test, which this repo's `PYTHONUTF8=1`
233
+ runbook had been making accidentally right.
234
+ * **A temp-directory cleanup race killed the Linux job** after it had compared 270 of 318 cells;
235
+ the same commit passed on re-run. A sandbox teardown cannot raise.
236
+ * ~~**The reference is load-bearing.**~~ **HISTORY.** The replayers used to default to the reference
237
+ as their `--ref`; there is no `--ref` left to default. What replaced it is limit zero: the
238
+ recording is now the only second party, and it cannot be asked a new question.
239
+ * **Nothing measures the corpus's own coverage from the outside.** The claims that stop a corpus
240
+ quietly ceasing to exercise a function it still names — the fence really varies, the diff reaches
241
+ past 200 elements, a splitlines case breaks where `split('\n')` does not, a capitalize case where
242
+ the rest matters, a `len` case where code points and UTF-16 units differ, both arms of every
243
+ two-valued decision — all live in `tests/snapshot/pure_snapshot.test.mjs` and are written by hand. They
244
+ are the best thing available and they are a list somebody maintains, not a measurement.
245
+
246
+ ## Standing decisions the cut handed forward
247
+
248
+ Not limits on what the tool proves: limits that OPENED when the reference went, created by the
249
+ recordings the cut took. They are here rather than in a phase note because the phase note is
250
+ per-machine.
251
+
252
+ 1. ~~**`cli.json` is a frozen oracle as well as a runtime table.**~~ **SETTLED IN P2.** The roles
253
+ were split rather than ranked. The file MOVED — byte for byte, minus its source digest — to
254
+ **`js/cli-table.json`**, a product path `package.json` already ships, and it is now the OWNED
255
+ document: nothing generates it, and the generator, the digest and doctor's row for it are gone.
256
+ The FROZEN ORACLE role was retired outright: the recorded copy was byte-identical to the table,
257
+ and a second copy nothing could re-record would have made every legitimate future edit an
258
+ unblessable red.
259
+ 2. **The 26 help fixtures cannot be re-recorded.** Adding or rewording a single CLI flag changes
260
+ `formatHelp`'s output and reddens `tests/snapshot/cli_help.test.mjs`, with no argparse left to ask.
261
+ Either the CLI's help text becomes effectively immutable, or the fixtures get re-blessed from
262
+ the port — which makes them a DETERMINISM check rather than a regression gate, the same
263
+ degradation `--idempotent`/`--deletion` already carry. Neither is wrong; picking neither is.
264
+ **This is limit zero with a deadline**, and it is the live one.
265
+ 3. ~~**`record-corpus` stops working entirely.**~~ **DONE, and it is what makes item 2 sharp.** The
266
+ job is deleted from `.github/workflows/ci.yml`. With it went the platform-independence gate and
267
+ the record-twice rot detector: the two recorded platform halves ARE the platform table now, and
268
+ nothing re-derives them. A replacement had to be written while both implementations still
269
+ existed — a port-recorded corpus proves nothing the port did not already believe — and what was
270
+ written instead is the two-OS `node-cells` job above, which replays rather than records.
@@ -0,0 +1,64 @@
1
+ # Plugin setup (OpenCode)
2
+
3
+ > Shared install for every Geneseed OpenCode plugin. Each plugin's own page
4
+ > covers its configuration and verify steps; this page covers the one-time
5
+ > wiring they all share. Canonical long-form lives in the
6
+ > [OpenCode adapter README](../adapters/opencode/README.md).
7
+
8
+ The seven plugins — `geneseed-activity`, `geneseed-context`, `geneseed-guard`,
9
+ `geneseed-learn`, `geneseed-notify`, `geneseed-ponytail`, `geneseed-workflow` —
10
+ ship in **one folder and install together**. OpenCode auto-loads every file in its plugins dir at startup (both
11
+ `.js` and `.ts`), so there is **no `opencode.json` entry** to add (the
12
+ `"plugin"` array is only for npm-package plugins). The directory does not exist
13
+ by default — create it the first time.
14
+
15
+ ## Global install (recommended)
16
+
17
+ The bundle is used from every repo, so install once into OpenCode's global
18
+ config dir. **Run this from inside the Geneseed folder** — the `*.js` glob
19
+ copies all seven plugins, and `GENESEED_HARNESS` is pointed at the sibling
20
+ bundle `geneseed upgrade` builds at `../Harness`:
21
+
22
+ ```
23
+ mkdir -p ~/.config/opencode/plugins
24
+ cp adapters/opencode/plugins/*.js ~/.config/opencode/plugins/
25
+ export GENESEED_HARNESS="$(dirname "$PWD")/Harness" # this shell
26
+ echo "export GENESEED_HARNESS=\"$GENESEED_HARNESS\"" >> ~/.zshrc # persist (run once)
27
+ ```
28
+
29
+ `GENESEED_HARNESS` points the plugins at your deployed bundle so they find the
30
+ memory store, `context.json`, and `wiki.jsonc` with no hand-typed path. Using a
31
+ non-default bundle location (`GENESEED_OUT`)? Set `GENESEED_HARNESS` to that
32
+ path instead of `../Harness`.
33
+
34
+ ### Or let the build place them
35
+
36
+ ```
37
+ geneseed-build --emit opencode-global # add --theme imperial if wanted
38
+ ```
39
+
40
+ The global emit writes only into the config dir and copies the plugins (and the
41
+ `workflows/` dir beside them) for you — no hand-typed `GENESEED_HARNESS`
42
+ needed, because the bundle lives in the config dir the plugins already resolve.
43
+
44
+ ## Per-project install
45
+
46
+ To scope the plugins to a single repo instead of globally:
47
+
48
+ ```
49
+ geneseed-build --emit opencode --target /path/to/your-repo
50
+ ```
51
+
52
+ This creates `.opencode/plugins/` in the repo and drops the seven `*.js` files
53
+ (plus `.opencode/workflows/`) in for you.
54
+
55
+ ## Verify they loaded
56
+
57
+ Start a session with `GENESEED_DEBUG=1` set and do a little work. Each plugin
58
+ logs to stderr on the event it hooks — e.g. `geneseed-context` logs what it
59
+ discovered on session start, `geneseed-learn` logs `wrote N memory file(s)` at
60
+ session end. Total silence from a plugin means it did not load: re-check the
61
+ filename, the `.js` extension, and that the path is exactly the plugins dir
62
+ above.
63
+
64
+ Per-plugin configuration and verify steps live on each plugin's own page.
@@ -0,0 +1,68 @@
1
+ # Token footprint
2
+
3
+ What a deployed harness costs in context-window tokens, per host. Token counts are
4
+ chars/4 estimates and shift a little with theme and version.
5
+
6
+ **Treat every number below as a floor, not a reading.** They were measured on a
7
+ `--footprint full` build of the neutral theme when the tree carried 41 skills and 17
8
+ agents; it carries more skills than that now, and nothing re-measures this page
9
+ automatically. For a live figure on your own install, ask the agent for the
10
+ `token-report` skill — that is what it is for.
11
+
12
+ ## Always-on cost per session
13
+
14
+ The context every host injects at session start, before you type anything:
15
+
16
+ | Component | Claude Code | OpenCode | Bob | Copilot |
17
+ |---|---|---|---|---|
18
+ | Root instruction file | ~10.7k (`CLAUDE.md`) | ~10.6k (`AGENT.md`) | ~10.7k (`AGENTS.md`) | ~10.7k (`AGENTS.md`) |
19
+ | Skill metadata (name + description) | ~1.6k | ~1.6k | ~1.6k | ~1.6k |
20
+ | Agent metadata | ~0.45k | ~0.55k | ~0.45k | ~0.45k |
21
+ | Eager memory/notebook injection | ~1.2k (SessionStart hook) | ~1.2k (context plugin) | ~1.2k (SessionStart hook) | — (no hooks; read on demand) |
22
+ | **Total** | **~14k** | **~14k** | **~14k** | **~12.8k** |
23
+
24
+ The emits are at parity by design: ~14k tokens, about 7% of a 200k
25
+ window (Copilot runs slightly lighter because it has no hook mechanism — the
26
+ memory/notebook indexes load when the agent reads them, not eagerly). The
27
+ eager-injection path is budget-capped identically everywhere
28
+ (16 KB per file, 48 KB total ≈ 12k tokens ceiling), so growing Memory degrades
29
+ every host the same way instead of one silently falling behind.
30
+
31
+ ## Where the tokens go
32
+
33
+ - **The root file is ~76% of the bill**, and its Rules section alone is ~6.3k
34
+ of the ~10.7k. The [footprint dial](#/docs/footprint) (`full` vs `lean`)
35
+ exists precisely because this is the only lever that matters — trimming
36
+ anything else is noise.
37
+ - **Skill bodies (~53k) and agent bodies (~10k) are lazy** on every host —
38
+ loaded only when invoked. A typical skill costs ≤3k per invocation; the
39
+ heaviest (`react-view-transitions`, ~17.5k with its reference files) loads
40
+ its references progressively.
41
+ - Plugin JavaScript (OpenCode, ~26k on disk) runs in the host runtime and
42
+ never enters the context window.
43
+
44
+ ## Why your session may show more
45
+
46
+ The always-on figures above are the *harness's* share. A real session's
47
+ token counter also includes, none of which Geneseed controls:
48
+
49
+ 1. **The host's own overhead** — system prompt plus built-in tool
50
+ definitions. On OpenCode this is typically 5–10k tokens before any
51
+ harness content loads.
52
+ 2. **Your repo's docs, injected eagerly** — the context delivery
53
+ (plugin on OpenCode, SessionStart hook on Claude Code / Bob; Copilot has no
54
+ hook channel, so its sessions read docs on demand instead) discovers and
55
+ injects `README.md`, `CONTRIBUTING.md`, and files under `docs/`, up to the
56
+ 48 KB budget (≈12k tokens). A doc-heavy repo fills it.
57
+ 3. **Wiki eager entries** plus the lazy listing of the rest, if a wiki is
58
+ configured.
59
+ 4. **Grown Memory/Notebook** — the emitted starter set is ~1.2k, but both
60
+ accumulate with use (same 48 KB shared budget).
61
+ 5. **Other instruction files the host also loads** — a global install plus a
62
+ per-repo install, a personal `~/.claude/CLAUDE.md`, or MCP servers each add
63
+ their own share.
64
+
65
+ So a ~34k session on a work repo is normal arithmetic: ~14k harness +
66
+ ~10k repo docs + ~5–10k host overhead. Set `GENESEED_DEBUG=1` (OpenCode) to
67
+ log exactly which files the context plugin injected, or run the SessionStart
68
+ command by hand to see the hook's payload.
@@ -0,0 +1,45 @@
1
+ [
2
+ {
3
+ "id": "start",
4
+ "label": "Get started"
5
+ },
6
+ {
7
+ "id": "concepts",
8
+ "label": "Core concepts"
9
+ },
10
+ {
11
+ "id": "howto",
12
+ "label": "How-to"
13
+ },
14
+ {
15
+ "id": "mcp",
16
+ "label": "MCP servers"
17
+ },
18
+ {
19
+ "id": "lsp",
20
+ "label": "Language servers",
21
+ "harness": "opencode"
22
+ },
23
+ {
24
+ "id": "plugins",
25
+ "label": "Plugins",
26
+ "harness": "opencode"
27
+ },
28
+ {
29
+ "id": "hooks",
30
+ "label": "Hooks",
31
+ "harness": "claude"
32
+ },
33
+ {
34
+ "id": "reference",
35
+ "label": "Reference"
36
+ },
37
+ {
38
+ "id": "extend",
39
+ "label": "Extend"
40
+ },
41
+ {
42
+ "id": "deeper",
43
+ "label": "Deeper"
44
+ }
45
+ ]
@@ -0,0 +1,6 @@
1
+ ---
2
+ group: deeper
3
+ order: 6
4
+ title: "About — version, license, links"
5
+ kind: "about"
6
+ ---
@@ -0,0 +1,8 @@
1
+ ---
2
+ group: deeper
3
+ order: 4
4
+ title: "Claude Code adapter"
5
+ kind: "markdown"
6
+ harness: "claude"
7
+ source: "adapters/claude-code/README.md"
8
+ ---