geneseed 3.1.1 → 3.1.3

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 (106) hide show
  1. package/CHANGELOG.md +123 -1
  2. package/DESIGN.md +15 -15
  3. package/README.md +1 -1
  4. package/SETUP.md +1 -1
  5. package/SHIPPED.md +1 -1
  6. package/bin/{geneseed.mjs → build-driver.mjs} +39 -42
  7. package/bin/geneseed-cli.mjs +33 -32
  8. package/bin/geneseed-hook.mjs +7 -7
  9. package/docs/design-history.md +40 -40
  10. package/docs/extending.md +111 -83
  11. package/docs/web/exclusions.md +1 -1
  12. package/docs/web/footprint.md +1 -1
  13. package/docs/web/foreman-mode.md +2 -2
  14. package/docs/web/themes-author.md +1 -1
  15. package/docs/web-ui.md +41 -29
  16. package/harness.config.json +1 -1
  17. package/js/README.md +203 -0
  18. package/js/build/bundle.mjs +231 -0
  19. package/js/{catalog.mjs → build/catalog.mjs} +7 -7
  20. package/js/build/emit-claude.mjs +287 -0
  21. package/js/build/emit-common.mjs +255 -0
  22. package/js/build/emit-opencode.mjs +214 -0
  23. package/js/{generate.mjs → build/generate.mjs} +27 -28
  24. package/js/{render.mjs → build/render.mjs} +13 -9
  25. package/js/{checkout.mjs → build/source.mjs} +9 -9
  26. package/js/build/stubs.mjs +256 -0
  27. package/js/{themes.mjs → build/themes.mjs} +24 -24
  28. package/js/build/version.mjs +155 -0
  29. package/js/{hooks.mjs → hosts/hooks.mjs} +47 -44
  30. package/js/{hosts.mjs → hosts/hosts.mjs} +30 -28
  31. package/js/{installs.mjs → hosts/installs.mjs} +18 -16
  32. package/js/{link.mjs → hosts/link.mjs} +29 -28
  33. package/js/{mcp.mjs → hosts/mcp.mjs} +10 -9
  34. package/js/{native.mjs → hosts/native.mjs} +24 -22
  35. package/js/{opencode.mjs → hosts/opencode.mjs} +12 -7
  36. package/js/{settings.mjs → hosts/settings.mjs} +63 -55
  37. package/js/inspect/checks-authoring.mjs +761 -0
  38. package/js/inspect/checks-build.mjs +231 -0
  39. package/js/inspect/checks-repo.mjs +308 -0
  40. package/js/{diff.mjs → inspect/diff.mjs} +34 -31
  41. package/js/inspect/doctor.mjs +286 -0
  42. package/js/{excludes.mjs → inspect/excludes.mjs} +30 -33
  43. package/js/{inventory.mjs → inspect/inventory.mjs} +20 -17
  44. package/js/{registry.mjs → inspect/registry.mjs} +12 -10
  45. package/js/inspect/scan.mjs +125 -0
  46. package/js/{status.mjs → inspect/status.mjs} +27 -25
  47. package/js/inspect/validate.mjs +199 -0
  48. package/js/lib/fs.mjs +37 -759
  49. package/js/lib/json.mjs +344 -0
  50. package/js/lib/paths.mjs +242 -0
  51. package/js/lib/proc.mjs +3 -3
  52. package/js/lib/text.mjs +181 -0
  53. package/js/lib/udiff.mjs +1 -1
  54. package/js/{memory.mjs → maintain/memory.mjs} +10 -10
  55. package/js/{migrate.mjs → maintain/migrate.mjs} +28 -26
  56. package/js/{setup.mjs → maintain/setup.mjs} +34 -32
  57. package/js/{uninstall.mjs → maintain/uninstall.mjs} +55 -56
  58. package/js/{update.mjs → maintain/update.mjs} +51 -51
  59. package/js/{anim.mjs → ui/anim.mjs} +30 -28
  60. package/js/{cli.mjs → ui/cli.mjs} +10 -10
  61. package/js/{menu.mjs → ui/menu.mjs} +10 -9
  62. package/js/{tui.mjs → ui/tui.mjs} +36 -34
  63. package/js/web/actions.mjs +84 -85
  64. package/js/web/activity.mjs +22 -21
  65. package/js/web/api.mjs +154 -51
  66. package/js/web/daemon.mjs +319 -0
  67. package/js/web/docs.mjs +24 -21
  68. package/js/web/graph.mjs +0 -0
  69. package/js/web/handler.mjs +442 -0
  70. package/js/web/jobs.mjs +21 -19
  71. package/js/web/routes.mjs +245 -0
  72. package/js/web/server.mjs +56 -987
  73. package/package.json +11 -3
  74. package/src/doctrines/README.md +1 -1
  75. package/themes/opencode/README.md +1 -1
  76. package/web/dist/assets/{Activity-D_sYBlW-.js → Activity-B49fE_ug.js} +1 -1
  77. package/web/dist/assets/{ActivityDetail-BomKcMhy.js → ActivityDetail-abt7hzD2.js} +1 -1
  78. package/web/dist/assets/{Diff-BSvi1WUg.js → Diff-D3_bVkih.js} +1 -1
  79. package/web/dist/assets/{Doctor-Cz1s-y1n.js → Doctor-hXp7srsx.js} +1 -1
  80. package/web/dist/assets/{Graph-CuntzFJ0.js → Graph-DvYMtlvl.js} +1 -1
  81. package/web/dist/assets/Harness-BIWei7N5.js +1 -0
  82. package/web/dist/assets/Laws-CxmnRdwt.js +3 -0
  83. package/web/dist/assets/{Library-DXhTN9ju.js → Library-MjbOkHIW.js} +1 -1
  84. package/web/dist/assets/{Markdown-BoiZcdUl.js → Markdown-Dx_w0faR.js} +1 -1
  85. package/web/dist/assets/{Profile-DfCHlZ03.js → Profile-BRFvv2gr.js} +1 -1
  86. package/web/dist/assets/{Rules-eND7hJv9.js → Rules-CfsV1EVy.js} +1 -1
  87. package/web/dist/assets/{Skills-CDl9opZO.js → Skills-DArA6IHS.js} +1 -1
  88. package/web/dist/assets/{StatusBadge-DSDtcArX.js → StatusBadge-C9bZOEZk.js} +1 -1
  89. package/web/dist/assets/{activity-Dmb0FTAx.js → activity-Cc0n2jwx.js} +1 -1
  90. package/web/dist/assets/index-Bx3oxZ1o.css +1 -0
  91. package/web/dist/assets/index-DC3-GlCY.js +1 -0
  92. package/web/dist/assets/index-DP035wPa.js +12 -0
  93. package/web/dist/assets/{index-Dg58ScBe.js → index-dHi-7M-F.js} +1 -1
  94. package/web/dist/index.html +2 -2
  95. package/web/src/pages/Laws.jsx +5 -19
  96. package/docs/declined.md +0 -200
  97. package/docs/limits.md +0 -329
  98. package/js/doctor.mjs +0 -1750
  99. package/js/emit.mjs +0 -1411
  100. package/web/dist/assets/About-DyK7yRy7.js +0 -1
  101. package/web/dist/assets/Harnesses-DgRyOviS.js +0 -1
  102. package/web/dist/assets/Laws-CWk3Mpri.js +0 -3
  103. package/web/dist/assets/Themes-CoAWuIeM.js +0 -2
  104. package/web/dist/assets/index-B-mXquBZ.css +0 -1
  105. package/web/dist/assets/index-CH_Z725C.js +0 -12
  106. package/web/dist/assets/index-jrY4m9jP.js +0 -1
