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.
- package/CHANGELOG.md +123 -1
- package/DESIGN.md +15 -15
- package/README.md +1 -1
- package/SETUP.md +1 -1
- package/SHIPPED.md +1 -1
- package/bin/{geneseed.mjs → build-driver.mjs} +39 -42
- package/bin/geneseed-cli.mjs +33 -32
- package/bin/geneseed-hook.mjs +7 -7
- package/docs/design-history.md +40 -40
- package/docs/extending.md +111 -83
- package/docs/web/exclusions.md +1 -1
- package/docs/web/footprint.md +1 -1
- package/docs/web/foreman-mode.md +2 -2
- package/docs/web/themes-author.md +1 -1
- package/docs/web-ui.md +41 -29
- package/harness.config.json +1 -1
- package/js/README.md +203 -0
- package/js/build/bundle.mjs +231 -0
- package/js/{catalog.mjs → build/catalog.mjs} +7 -7
- package/js/build/emit-claude.mjs +287 -0
- package/js/build/emit-common.mjs +255 -0
- package/js/build/emit-opencode.mjs +214 -0
- package/js/{generate.mjs → build/generate.mjs} +27 -28
- package/js/{render.mjs → build/render.mjs} +13 -9
- package/js/{checkout.mjs → build/source.mjs} +9 -9
- package/js/build/stubs.mjs +256 -0
- package/js/{themes.mjs → build/themes.mjs} +24 -24
- package/js/build/version.mjs +155 -0
- package/js/{hooks.mjs → hosts/hooks.mjs} +47 -44
- package/js/{hosts.mjs → hosts/hosts.mjs} +30 -28
- package/js/{installs.mjs → hosts/installs.mjs} +18 -16
- package/js/{link.mjs → hosts/link.mjs} +29 -28
- package/js/{mcp.mjs → hosts/mcp.mjs} +10 -9
- package/js/{native.mjs → hosts/native.mjs} +24 -22
- package/js/{opencode.mjs → hosts/opencode.mjs} +12 -7
- package/js/{settings.mjs → hosts/settings.mjs} +63 -55
- package/js/inspect/checks-authoring.mjs +761 -0
- package/js/inspect/checks-build.mjs +231 -0
- package/js/inspect/checks-repo.mjs +308 -0
- package/js/{diff.mjs → inspect/diff.mjs} +34 -31
- package/js/inspect/doctor.mjs +286 -0
- package/js/{excludes.mjs → inspect/excludes.mjs} +30 -33
- package/js/{inventory.mjs → inspect/inventory.mjs} +20 -17
- package/js/{registry.mjs → inspect/registry.mjs} +12 -10
- package/js/inspect/scan.mjs +125 -0
- package/js/{status.mjs → inspect/status.mjs} +27 -25
- package/js/inspect/validate.mjs +199 -0
- package/js/lib/fs.mjs +37 -759
- package/js/lib/json.mjs +344 -0
- package/js/lib/paths.mjs +242 -0
- package/js/lib/proc.mjs +3 -3
- package/js/lib/text.mjs +181 -0
- package/js/lib/udiff.mjs +1 -1
- package/js/{memory.mjs → maintain/memory.mjs} +10 -10
- package/js/{migrate.mjs → maintain/migrate.mjs} +28 -26
- package/js/{setup.mjs → maintain/setup.mjs} +34 -32
- package/js/{uninstall.mjs → maintain/uninstall.mjs} +55 -56
- package/js/{update.mjs → maintain/update.mjs} +51 -51
- package/js/{anim.mjs → ui/anim.mjs} +30 -28
- package/js/{cli.mjs → ui/cli.mjs} +10 -10
- package/js/{menu.mjs → ui/menu.mjs} +10 -9
- package/js/{tui.mjs → ui/tui.mjs} +36 -34
- package/js/web/actions.mjs +84 -85
- package/js/web/activity.mjs +22 -21
- package/js/web/api.mjs +154 -51
- package/js/web/daemon.mjs +319 -0
- package/js/web/docs.mjs +24 -21
- package/js/web/graph.mjs +0 -0
- package/js/web/handler.mjs +442 -0
- package/js/web/jobs.mjs +21 -19
- package/js/web/routes.mjs +245 -0
- package/js/web/server.mjs +56 -987
- package/package.json +11 -3
- package/src/doctrines/README.md +1 -1
- package/themes/opencode/README.md +1 -1
- package/web/dist/assets/{Activity-D_sYBlW-.js → Activity-B49fE_ug.js} +1 -1
- package/web/dist/assets/{ActivityDetail-BomKcMhy.js → ActivityDetail-abt7hzD2.js} +1 -1
- package/web/dist/assets/{Diff-BSvi1WUg.js → Diff-D3_bVkih.js} +1 -1
- package/web/dist/assets/{Doctor-Cz1s-y1n.js → Doctor-hXp7srsx.js} +1 -1
- package/web/dist/assets/{Graph-CuntzFJ0.js → Graph-DvYMtlvl.js} +1 -1
- package/web/dist/assets/Harness-BIWei7N5.js +1 -0
- package/web/dist/assets/Laws-CxmnRdwt.js +3 -0
- package/web/dist/assets/{Library-DXhTN9ju.js → Library-MjbOkHIW.js} +1 -1
- package/web/dist/assets/{Markdown-BoiZcdUl.js → Markdown-Dx_w0faR.js} +1 -1
- package/web/dist/assets/{Profile-DfCHlZ03.js → Profile-BRFvv2gr.js} +1 -1
- package/web/dist/assets/{Rules-eND7hJv9.js → Rules-CfsV1EVy.js} +1 -1
- package/web/dist/assets/{Skills-CDl9opZO.js → Skills-DArA6IHS.js} +1 -1
- package/web/dist/assets/{StatusBadge-DSDtcArX.js → StatusBadge-C9bZOEZk.js} +1 -1
- package/web/dist/assets/{activity-Dmb0FTAx.js → activity-Cc0n2jwx.js} +1 -1
- package/web/dist/assets/index-Bx3oxZ1o.css +1 -0
- package/web/dist/assets/index-DC3-GlCY.js +1 -0
- package/web/dist/assets/index-DP035wPa.js +12 -0
- package/web/dist/assets/{index-Dg58ScBe.js → index-dHi-7M-F.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +5 -19
- package/docs/declined.md +0 -200
- package/docs/limits.md +0 -329
- package/js/doctor.mjs +0 -1750
- package/js/emit.mjs +0 -1411
- package/web/dist/assets/About-DyK7yRy7.js +0 -1
- package/web/dist/assets/Harnesses-DgRyOviS.js +0 -1
- package/web/dist/assets/Laws-CWk3Mpri.js +0 -3
- package/web/dist/assets/Themes-CoAWuIeM.js +0 -2
- package/web/dist/assets/index-B-mXquBZ.css +0 -1
- package/web/dist/assets/index-CH_Z725C.js +0 -12
- package/web/dist/assets/index-jrY4m9jP.js +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
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};
|
package/web/dist/index.html
CHANGED
|
@@ -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-
|
|
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-
|
|
16
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bx3oxZ1o.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<div id="root"></div>
|
package/web/src/pages/Laws.jsx
CHANGED
|
@@ -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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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.
|