@@ -1,2 +1,2 @@
1
- import{h as _,j as e,g,I as N,u as w,E as S,L as E,a as T}from"./index-CH_Z725C.js";import{r as p}from"./react-DOpiTn5v.js";import{m as I}from"./marked.esm-JKvyS0Pp.js";const R="geneseed-harness",P=[{id:"opencode",label:"OpenCode"},{id:"claude",label:"Claude Code"}];function A(){return _(R,s=>s==="claude"||s==="opencode"?s:"opencode")}function $(s,n=0){const a=String(s).toLowerCase().trim().replace(/[^a-z0-9\s-]/g,"").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"");return a&&/^[a-z]/.test(a)?a:`h-${n}-${a||"section"}`}function y(s,n){if(!s||!n)return null;try{return s.querySelector(n)}catch{return null}}function H(s){const n=[];let a=!1,l=0;for(const r of(s||"").split(`
1
+ import{i as _,j as e,g,I as N,u as w,E as S,L as E,a as T}from"./index-DP035wPa.js";import{r as p}from"./react-DOpiTn5v.js";import{m as I}from"./marked.esm-JKvyS0Pp.js";const R="geneseed-harness",P=[{id:"opencode",label:"OpenCode"},{id:"claude",label:"Claude Code"}];function A(){return _(R,s=>s==="claude"||s==="opencode"?s:"opencode")}function $(s,n=0){const a=String(s).toLowerCase().trim().replace(/[^a-z0-9\s-]/g,"").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"");return a&&/^[a-z]/.test(a)?a:`h-${n}-${a||"section"}`}function y(s,n){if(!s||!n)return null;try{return s.querySelector(n)}catch{return null}}function H(s){const n=[];let a=!1,l=0;for(const r of(s||"").split(`
2
2
  `)){if(r.startsWith("```")){a=!a;continue}if(a)continue;const d=r.match(/^(##|###)\s+(.+?)\s*$/);d&&(l+=1,n.push({level:d[1].length,title:d[2],id:$(d[2],l)}))}return n}function M(s,n){const a=new Map((n||[]).map(r=>[r.label,r])),l=(s||"").replace(/\[\[([^\]]+)\]\]/g,(r,d)=>{const i=a.get(d.trim());return i?`[${i.label}](#/item/${i.type}/${encodeURIComponent(i.name)})`:r});return I.parse(l,{breaks:!1})}const q={"opencode-global":"path-a-opencode-global-recommended","opencode-per-repo":"path-b-opencode-per-repo","claude-code":"path-c-claude-code","agent-md":"path-d-any-agentmd-tool",bundle:"path-d-any-agentmd-tool"};function O({page:s,overview:n,onAction:a}){const l=p.useRef(null),r=p.useMemo(()=>H(s.body),[s.body]),d=p.useMemo(()=>M(s.body,s.links),[s.body,s.links]);p.useEffect(()=>{const t=l.current;if(!t)return;let o=0;t.querySelectorAll("h1, h2, h3").forEach(u=>{if(o+=1,!u.id){const x=$(u.textContent||"",o);x&&(u.id=x)}})},[d]),p.useEffect(()=>{const t=l.current;if(!t)return;const o=u=>{const x=u.target.closest("a[href]");if(!x)return;const f=x.getAttribute("href");f&&f.startsWith("#/")&&(u.preventDefault(),g(f))};return t.addEventListener("click",o),()=>t.removeEventListener("click",o)},[d]),p.useEffect(()=>{const t=s.anchor;if(!t)return;const o=y(l.current,`#${CSS.escape(t)}`);o&&o.scrollIntoView({behavior:"smooth",block:"start"})},[d,s.anchor]),p.useEffect(()=>{const t=l.current;if(!t||s.id!=="install-paths")return;t.querySelectorAll(".docs-here").forEach(x=>x.classList.remove("docs-here"));const o=q[n==null?void 0:n.emit];if(!o)return;const u=y(t,`#${CSS.escape(o)}`);u&&u.classList.add("docs-here")},[d,s.id,n==null?void 0:n.emit]);const i=t=>{if(!t)return;const o=y(l.current,`#${CSS.escape(t)}`);o&&o.scrollIntoView({behavior:"smooth",block:"start"})},j=r.length>=3,m=[];return(s.id==="install-paths"||s.id==="install-quick")&&(m.push({label:"Run doctor",action:"doctor"}),m.push({label:"Rebuild",action:"build"})),s.id==="verify"&&m.push({label:"Run doctor",action:"doctor"}),s.id==="self-improve"&&m.push({label:"Open diff",hash:"#/diff"}),s.id==="upgrade"&&m.push({label:"Update",action:"update"}),e.jsxs("div",{className:"detail-doc",children:[e.jsx("h1",{style:{marginTop:0},children:s.title}),s.source&&e.jsx("div",{className:"doc-meta",children:e.jsxs("span",{className:"badge",children:[e.jsx("span",{className:"dot"}),s.source]})}),m.length>0&&e.jsx("div",{className:"row wrap gap-10",style:{margin:"10px 0 14px"},children:m.map(t=>t.hash?e.jsx("a",{className:"btn ghost sm",href:t.hash,children:t.label},t.label):e.jsx("button",{className:"btn ghost sm",onClick:()=>a&&a(t.action),children:t.label},t.label))}),j&&e.jsxs("div",{className:"docs-toc",children:[e.jsx("div",{className:"docs-toc-head",children:"On this page"}),e.jsx("ul",{children:r.map(t=>e.jsx("li",{className:`lvl-${t.level}`,children:e.jsx("a",{href:`#${t.id}`,onClick:o=>{o.preventDefault(),i(t.id)},children:t.title})},t.id))})]}),e.jsx("div",{className:"markdown",ref:l,dangerouslySetInnerHTML:{__html:d}}),s.link&&e.jsx("div",{style:{marginTop:24},children:e.jsx("a",{className:"btn soft sm",href:s.link.hash,children:s.link.label})})]})}const U=1200;function k({a:s,kind:n}){const a=n==="positional"?s.dest:s.names.join(", "),l=[];return s.metavar&&l.push(s.metavar),s.choices&&l.push(`{${s.choices.join("|")}}`),s.default!==null&&s.default!==void 0&&s.default!==""&&!s.is_flag&&l.push(`default: ${s.default}`),s.required&&l.push("required"),e.jsxs("div",{className:"cli-arg",children:[e.jsx("code",{className:"cli-arg-name",children:a}),l.length>0&&e.jsxs("code",{className:"cli-arg-meta",children:[" ",l.join(" · ")]}),s.help&&e.jsx("div",{className:"cli-arg-help",children:s.help})]})}function D({text:s}){const[n,a]=p.useState(!1),l=async()=>{try{await navigator.clipboard.writeText(s),a(!0),setTimeout(()=>a(!1),U)}catch{}};return e.jsxs("button",{className:"btn ghost sm",onClick:l,title:"Copy command",children:[e.jsx(N,{name:n?"spark":"copy"}),n?"Copied":"Copy"]})}function F({cmd:s,prog:n}){const a=[n,s.name];for(const r of s.positionals||[]){const d=(r.metavar||r.dest||"").toUpperCase();a.push(r.required?d:`[${d}]`)}(s.options||[]).length&&a.push("[OPTIONS]");const l=a.join(" ");return e.jsxs("div",{className:"card pad-md mb-12 cli-card",children:[e.jsxs("div",{className:"row wrap between",style:{alignItems:"flex-start"},children:[e.jsxs("div",{className:"stack",style:{gap:4},children:[e.jsx("h3",{style:{margin:0},children:e.jsx("code",{children:s.name})}),s.help&&e.jsx("p",{className:"sub",style:{margin:0},children:s.help})]}),e.jsx(D,{text:`${n} ${s.name}`})]}),e.jsx("pre",{className:"cli-syn",children:l}),(s.positionals||[]).length>0&&e.jsxs("div",{className:"cli-block",children:[e.jsx("div",{className:"cli-block-head",children:"Arguments"}),s.positionals.map(r=>e.jsx(k,{a:r,kind:"positional"},r.dest))]}),(s.options||[]).length>0&&e.jsxs("div",{className:"cli-block",children:[e.jsx("div",{className:"cli-block-head",children:"Options"}),s.options.map(r=>e.jsx(k,{a:r,kind:"option"},r.names.join(",")))]})]})}function G({page:s}){const n=s.prog||"geneseed",a=s.commands||[];return e.jsxs("div",{className:"detail-doc",children:[e.jsx("h1",{style:{marginTop:0},children:s.title}),e.jsxs("p",{className:"sub",style:{marginTop:4},children:["Every subcommand of ",e.jsx("code",{children:n}),". Generated from the parser at request time (the same one ",e.jsx("code",{children:"geneseed"})," actually parses), so this page cannot drift."]}),e.jsx("div",{style:{marginTop:18},children:a.map(l=>e.jsx(F,{cmd:l,prog:n},l.name))})]})}function z({page:s}){const n=s.rows||[],a=s.theme||"neutral";return e.jsxs("div",{className:"detail-doc",children:[e.jsx("h1",{style:{marginTop:0},children:s.title}),e.jsxs("p",{className:"sub",style:{marginTop:4},children:["Geneseed's invented vocabulary, decoded. Left: the neutral, plain-English term. Right: the word your ",e.jsx("code",{style:{textTransform:"capitalize"},children:a})," theme actually uses in the agent's voice."]}),e.jsx("div",{className:"card pad-md",style:{marginTop:18},children:e.jsxs("table",{className:"glossary-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Concept"}),e.jsx("th",{children:"Neutral term"}),e.jsxs("th",{children:["In ",a]}),e.jsx("th",{children:"Meaning"})]})}),e.jsx("tbody",{children:n.map(l=>e.jsxs("tr",{children:[e.jsx("td",{children:e.jsx("strong",{children:l.label})}),e.jsx("td",{children:e.jsx("code",{children:l.neutral||"—"})}),e.jsx("td",{children:e.jsx("code",{children:l.themed||l.neutral||"—"})}),e.jsx("td",{className:"dim",children:l.desc})]},l.label))})]})})]})}function B({page:s}){const n=s.version||{},a=[["Theme",s.theme],["Install mode",s.emit],["Deployed",s.deployed?"yes":"no"],["Target",e.jsx("code",{children:s.target},"t")],["Source root",e.jsx("code",{children:s.root},"r")],["Installed build",e.jsx("code",{children:n.installed_fp||"(none)"},"i")],["Source build",e.jsx("code",{children:n.source_fp||"—"},"s")],["Match?",n.verdict||"—"],["License",s.license]];return e.jsxs("div",{className:"detail-doc",children:[e.jsx("h1",{style:{marginTop:0},children:s.title}),e.jsx("p",{className:"sub",style:{marginTop:4},children:"Live snapshot of the install behind this UI, plus where to go next."}),e.jsx("div",{className:"card pad-md",style:{marginTop:18},children:a.map(([l,r])=>e.jsxs("div",{className:"kv",children:[e.jsx("span",{className:"k",children:l}),e.jsx("span",{className:"v",children:r??"—"})]},l))}),e.jsxs("div",{className:"row wrap gap-10",style:{marginTop:18},children:[e.jsxs("a",{className:"btn ghost",href:s.repo,target:"_blank",rel:"noreferrer",children:[e.jsx(N,{name:"external"}),s.repo_is_github?"GitHub repo":"Source repo"]}),s.repo_is_github&&e.jsxs(e.Fragment,{children:[e.jsxs("a",{className:"btn ghost",href:`${s.repo}/issues`,target:"_blank",rel:"noreferrer",children:[e.jsx(N,{name:"external"}),"File an issue"]}),e.jsxs("a",{className:"btn ghost",href:`${s.repo}/blob/main/LICENSE`,target:"_blank",rel:"noreferrer",children:[e.jsx(N,{name:"external"}),"License"]})]})]})]})}function C(s){var a,l,r;const n=(r=(l=(a=s==null?void 0:s.groups)==null?void 0:a[0])==null?void 0:l.pages)==null?void 0:r[0];return(n==null?void 0:n.id)||""}function V({pageId:s,harness:n,overview:a,onAction:l}){const{data:r,error:d,loading:i}=w(()=>s?T.docsPage(s,n):Promise.resolve(null),[s,n]);if(d)return e.jsx(S,{error:d,style:{margin:18}});if(i||!r)return e.jsx(E,{label:"Loading page…"});switch(r.kind){case"markdown":case"concept":return e.jsx(O,{page:r,overview:a,onAction:l});case"cli":return e.jsx(G,{page:r});case"glossary":return e.jsx(z,{page:r});case"about":return e.jsx(B,{page:r});default:return e.jsxs("div",{className:"empty",children:[e.jsx("div",{className:"big",children:"Unknown page kind"}),r.kind]})}}function Y(s,n){if(!s||!n)return null;for(const a of s.groups)if(a.pages.some(l=>l.id===n))return a;return null}function Q({page:s,query:n,overview:a,onAction:l}){var v;const[r,d]=A(),{data:i,error:j}=w(()=>T.docs(r),[r]),m=s||C(i);p.useEffect(()=>{if(!i||!m)return;if(!i.groups.some(h=>h.pages.some(b=>b.id===m))){const h=C(i);h&&h!==m&&g(`#/docs/${encodeURIComponent(h)}`)}},[i,m]);const t=Y(i,m)||((v=i==null?void 0:i.groups)==null?void 0:v[0]),o=(n||"").toLowerCase().trim(),u=p.useMemo(()=>i?o?i.groups.map(c=>({...c,pages:c.pages.filter(h=>h.title.toLowerCase().includes(o)||c.label.toLowerCase().includes(o)||h.id.toLowerCase().includes(o))})).filter(c=>c.pages.length>0):t?[t]:[]:[],[i,o,t]);if(j)return e.jsx(S,{error:j});const x=(i==null?void 0:i.groups)||[],f=!!o,L=c=>{var b;const h=(b=c.pages[0])==null?void 0:b.id;h&&g(`#/docs/${encodeURIComponent(h)}`)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"head-row mb-16",children:[e.jsxs("div",{children:[e.jsx("div",{className:"eyebrow",children:"documentation"}),e.jsx("h1",{className:"h",children:"Docs"}),e.jsx("p",{className:"sub",children:"Concept pages, a generated CLI reference, and a glossary. Pages and config that differ by host are filtered to your selected harness."})]}),e.jsx("div",{className:"seg",role:"group","aria-label":"Harness",children:P.map(c=>e.jsx("button",{className:r===c.id?"on":"",onClick:()=>d(c.id),"aria-pressed":r===c.id,children:c.label},c.id))})]}),e.jsx("div",{className:"lib-secbar",children:x.map(c=>e.jsxs("button",{className:`lib-secchip ${(t==null?void 0:t.id)===c.id?"on":""}`,onClick:()=>L(c),children:[e.jsx("span",{children:c.label}),e.jsx("span",{className:"lib-secchip-n",children:c.pages.length})]},c.id))}),e.jsxs("div",{className:"lib lib-2",children:[e.jsxs("div",{className:"card lib-main",children:[e.jsxs("div",{className:"lib-head",children:[e.jsx("span",{className:"lib-head-label",children:o?"all groups":(t==null?void 0:t.label)||""}),e.jsxs("span",{className:"lib-head-count",children:[u.reduce((c,h)=>c+h.pages.length,0)," pages"]})]}),e.jsxs("div",{className:"lib-rows",children:[u.map(c=>e.jsxs("div",{className:"docs-group",children:[f&&e.jsx("div",{className:"docs-group-head",children:c.label}),c.pages.map(h=>e.jsxs("button",{className:`lib-row ${m===h.id?"on":""}`,onClick:()=>g(`#/docs/${encodeURIComponent(h.id)}`),children:[e.jsx("div",{className:"lr-name",children:h.title}),h.date?e.jsx("div",{className:"lr-desc",children:h.date}):null]},h.id))]},c.id)),u.length===0&&i&&e.jsxs("div",{className:"empty",style:{padding:32},children:[e.jsx("div",{className:"big",children:"No matches"}),"Try another search."]}),!i&&e.jsx(E,{label:"Loading docs…"})]})]}),e.jsx("div",{className:"card lib-detail",children:e.jsx(V,{pageId:m,harness:r,overview:a,onAction:l})})]})]})}export{Q as default};
@@ -11,9 +11,9 @@
11
11
  <!-- The brand sprout as an inline SVG favicon (no extra asset to serve). -->
12
12
  <link rel="icon" type="image/svg+xml"
13
13
  href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21.5v-9' stroke='%233AD4C4' stroke-width='2' stroke-linecap='round' fill='none'/%3E%3Cpath d='M12 13c0-4.5 3.2-7.5 7.5-7.5 0 4.5-3.2 7.5-7.5 7.5z' fill='%233AD4C4'/%3E%3Cpath d='M12 14.5c0-3.6-2.6-6-6-6 0 3.6 2.6 6 6 6z' fill='%233AD4C4' opacity='.42'/%3E%3C/svg%3E" />
14
- <script type="module" crossorigin src="/assets/index-CH_Z725C.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-DP035wPa.js"></script>
15
15
  <link rel="modulepreload" crossorigin href="/assets/react-DOpiTn5v.js">
16
- <link rel="stylesheet" crossorigin href="/assets/index-B-mXquBZ.css">
16
+ <link rel="stylesheet" crossorigin href="/assets/index-Bx3oxZ1o.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>
@@ -5,6 +5,7 @@ import { useAsync } from '../hooks/useAsync.js'
5
5
  import { romanToInt } from '../lib/roman.js'
6
6
  import Loading from '../components/Loading.jsx'
7
7
  import ErrorState from '../components/ErrorState.jsx'
8
+ import { LAW_CATS, LAW_CAT_ORDER, PACK_CATS } from '../lib/lawCats.js'
8
9
 
9
10
  // THE FILENAME AND THE ROUTE STAY `laws`. tests/helpers/cli_golden.mjs hard-requires this path,
10
11
  // doctor's lawMetaProblems reads this ONE file out of web/src, and the npm partition ships it
@@ -15,27 +16,12 @@ import ErrorState from '../components/ErrorState.jsx'
15
16
  //
16
17
  // ⚠ SIX, THOUGH ONLY FOUR HAVE A MEMBER. `context` and `comms` lost theirs when the corpus became
17
18
  // nine invariants — that material moved to the ontology and the doctrine packs, neither of which
18
- // is classed here. The list stays six because it is the VOCABULARY that js/inventory.mjs's
19
+ // is classed here. The list stays six because it is the VOCABULARY that js/inspect/inventory.mjs's
19
20
  // LAW_CLASSES publishes and doctor quotes verbatim; the PAGE renders only non-empty facets, which
20
21
  // is a display decision and not a change to the taxonomy.
21
- const LAW_CATS = {
22
- security: { label: 'Security', c: 'oklch(0.74 0.085 45)' },
23
- verify: { label: 'Verification', c: 'oklch(0.78 0.075 200)' },
24
- process: { label: 'Process', c: 'oklch(0.76 0.075 150)' },
25
- craft: { label: 'Craft', c: 'oklch(0.78 0.085 95)' },
26
- context: { label: 'Context', c: 'oklch(0.74 0.08 280)' },
27
- comms: { label: 'Communication', c: 'oklch(0.76 0.085 345)' },
28
- }
29
- const LAW_CAT_ORDER = ['security', 'verify', 'process', 'craft', 'context', 'comms']
30
-
31
- // A doctrine rule's class IS its pack — there is no second taxonomy over one. Colours only; the
32
- // pack's NAME and blurb are themed and come from the API, so they are never transcribed here.
33
- const PACK_CATS = {
34
- craft: 'oklch(0.78 0.085 95)',
35
- rigor: 'oklch(0.78 0.075 200)',
36
- ops: 'oklch(0.76 0.075 150)',
37
- process: 'oklch(0.74 0.085 45)',
38
- }
22
+ // The colour vocabulary moved to lib/lawCats.js when the Journal dashboard's constitution
23
+ // map started painting the same rules — this page cannot be imported from the app shell
24
+ // (it is lazy for a reason) and it cannot move (doctor reads LAW_META out of this path).
39
25
 
40
26
  // One row per invariant in src/laws/universal.md: its class (fallback for an older server that
41
27
  // returns no `klass`) and the one-line principle shown in the table's "Principle" column — display
package/docs/declined.md DELETED
@@ -1,200 +0,0 @@
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
- **The recording is smaller than it was.** On **2026-08-17** the emit, cli and web corpora were
12
- retired — 1381 cells, produced by an implementation that no longer exists and re-recordable by
13
- nothing, so they reddened on every deliberate content change while saying nothing about a defect
14
- that happened to be stable. What licensed it was a measurement: all five mutations whose only
15
- declared gate was a corpus replay are killed by `tests/unit/` alone. `docs/limits.md` carries the
16
- full argument and what it cost. **The consequence for this page is narrow and worth stating: an
17
- entry whose "how it stays honest" half named one of those three corpora now names a weaker gate, or
18
- none.** The help fixtures under `tests/__snapshots__/help/`, which most of this page rests on, are
19
- untouched.
20
-
21
- ---
22
-
23
- ## `--help`: the prog is `geneseed`, not `harness`
24
-
25
- **Status:** declined (the port's wording is kept), narrow and gated.
26
-
27
- **What differs.** The reference opened `usage: harness status` for `status --help`; this CLI
28
- opens `usage: geneseed status`. The same substitution applies to `bin/geneseed-hook.mjs`, which
29
- spells itself `geneseed-hook`.
30
-
31
- **Why.** `harness` named the Python file this migration deleted. It was not a command anyone
32
- could type, and now it names nothing at all. This CLI already spells itself `geneseed` in every
33
- error it prints (`geneseed: error: …`, `bin/geneseed-cli.mjs`'s `die`), so reproducing `harness`
34
- here would make the help text the only surface still pointing at the old name.
35
-
36
- **How it stays honest.** `prog` is an ARGUMENT of `js/cli.mjs`'s `formatHelp`, not a constant.
37
- `tests/snapshot/cli_help.test.mjs` replays all 26 recorded fixtures through it with the reference's own
38
- prog and requires them byte for byte. The rename is the only thing that moves, and a separate
39
- assertion pins the shipped spelling so a renderer that ignored `prog` could not pass both.
40
-
41
- That the formatter agrees at every WIDTH — argparse's whole layout: usage assembly, the wrapped
42
- continuation indent, the two-column help position, the wrap column — was established by a live
43
- sweep over all 26 verbs at every `COLUMNS` from 20 to 200 while the reference still ran. It
44
- retired with the reference, and it is the reason the fixtures can be trusted: the 26 are recorded
45
- at ONE width (`COLUMNS=80`), and at that width two real divergences were invisible — a mutually
46
- exclusive group emitted as one unsplittable usage part, and `_max_help_position` baked as a
47
- constant where argparse derives it from the width. Both were found by the sweep and fixed. A
48
- claim about "the entire layout" that rests on a corpus at one width is a claim about one width,
49
- so a change to the wrapping arithmetic now needs its own argument rather than a green corpus.
50
-
51
- Note that the divergence could not have been applied to a recorded text after the fact: the
52
- continuation indent of a wrapped usage line is `len('usage: ') + len(prog) + 1`, so four of the
53
- 26 usage blocks re-flow when the prog changes length.
54
-
55
- ---
56
-
57
- ## `update --help` names `update`, where the reference names `upgrade`
58
-
59
- **Status:** reference behaviour, deliberately not reproduced.
60
-
61
- **What differs.** The reference printed `usage: harness upgrade [-h] [theme]` for `update --help`
62
- — the canonical verb, for a command the user spelled `update`. This CLI prints `usage: geneseed
63
- update`.
64
-
65
- **Why.** argparse implements an alias by registering a second key in `_SubParsersAction.choices`
66
- pointing at the SAME parser object, and `parser.prog` was fixed when that object was built. The
67
- leaked name is an implementation detail of the reference's alias mechanism, not a statement about
68
- the CLI. Recorded verbatim in `tests/__snapshots__/help/update.txt` and replayed with the
69
- reference's prog, so the reference's answer is preserved; the shipped call passes the verb the
70
- user typed.
71
-
72
- ---
73
-
74
- ## `link` bakes the interpreter into a written shim, where the Unix arm used to symlink
75
-
76
- **Status:** ACCEPTED, both consequences below. This is a change to the PRODUCT, not a port
77
- divergence — since Task 2b both implementations write the same shim — and it is recorded here
78
- because this is the page that exists for "what did we decide to live with".
79
-
80
- **What changed.** `./geneseed link` used to symlink `ROOT/geneseed` into `~/.local/bin`; the
81
- launcher then resolved the symlink at RUN TIME and found its own interpreter. It now writes a
82
- `#!/bin/sh` shim carrying a `# GENESEED_LINK_SHIM` marker that `exec`s the runtime named by
83
- absolute path (`process.execPath`), mirroring what the Windows arm has always done. The reason it
84
- moved is in `.superpowers/sdd/progress.md` Task 2b: the two implementations disagreed on four
85
- cells, and this repo has no deliberate-divergence mechanism, so the reference moved to the port's
86
- shape rather than the corpus recording a split.
87
-
88
- **Consequence 1 — durability.** The baked path can go away. A shim written under `nvm` names
89
- `~/.nvm/versions/node/v22.x/bin/node`; switch or clean up, and `geneseed` on `PATH` points at a
90
- runtime that no longer exists. The old symlink survived that, because it deferred the choice to the
91
- launcher. Accepted: the fix is one `./geneseed link` re-run, the failure is loud and immediate
92
- (`no such file or directory`), and the Windows arm has carried the same property since it
93
- shipped. Prefer the old behaviour? A shell function in your rc does exactly that, and SETUP.md
94
- offers it beside `link`.
95
-
96
- **Consequence 2 — which CLI is on `PATH`.** The shim names the implementation that WROTE it, and
97
- while two implementations existed that decided whether `geneseed tui` and `geneseed menu` opened
98
- the full-screen panel or refused it. This is now settled rather than a consequence: there is one
99
- implementation, and the panel it does not carry no longer exists anywhere to be reached. Both
100
- verbs cross and both refuse the panel by name, stating what this entry does carry (the TUI's
101
- layout half, not its screens) and offering `setup`, `doctor` and `build` instead. The refusal
102
- names no file, deliberately — an earlier one pointed at the panel's source, and a pointer to
103
- something a migration is deleting is worse than a plain statement that the screen is not there.
104
-
105
- ---
106
-
107
- ## argparse's ERROR wording is not reproduced
108
-
109
- **Status:** declined, pre-existing (P5a, P10c), restated here because it is the neighbour of the
110
- entry above and readers keep finding it separately.
111
-
112
- argparse printed a usage block around every parse failure and prefixed it `harness: error:`; both
113
- entries state the fault on one line prefixed `geneseed: error:` / `geneseed-hook: error:`. See
114
- `bin/geneseed-cli.mjs`'s `parse` docblock.
115
-
116
- **What used to be gated beside it no longer is.** Every error a command's own body raises was in
117
- the recorded CLI corpus, replayed cell by cell; that corpus was retired on 2026-08-17 and the
118
- per-verb error texts are now covered only where `tests/unit/` happens to drive the verb. The
119
- divergence declared here is unchanged — the port has never reproduced argparse's wording and does
120
- not start now — but the *neighbouring* claim, that the rest of the error surface is pinned byte for
121
- byte, has expired.
122
-
123
- ---
124
-
125
- ## Top-level `--help` is still a refusal on the Node entries
126
-
127
- **Status:** open, out of scope for Task 10, stated so it is not mistaken for done.
128
-
129
- `geneseed --help` with no verb prints
130
- `geneseed: error: the following arguments are required: cmd (one of …)` on stderr and exits 2,
131
- where the reference printed the whole parser's help with a subcommand table. Task 10's surface
132
- was the 26 per-verb help texts and only those are recorded.
133
-
134
- Rendering the top-level help needs argparse's `_iter_indented_subactions` path — the subcommand
135
- choices as a metavar in the usage line, and a nested, differently-indented action list whose
136
- invocations feed the same `_action_max_length` — which is a distinct piece of the formatter with
137
- no fixture behind it. Whoever writes it should record it the same way first.
138
-
139
- ---
140
-
141
- ## `--help` anywhere on the line wins, where argparse would fail on an earlier option
142
-
143
- **Status:** declined (the port's behaviour is kept), and the gate that pins it has been corrected
144
- to stop calling it argparse's rule.
145
-
146
- **What differs.** Both Node entries scan the whole argument list for `-h`/`--help` BEFORE parsing
147
- (`bin/geneseed-cli.mjs`, `bin/geneseed-hook.mjs`), so `geneseed version --target --help` prints
148
- the help and exits 0. argparse consumes optionals left to right, so `--target` swallows `--help`
149
- as its value and the reference exits 2 with `argument --target: expected one argument`. Three
150
- error classes reach it — a missing value, an invalid `type=int` value (`web --port abc -h`), and a
151
- mutex conflict (`theme --solid-only --transparent-only --help`) — across 19 mandatory-value
152
- options on 13 verbs, plus `--memory` and `--root` on the hook entry.
153
-
154
- **Why declined.** On a malformed command line the two answers are "help, exit 0" and "the error,
155
- exit 2". Neither takes an action; nothing is written, nothing is destroyed. A scan that stopped at
156
- the first option expecting a value would have to reproduce argparse's consumption order to be
157
- right, and that order is the piece of the parser this port deliberately does not carry (see
158
- argparse's ERROR wording above). Printing help for a line that contains `--help` is the answer a
159
- user is more likely to have wanted, and it is the same answer for every shape of malformed line.
160
-
161
- **How it stays honest.** `tests/snapshot/cli_help.test.mjs`'s `a --help anywhere in the line wins` used to
162
- docblock this as "argparse's own rule". It is not — that generalised from `diff --nope --help`,
163
- which works only because an UNKNOWN option is deferred to the end of `parse_known_args`. Two of
164
- that test's four cases are cases where the reference errors instead, so the test pins a
165
- divergence; its docblock now says which cases those are and points here.
166
-
167
- ## A verb that postdates the recorder is exempted from the cell matrix by name
168
-
169
- **Status:** ACCEPTED, 2026-08-19. This widens an existing exemption rather than minting a second
170
- one, and it changes a TEST, not the product.
171
-
172
- **What differs.** `tests/unit/hook_cli.test.mjs`'s `the matrix covers every verb each entry
173
- claims` asserted a strict equality between the hook verbs `tests/helpers/matrix/cli.*.json`
174
- covers and the verbs `bin/geneseed-hook.mjs` carries. The CLI half of the same test already
175
- exempted a named `NATIVE` list; the hook half exempted nothing. Both halves now filter through
176
- the same list.
177
-
178
- **Why the recording could not be kept as the whole answer.** The matrix was exported by the
179
- reference implementation, which was deleted on 2026-08-17 along with every recorder that could
180
- write a cell. So the covered set is frozen at four hook verbs for good. A fifth hook verb —
181
- wired in `js/settings.mjs`, carried in `bin/geneseed-hook.mjs`, declared in `js/cli-table.json` —
182
- would fail this equality with **no green path**: the corpus cannot grow, and the failure message
183
- correctly forbids deleting cells to pass. The gate had become a wall, and a wall that stops a
184
- legitimate change is not protection, it is an accident of when the recorder died.
185
-
186
- The reason the three CLI verbs were exempted in the first place — *the reference had nothing to
187
- compare against, and authoring one would be recording a copy of the value under test* — is
188
- exactly the reason a verb that postdates the reference has no cell. Same reason, same exemption.
189
-
190
- **What was lost, measured.** Nothing that another gate does not already hold. The only property
191
- the frozen hook half carried beyond `NATIVE` was *"a hook verb cannot be removed by a coordinated
192
- edit of three files"* — and `tests/unit/hook_cli.test.mjs`'s own literal (`sorted(wired)` against
193
- the four names) plus its `verbsOf(HOOK) === wired` equality are two editable copies of that same
194
- set, twelve lines above. `tests/mutate.mjs`'s M8 plants the one-file removal and is killed there,
195
- not here.
196
-
197
- **How it stays honest.** The exemption is a NAMED list, not a containment: a new verb has to be
198
- written into `NATIVE` deliberately, in two files that read each other's source, and the reverse
199
- check refuses an exemption for a verb neither entry point carries. So the next new verb still
200
- fails loudly until someone decides about it — which is the whole point of the original design.