@spec-engine/spec-engine 0.8.1 → 0.9.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 (41) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/AGENTS.md +678 -0
  3. package/dist/agent/mcp.json +11 -0
  4. package/dist/agent/pre-commit +3 -0
  5. package/dist/agent/skills/req-author/SKILL.md +102 -0
  6. package/dist/docs/agent-reference/index.html +1280 -33
  7. package/dist/docs/architecture/index.html +6 -6
  8. package/dist/docs/checks/index.html +19 -266
  9. package/dist/docs/commands/index.html +32 -164
  10. package/dist/docs/format/index.html +2 -9
  11. package/dist/docs/getting-started/index.html +3 -3
  12. package/dist/docs/guides/platform-setup/index.html +2 -2
  13. package/dist/docs/pagefind/fragment/en_10e3da6.pf_fragment +0 -0
  14. package/dist/docs/pagefind/fragment/en_37cae5e.pf_fragment +0 -0
  15. package/dist/docs/pagefind/fragment/en_7fc2fcd.pf_fragment +0 -0
  16. package/dist/docs/pagefind/fragment/en_96d3bc1.pf_fragment +0 -0
  17. package/dist/docs/pagefind/fragment/en_a26aa07.pf_fragment +0 -0
  18. package/dist/docs/pagefind/fragment/en_b7e68ef.pf_fragment +0 -0
  19. package/dist/docs/pagefind/fragment/en_d8a5bcd.pf_fragment +0 -0
  20. package/dist/docs/pagefind/fragment/en_de67937.pf_fragment +0 -0
  21. package/dist/docs/pagefind/fragment/en_fc3c55b.pf_fragment +0 -0
  22. package/dist/docs/pagefind/index/en_1664b45.pf_index +0 -0
  23. package/dist/docs/pagefind/index/en_a8b8bb6.pf_index +0 -0
  24. package/dist/docs/pagefind/pagefind-entry.json +1 -1
  25. package/dist/docs/pagefind/pagefind.en_bc3d4a7766.pf_meta +0 -0
  26. package/dist/docs/platform-mapping/index.html +2 -1
  27. package/dist/docs/tags/index.html +9 -92
  28. package/dist/impl.js +1948 -653
  29. package/package.json +1 -1
  30. package/dist/docs/pagefind/fragment/en_1f98c2d.pf_fragment +0 -0
  31. package/dist/docs/pagefind/fragment/en_24f76b6.pf_fragment +0 -0
  32. package/dist/docs/pagefind/fragment/en_2ee687a.pf_fragment +0 -0
  33. package/dist/docs/pagefind/fragment/en_4bd9de6.pf_fragment +0 -0
  34. package/dist/docs/pagefind/fragment/en_9cc1878.pf_fragment +0 -0
  35. package/dist/docs/pagefind/fragment/en_9cfc306.pf_fragment +0 -0
  36. package/dist/docs/pagefind/fragment/en_b81dc52.pf_fragment +0 -0
  37. package/dist/docs/pagefind/fragment/en_c3abf3d.pf_fragment +0 -0
  38. package/dist/docs/pagefind/fragment/en_c3e92c6.pf_fragment +0 -0
  39. package/dist/docs/pagefind/index/en_59c3abf.pf_index +0 -0
  40. package/dist/docs/pagefind/index/en_a630d23.pf_index +0 -0
  41. package/dist/docs/pagefind/pagefind.en_ced49c317e.pf_meta +0 -0
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-kmpt7d6z"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Agent Reference | Spec Engine</title><link rel="canonical" href="https://docs.spec-engine.dev/agent-reference/"/><link rel="sitemap" href="/sitemap-index.xml"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="true"/><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Archivo:wght@600;700;800;900&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap"/><link rel="icon" href="/favicon.ico" sizes="any"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png"/><link rel="apple-touch-icon" sizes="180x180" href="/favicon-180.png"/><link rel="manifest" href="/site.webmanifest"/><link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml"/><meta name="generator" content="Astro v7.2.8"/><meta name="generator" content="Starlight v0.40.0"/><meta property="og:title" content="Agent Reference"/><meta property="og:type" content="article"/><meta property="og:url" content="https://docs.spec-engine.dev/agent-reference/"/><meta property="og:locale" content="en"/><meta property="og:description" content="Machine-oriented reference for coding agents driving the spec CLI"/><meta property="og:site_name" content="Spec Engine"/><meta name="twitter:card" content="summary_large_image"/><meta name="description" content="Machine-oriented reference for coding agents driving the spec CLI"/><meta name="theme-color" content="#0F1214"/><meta property="og:image" content="https://docs.spec-engine.dev/og-spec-engine-docs.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta name="twitter:image" content="https://docs.spec-engine.dev/og-spec-engine-docs.png"/><script>
1
+ <!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-kmpt7d6z"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Agent Reference | Spec Engine</title><link rel="canonical" href="https://docs.spec-engine.dev/agent-reference/"/><link rel="sitemap" href="/sitemap-index.xml"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="true"/><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Archivo:wght@600;700;800;900&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap"/><link rel="icon" href="/favicon.ico" sizes="any"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png"/><link rel="apple-touch-icon" sizes="180x180" href="/favicon-180.png"/><link rel="manifest" href="/site.webmanifest"/><link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml"/><meta name="generator" content="Astro v7.2.8"/><meta name="generator" content="Starlight v0.40.0"/><meta property="og:title" content="Agent Reference"/><meta property="og:type" content="article"/><meta property="og:url" content="https://docs.spec-engine.dev/agent-reference/"/><meta property="og:locale" content="en"/><meta property="og:description" content="The model, the authoring standard, and the exact contract of the spec CLI, for coding agents and the people who direct them"/><meta property="og:site_name" content="Spec Engine"/><meta name="twitter:card" content="summary_large_image"/><meta name="description" content="The model, the authoring standard, and the exact contract of the spec CLI, for coding agents and the people who direct them"/><meta name="theme-color" content="#0F1214"/><meta property="og:image" content="https://docs.spec-engine.dev/og-spec-engine-docs.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta name="twitter:image" content="https://docs.spec-engine.dev/og-spec-engine-docs.png"/><script>
2
2
  window.StarlightThemeProvider = (() => {
3
3
  const storedTheme =
4
4
  typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
@@ -70,41 +70,1288 @@
70
70
  })();
71
71
  </script></sl-sidebar-state-persist><div class="md:sl-hidden"><div class="mobile-preferences sl-flex astro-hclhd3l6"><div class="social-icons astro-hclhd3l6"><a href="https://github.com/spec-engine/spec-engine" rel="me" class="sl-flex astro-n6yxnlqb"><span class="sr-only astro-n6yxnlqb">GitHub</span><svg aria-hidden="true" class="astro-n6yxnlqb astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.83-.26.83-.57L9 21.07c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.08-.74.09-.73.09-.73 1.2.09 1.83 1.24 1.83 1.24 1.08 1.83 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.14-.3-.54-1.52.1-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.64 1.66.24 2.88.12 3.18a4.65 4.65 0 0 1 1.23 3.22c0 4.61-2.8 5.63-5.48 5.92.42.36.81 1.1.81 2.22l-.01 3.29c0 .31.2.69.82.57A12 12 0 0 0 12 .3Z"/></svg></a></div><starlight-theme-select><label style="--sl-select-width: 6.25em" class="astro-bqlrlpix"><span class="sr-only astro-bqlrlpix">Select theme</span><svg aria-hidden="true" class="icon label-icon astro-bqlrlpix astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg><select autocomplete="off" class="astro-bqlrlpix"><option value="dark" class="astro-bqlrlpix">Dark</option><option value="light" class="astro-bqlrlpix">Light</option><option value="auto" selected class="astro-bqlrlpix">Auto</option></select><svg aria-hidden="true" class="icon caret astro-bqlrlpix astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg></label></starlight-theme-select><script>
72
72
  StarlightThemeProvider.updatePickers();
73
- </script></div></div></div></div></nav><div class="main-frame astro-qobrlvtk"><script type="module">var e=document.getElementById(`starlight__sidebar`),t=e?.querySelector(`sl-sidebar-state-persist`),n=`sl-sidebar-state`,r=()=>{let r=[],i=t?.dataset.hash||``;try{let e=sessionStorage.getItem(n),t=JSON.parse(e||`{}`);Array.isArray(t.open)&&t.hash===i&&(r=t.open)}catch{}return{hash:i,open:r,scroll:e?.scrollTop||0}},i=e=>{try{sessionStorage.setItem(n,JSON.stringify(e))}catch{}},a=()=>i(r()),o=(e,t)=>{let n=r();n.open[t]=e,i(n)};t?.addEventListener(`click`,e=>{if(!(e.target instanceof Element))return;let t=e.target.closest(`summary`)?.closest(`details`);if(!t)return;let n=t.querySelector(`sl-sidebar-restore`),r=parseInt(n?.dataset.index||``);isNaN(r)||o(!t.open,r)}),addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&a()}),addEventListener(`pageHide`,a);</script><div class="lg:sl-flex astro-iv7ihhtf"><aside class="right-sidebar-container print:hidden astro-iv7ihhtf"><div class="right-sidebar astro-iv7ihhtf"><div class="lg:sl-hidden astro-dfaa6j3s"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-hhhdejk6"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-hhhdejk6"><details id="starlight__mobile-toc" class="astro-hhhdejk6"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-hhhdejk6"><span class="toggle sl-flex astro-hhhdejk6">On this page<svg aria-hidden="true" class="caret astro-hhhdejk6 astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-hhhdejk6"></span></summary><div class="dropdown astro-hhhdejk6"><ul class="isMobile astro-l6rvz6dz" style="--depth: 0;"><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#_top" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Overview</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#mental-model" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Mental model</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#invocation" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Invocation</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#the-agent-loop" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">The agent loop</span></a><ul class="isMobile astro-l6rvz6dz" style="--depth: 1;"><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#route--before-changing-code" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Route — before changing code</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#tag--while-implementing" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Tag — while implementing</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#check--before-finishing" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Check — before finishing</span></a></li></ul></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#path-rules-for-spec-resolve" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Path rules for spec resolve</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#gotchas" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Gotchas</span></a></li></ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-dfaa6j3s"><div class="sl-container astro-dfaa6j3s"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-l6rvz6dz" style="--depth: 0;"><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#_top" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Overview</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#mental-model" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Mental model</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#invocation" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Invocation</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#the-agent-loop" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">The agent loop</span></a><ul class="astro-l6rvz6dz" style="--depth: 1;"><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#route--before-changing-code" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Route — before changing code</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#tag--while-implementing" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Tag — while implementing</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#check--before-finishing" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Check — before finishing</span></a></li></ul></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#path-rules-for-spec-resolve" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Path rules for spec resolve</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#gotchas" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Gotchas</span></a></li></ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js"></script></div></div></div></aside><div class="main-pane astro-iv7ihhtf"><main data-pagefind-body class="astro-kmpt7d6z" lang="en" dir="ltr"><div class="content-panel astro-d3w5v5qg"><div class="sl-container astro-d3w5v5qg"><h1 id="_top" class="astro-elp5pbgd">Agent Reference</h1></div></div><div class="content-panel astro-d3w5v5qg"><div class="sl-container astro-d3w5v5qg"><div class="sl-markdown-content"><p>Machine-oriented reference for coding agents. For narrative and rationale, see the other docs sections.</p>
74
- <div class="sl-heading-wrapper level-h2"><h2 id="mental-model">Mental model</h2><a class="sl-anchor-link" href="#mental-model"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Mental model”</span></a></div>
75
- <ul>
76
- <li>Canonical truth: <code dir="auto">spec-engine/&lt;DOMAIN&gt;/SPEC.json</code> in git, one shared schema on every surface. Ids (<code dir="auto">KEY-NNN</code>) are permanent; changes supersede, never overwrite.</li>
77
- <li>Code binds with <code dir="auto">// @spec KEY-NNN</code>. A test tag may add <code dir="auto">unit</code> | <code dir="auto">integration</code> | <code dir="auto">e2e</code>.</li>
78
- <li>Tag kind is path-derived: implementation code implements, a test path verifies.</li>
79
- <li>The index at <code dir="auto">&lt;platformDir&gt;/.spec-engine/index.sqlite</code> is derived and disposable.</li>
80
- <li>Coverage, drift, and propagation are SQL projections over tags. Drift compares a member’s pin (<code dir="auto">spec-engine@N</code>) against each referenced requirement’s <code dir="auto">changed_at_version</code>; the platform version is the max domain version. Propagation rows come in dependency order, providers before consumers, from platform-map’s <code dir="auto">dependsOn</code>.</li>
81
- </ul>
82
- <p>The repo’s <code dir="auto">AGENTS.md</code> is the canonical copy of this page, with every command’s flags and <code dir="auto">--json</code> shape.</p>
83
- <div class="sl-heading-wrapper level-h2"><h2 id="invocation">Invocation</h2><a class="sl-anchor-link" href="#invocation"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Invocation”</span></a></div>
84
- <div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.v4551.css"><script type="module" src="/_astro/ec.0vx5m.js"></script><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">bunx</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">@spec-engine/spec-engine</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">&lt;command&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> [...] </span><span style="--0:#919F9F;--1:#5F636F"># published npm package (requires Bun)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">&lt;command&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> [...] </span><span style="--0:#919F9F;--1:#5F636F"># after: bun add -g @spec-engine/spec-engine</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">bun</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">packages/engine/src/cli.ts</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">&lt;command&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> [...] </span><span style="--0:#919F9F;--1:#5F636F"># from a checkout</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">./dist/spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">&lt;command&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> [...] </span><span style="--0:#919F9F;--1:#5F636F"># compiled binary</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="bunx @spec-engine/spec-engine <command> [...] # published npm package (requires Bun)spec <command> [...] # after: bun add -g @spec-engine/spec-enginebun packages/engine/src/cli.ts <command> [...] # from a checkout./dist/spec <command> [...] # compiled binary"><div></div></button></div></figure></div>
85
- <ul>
86
- <li>Almost every command takes an optional trailing <code dir="auto">platformDir</code> positional (default: cwd)</li>
87
- <li>All commands are non-interactive when stdin is not a TTY</li>
88
- <li><code dir="auto">--json</code> output → <strong>stdout only</strong>; guidance, warnings, diagnostics → <strong>stderr</strong></li>
89
- </ul>
90
- <div class="sl-heading-wrapper level-h2"><h2 id="the-agent-loop">The agent loop</h2><a class="sl-anchor-link" href="#the-agent-loop"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “The agent loop”</span></a></div>
91
- <div class="sl-heading-wrapper level-h3"><h3 id="route--before-changing-code">Route — before changing code</h3><a class="sl-anchor-link" href="#route--before-changing-code"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Route — before changing code”</span></a></div>
92
- <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">query</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">renewal charge</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">resolve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">src/billing/renew.ts</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="spec query &quot;renewal charge&quot; . --jsonspec resolve src/billing/renew.ts . --json"><div></div></button></div></figure></div>
93
- <div class="sl-heading-wrapper level-h3"><h3 id="tag--while-implementing">Tag — while implementing</h3><a class="sl-anchor-link" href="#tag--while-implementing"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Tag — while implementing”</span></a></div>
73
+ </script></div></div></div></div></nav><div class="main-frame astro-qobrlvtk"><script type="module">var e=document.getElementById(`starlight__sidebar`),t=e?.querySelector(`sl-sidebar-state-persist`),n=`sl-sidebar-state`,r=()=>{let r=[],i=t?.dataset.hash||``;try{let e=sessionStorage.getItem(n),t=JSON.parse(e||`{}`);Array.isArray(t.open)&&t.hash===i&&(r=t.open)}catch{}return{hash:i,open:r,scroll:e?.scrollTop||0}},i=e=>{try{sessionStorage.setItem(n,JSON.stringify(e))}catch{}},a=()=>i(r()),o=(e,t)=>{let n=r();n.open[t]=e,i(n)};t?.addEventListener(`click`,e=>{if(!(e.target instanceof Element))return;let t=e.target.closest(`summary`)?.closest(`details`);if(!t)return;let n=t.querySelector(`sl-sidebar-restore`),r=parseInt(n?.dataset.index||``);isNaN(r)||o(!t.open,r)}),addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&a()}),addEventListener(`pageHide`,a);</script><div class="lg:sl-flex astro-iv7ihhtf"><aside class="right-sidebar-container print:hidden astro-iv7ihhtf"><div class="right-sidebar astro-iv7ihhtf"><div class="lg:sl-hidden astro-dfaa6j3s"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-hhhdejk6"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-hhhdejk6"><details id="starlight__mobile-toc" class="astro-hhhdejk6"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-hhhdejk6"><span class="toggle sl-flex astro-hhhdejk6">On this page<svg aria-hidden="true" class="caret astro-hhhdejk6 astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-hhhdejk6"></span></summary><div class="dropdown astro-hhhdejk6"><ul class="isMobile astro-l6rvz6dz" style="--depth: 0;"><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#_top" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Overview</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#what-spec-engine-is" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">What Spec Engine is</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#the-loop" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">The loop</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#what-to-tag" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">What to tag</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#in-ci" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">In CI</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#requirement-authoring-standard" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Requirement authoring standard</span></a><ul class="isMobile astro-l6rvz6dz" style="--depth: 1;"><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#requirement-id-format" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Requirement id format</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#the-one-promise-lint" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">The one-promise lint</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#the-statement-rules" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">The statement rules</span></a></li></ul></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#authoring-requirements-brief--mint" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Authoring requirements (brief → mint)</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#command-reference" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Command reference</span></a><ul class="isMobile astro-l6rvz6dz" style="--depth: 1;"><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#invocation" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Invocation</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#platforms-members-and-pins" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Platforms, members, and pins</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#tags" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Tags</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#exit-code-contract" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Exit code contract</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#commands" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Commands</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-index" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec index</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-check" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec check</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-guard" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec guard</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-map" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec map</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-get" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec get</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-list" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec list</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-query" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec query</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-relations" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec relations</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-provenance" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec provenance</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-resolve" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec resolve</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-propagation" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec propagation</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-gate" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec gate</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-init" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec init</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-domain" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec domain</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-migrate" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec migrate</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-req" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec req</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-term" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec term</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-glossary" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec glossary</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-deprecate" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec deprecate</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-supersede" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec supersede</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-move" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec move</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-amend" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec amend</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-accept" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec accept</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-lint" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec lint</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-serve" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec serve</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-docs" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec docs</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-mcp" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec mcp</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#gotchas" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Gotchas</span></a></li></ul></li></ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-dfaa6j3s"><div class="sl-container astro-dfaa6j3s"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-l6rvz6dz" style="--depth: 0;"><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#_top" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Overview</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#what-spec-engine-is" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">What Spec Engine is</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#the-loop" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">The loop</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#what-to-tag" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">What to tag</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#in-ci" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">In CI</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#requirement-authoring-standard" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Requirement authoring standard</span></a><ul class="astro-l6rvz6dz" style="--depth: 1;"><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#requirement-id-format" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Requirement id format</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#the-one-promise-lint" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">The one-promise lint</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#the-statement-rules" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">The statement rules</span></a></li></ul></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#authoring-requirements-brief--mint" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Authoring requirements (brief → mint)</span></a></li><li style="--depth: 0;" class="astro-l6rvz6dz"><a href="#command-reference" style="--depth: 0;" class="astro-l6rvz6dz"><span style="--depth: 0;" class="astro-l6rvz6dz">Command reference</span></a><ul class="astro-l6rvz6dz" style="--depth: 1;"><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#invocation" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Invocation</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#platforms-members-and-pins" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Platforms, members, and pins</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#tags" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Tags</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#exit-code-contract" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Exit code contract</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#commands" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Commands</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-index" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec index</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-check" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec check</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-guard" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec guard</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-map" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec map</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-get" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec get</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-list" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec list</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-query" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec query</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-relations" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec relations</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-provenance" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec provenance</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-resolve" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec resolve</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-propagation" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec propagation</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-gate" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec gate</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-init" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec init</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-domain" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec domain</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-migrate" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec migrate</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-req" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec req</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-term" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec term</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-glossary" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec glossary</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-deprecate" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec deprecate</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-supersede" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec supersede</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-move" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec move</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-amend" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec amend</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-accept" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec accept</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-lint" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec lint</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-serve" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec serve</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-docs" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec docs</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#spec-mcp" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">spec mcp</span></a></li><li style="--depth: 1;" class="astro-l6rvz6dz"><a href="#gotchas" style="--depth: 1;" class="astro-l6rvz6dz"><span style="--depth: 1;" class="astro-l6rvz6dz">Gotchas</span></a></li></ul></li></ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js"></script></div></div></div></aside><div class="main-pane astro-iv7ihhtf"><main data-pagefind-body class="astro-kmpt7d6z" lang="en" dir="ltr"><div class="content-panel astro-d3w5v5qg"><div class="sl-container astro-d3w5v5qg"><h1 id="_top" class="astro-elp5pbgd">Agent Reference</h1></div></div><div class="content-panel astro-d3w5v5qg"><div class="sl-container astro-d3w5v5qg"><div class="sl-markdown-content"><p>The reference for a coding agent working in a repository that uses Spec Engine. It has three parts: the model and the working loop, the standard every requirement is written against, and the exact contract of every <code dir="auto">spec</code> command.</p>
74
+ <div class="sl-heading-wrapper level-h2"><h2 id="what-spec-engine-is">What Spec Engine is</h2><a class="sl-anchor-link" href="#what-spec-engine-is"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “What Spec Engine is”</span></a></div>
75
+ <p>Spec Engine helps developers, product teams, and agents write better specs and keep them current, so the software changes accurately.</p>
76
+ <p>It works with four things:</p>
77
+ <table>
78
+ <thead>
79
+ <tr>
80
+ <th>Noun</th>
81
+ <th>What it is</th>
82
+ <th>Where it lives</th>
83
+ <th>Authored or derived</th>
84
+ </tr>
85
+ </thead>
86
+ <tbody>
87
+ <tr>
88
+ <td>Domain</td>
89
+ <td>A named subject area of requirements with an UPPERCASE key (<code dir="auto">BILLING</code>), an owner, and a charter that says what belongs in it</td>
90
+ <td><code dir="auto">spec-engine/&lt;KEY&gt;/SPEC.json</code>, one file per domain</td>
91
+ <td>Authored</td>
92
+ </tr>
93
+ <tr>
94
+ <td>Requirement</td>
95
+ <td>One durable rule of the product’s behavior, addressed by a permanent id <code dir="auto">KEY-NNN</code> (<code dir="auto">BILLING-009</code>), with a statement, a why, and a status</td>
96
+ <td>An entry in its domain’s <code dir="auto">SPEC.json</code></td>
97
+ <td>Authored</td>
98
+ </tr>
99
+ <tr>
100
+ <td>Tag</td>
101
+ <td>A comment <code dir="auto">@spec KEY-NNN</code> that binds a line of code or a test to a requirement</td>
102
+ <td>Source and test files, in comments</td>
103
+ <td>Authored in code</td>
104
+ </tr>
105
+ <tr>
106
+ <td>Index</td>
107
+ <td>A SQLite database of every domain, requirement, and tag, which the read and check commands query</td>
108
+ <td><code dir="auto">.spec-engine/index.sqlite</code> at the platform root</td>
109
+ <td>Derived</td>
110
+ </tr>
111
+ </tbody>
112
+ </table>
113
+ <ul>
114
+ <li>The <code dir="auto">SPEC.json</code> files are the only authored truth about what the product promises. Write them through the <code dir="auto">spec</code> commands. An id is never reused or renumbered: a changed promise is superseded by a new id, and an ended one is deprecated.</li>
115
+ <li>Tags in code are the only truth about where a requirement is implemented and where it is verified. A tag in implementation code implements; a tag in a test file verifies. The kind comes from the file path, never from the tag text.</li>
116
+ <li>The index is derived and disposable. Every command rebuilds it from the <code dir="auto">SPEC.json</code> files and the tags, and deleting <code dir="auto">.spec-engine/</code> is always safe.</li>
117
+ <li>Requirements are not tickets. One ticket usually becomes several requirements. A ticket id is provenance only: it goes in <code dir="auto">--issue</code>, never in a requirement id or a tag.</li>
118
+ </ul>
119
+ <div class="sl-heading-wrapper level-h2"><h2 id="the-loop">The loop</h2><a class="sl-anchor-link" href="#the-loop"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “The loop”</span></a></div>
120
+ <div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.v4551.css"><script type="module" src="/_astro/ec.0vx5m.js"></script><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">query</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">renewal charge</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># 1. route: requirements on the topic</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">resolve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">src/billing/renew.ts</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># or the ones tagged in a file</span></div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># 2. implement the change</span></div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># 3. tag the function that produces the behavior and the test that proves it</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">check</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--ci</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># 4. exit 1: fix before the PR</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">guard</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># 5. exit 1: the change loses a requirement</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="spec query &quot;renewal charge&quot; . --json # 1. route: requirements on the topicspec resolve src/billing/renew.ts . --json # or the ones tagged in a filespec check . --ci --json # 4. exit 1: fix before the PRspec guard . --json # 5. exit 1: the change loses a requirement"><div></div></button></div></figure></div>
121
+ <p>When <code dir="auto">spec guard</code> blocks, relay its <code dir="auto">🛑 spec-guard:</code> block verbatim to the user. Then either <code dir="auto">spec supersede</code> the requirement with a successor or get the user’s explicit decision to <code dir="auto">spec deprecate</code> it. Never delete a requirement to make the gate pass.</p>
122
+ <div class="sl-heading-wrapper level-h2"><h2 id="what-to-tag">What to tag</h2><a class="sl-anchor-link" href="#what-to-tag"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “What to tag”</span></a></div>
94
123
  <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="ts"><code><div class="ec-line"><div class="code"><span style="--0:#C792EA;--1:#8844AE">export</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#C792EA;--1:#8844AE">function</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">renew</span><span style="--0:#D9F5DD;--1:#111111">()</span><span style="--0:#D6DEEB;--1:#403F53"> { </span><span style="--0:#919F9F;--1:#5F636F">/* … */</span><span style="--0:#D6DEEB;--1:#403F53"> } </span><span style="--0:#919F9F;--1:#5D6376">// @spec BILLING-009</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">it</span><span style="--0:#D6DEEB;--1:#403F53">(</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">charges current price</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#D9F5DD;--1:#111111">()</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#C792EA;--1:#8844AE">=&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> {}) </span><span style="--0:#919F9F;--1:#5D6376">// @spec BILLING-009 unit</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="export function renew() { /* … */ } // @spec BILLING-009it(&quot;charges current price&quot;, () => {}) // @spec BILLING-009 unit"><div></div></button></div></figure></div>
95
- <div class="sl-heading-wrapper level-h3"><h3 id="check--before-finishing">Check — before finishing</h3><a class="sl-anchor-link" href="#check--before-finishing"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Check — before finishing”</span></a></div>
96
- <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">echo</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">|</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">req</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">bil</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># next unused BILLING id</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">index</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">check</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--ci</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># exit 1 → fix before PR</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">guard</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--json</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># exit 1 → this change deletes a live requirement</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="echo | spec req bil # next unused BILLING idspec index . --jsonspec check . --ci --json # exit 1 → fix before PRspec guard . --json # exit 1 → this change deletes a live requirement"><div></div></button></div></figure></div>
97
- <div class="sl-heading-wrapper level-h2"><h2 id="path-rules-for-spec-resolve">Path rules for <code dir="auto">spec resolve</code></h2><a class="sl-anchor-link" href="#path-rules-for-spec-resolve"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Path rules for spec resolve”</span></a></div>
98
124
  <ul>
99
- <li><strong>Multi-repo platform:</strong> pass <code dir="auto">&lt;repo&gt;/&lt;path&gt;</code> — e.g., <code dir="auto">spec resolve api/src/renew.ts .</code></li>
100
- <li><strong>Single-repo (rung-1):</strong> pass the path from the platform root (<code dir="auto">src/orders.ts</code>, <code dir="auto">.github/workflows/ci.yml</code>); a lone repository’s files carry no directory-name prefix</li>
101
- <li>Absolute paths normalized against <code dir="auto">platformDir</code>; paths outside it exit 2</li>
125
+ <li>A tag binds the one function that produces the behavior and the one test that proves it.</li>
126
+ <li>Helpers, glue code, and configuration carry no tag.</li>
127
+ <li>If you cannot name the behavior a line produces, do not tag it.</li>
128
+ <li>A test tag may end with a level: <code dir="auto">unit</code>, <code dir="auto">integration</code>, or <code dir="auto">e2e</code>.</li>
129
+ </ul>
130
+ <div class="sl-heading-wrapper level-h2"><h2 id="in-ci">In CI</h2><a class="sl-anchor-link" href="#in-ci"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “In CI”</span></a></div>
131
+ <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">check</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--ci</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--base</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">$(</span><span style="--0:#82AAFF;--1:#3B61B0">git</span><span style="--0:#ECC48D;--1:#984E4D"> </span><span style="--0:#ECC48D;--1:#3B61B0">merge-base</span><span style="--0:#ECC48D;--1:#984E4D"> </span><span style="--0:#ECC48D;--1:#3B61B0">HEAD</span><span style="--0:#ECC48D;--1:#984E4D"> </span><span style="--0:#ECC48D;--1:#3B61B0">origin/main</span><span style="--0:#ECC48D;--1:#984E4D">)</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="spec check . --ci --base &quot;$(git merge-base HEAD origin/main)&quot;"><div></div></button></div></figure></div>
132
+ <p>Without <code dir="auto">--base</code>, <code dir="auto">check</code> diffs against <code dir="auto">HEAD</code>, which in a CI checkout already holds the change, so the removal and loss checks find nothing to report.</p>
133
+ <div class="sl-heading-wrapper level-h2"><h2 id="requirement-authoring-standard">Requirement authoring standard</h2><a class="sl-anchor-link" href="#requirement-authoring-standard"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Requirement authoring standard”</span></a></div>
134
+ <!-- @spec CHRT-024 -->
135
+ <p>The rules every new requirement is written against. The statement shapes and the statement rules are also checked at write time and by <code dir="auto">spec check</code>.</p>
136
+ <ol start="0">
137
+ <li>
138
+ <p><strong>A requirement is one rule of the product’s behavior, stated from the user’s point of view.</strong> The statement rules below are the mechanical half of this rule: <code dir="auto">spec req</code>, <code dir="auto">amend</code>, <code dir="auto">supersede</code>, <code dir="auto">move</code>, and <code dir="auto">accept</code> refuse a statement that breaks one, <code dir="auto">spec check</code> reports an Active statement that breaks one as <code dir="auto">STATEMENT_RULES</code>, and <code dir="auto">spec lint</code> lists every finding. The judgment half stays with the author.</p>
139
+ </li>
140
+ <li>
141
+ <p><strong>One behavior per requirement.</strong> A ticket usually splits into several requirements. Never mint one requirement per ticket.</p>
142
+ </li>
143
+ <li>
144
+ <p><strong>Write the statement in a fixed shape.</strong> Pick the sentence shape that matches the situation. The word “shall” separates who acts from the observable result:</p>
145
+ <table>
146
+ <thead>
147
+ <tr>
148
+ <th>Situation</th>
149
+ <th>Shape</th>
150
+ <th>Example</th>
151
+ </tr>
152
+ </thead>
153
+ <tbody>
154
+ <tr>
155
+ <td>Always true</td>
156
+ <td>The <code dir="auto">&lt;system&gt;</code> shall <code dir="auto">&lt;result&gt;</code></td>
157
+ <td>The spec CLI shall write JSON output to stdout only.</td>
158
+ </tr>
159
+ <tr>
160
+ <td>Triggered by an event</td>
161
+ <td>When <code dir="auto">&lt;trigger&gt;</code>, the <code dir="auto">&lt;system&gt;</code> shall <code dir="auto">&lt;result&gt;</code></td>
162
+ <td>When an unknown requirement id is passed, spec resolve shall print <code dir="auto">[]</code> and exit 0.</td>
163
+ </tr>
164
+ <tr>
165
+ <td>Only in a certain state</td>
166
+ <td>While <code dir="auto">&lt;state&gt;</code>, the <code dir="auto">&lt;system&gt;</code> shall <code dir="auto">&lt;result&gt;</code></td>
167
+ <td>While the index file is missing, read commands shall rebuild it before answering.</td>
168
+ </tr>
169
+ <tr>
170
+ <td>Handling something going wrong</td>
171
+ <td>If <code dir="auto">&lt;failure&gt;</code>, then the <code dir="auto">&lt;system&gt;</code> shall <code dir="auto">&lt;result&gt;</code></td>
172
+ <td>If the results file cannot be parsed, then spec check shall report the file and exit 2.</td>
173
+ </tr>
174
+ <tr>
175
+ <td>Only when a feature is present</td>
176
+ <td>Where <code dir="auto">&lt;feature&gt;</code>, the <code dir="auto">&lt;system&gt;</code> shall <code dir="auto">&lt;result&gt;</code></td>
177
+ <td>Where a member config lists ignore directories, the scanner shall skip them.</td>
178
+ </tr>
179
+ </tbody>
180
+ </table>
181
+ <p>A statement that fits no row has not yet decided what triggers the behavior or what observably happens.</p>
182
+ <p>A statement may end with a trailing <code dir="auto">so &lt;observable consequence&gt;</code> clause (“…shall report NO_SPEC_CONFIG at warning severity, so a lone warning never fails the gate”) when the consequence is itself observable. The reason the behavior exists belongs in the why, never in the statement.</p>
183
+ <p>One promise per statement. A result that chains two behaviors with <code dir="auto">and</code>, or that sets two severities, is two requirements: a single test must be able to prove the whole statement. Prefer <code dir="auto">When</code>, <code dir="auto">If … then</code>, and the bare <code dir="auto">shall</code> form. Use <code dir="auto">While</code> and <code dir="auto">Where</code> only when a state or a feature is what gates the behavior.</p>
184
+ </li>
185
+ <li>
186
+ <p><strong>It must make sense on its own.</strong> A reader who sees only this one statement, in search results or a report, understands it. A statement that says “the normalization” or “that prefix” without naming the subject fails.</p>
187
+ </li>
188
+ <li>
189
+ <p><strong>Describe what it does, not how.</strong> Say what a user or calling agent observes. If renaming an internal function would make the statement wrong, it is written at the wrong level.</p>
190
+ </li>
191
+ <li>
192
+ <p><strong>No expiration dates.</strong> No “currently”, no “the new behavior”, no references to things that were removed.</p>
193
+ </li>
194
+ <li>
195
+ <p><strong>The why states what breaks.</strong> Answer “what goes wrong if this stops being true”. Never restate the requirement in other words.</p>
196
+ </li>
197
+ <li>
198
+ <p><strong>Three link types, three jobs.</strong> Tests prove it. Code implements it. Tickets are only history: never an id, never proof.</p>
199
+ </li>
200
+ <li>
201
+ <p><strong>File it where the promise lives, and search first.</strong> Pick the domain whose charter covers the behavior, and run <code dir="auto">spec query</code> before minting. If something close exists, amend it instead.</p>
202
+ </li>
203
+ </ol>
204
+ <div class="sl-heading-wrapper level-h3"><h3 id="requirement-id-format">Requirement id format</h3><a class="sl-anchor-link" href="#requirement-id-format"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Requirement id format”</span></a></div>
205
+ <!-- @spec CHRT-023 -->
206
+ <ul>
207
+ <li>Ids are <code dir="auto">KEY-NNN</code>: the domain key, a hyphen, and a three-digit zero-padded number (<code dir="auto">BILLING-010</code>).</li>
208
+ <li>Ids are permanent. A requirement is superseded or deprecated, never renumbered and never deleted.</li>
209
+ </ul>
210
+ <div class="sl-heading-wrapper level-h3"><h3 id="the-one-promise-lint">The one-promise lint</h3><a class="sl-anchor-link" href="#the-one-promise-lint"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “The one-promise lint”</span></a></div>
211
+ <p>A domain envelope may declare <code dir="auto">grammar: "ears"</code> with <code dir="auto">grammarSeverity: "warning" | "error"</code> (default warning). Set both with <code dir="auto">spec domain grammar &lt;KEY&gt;</code>, never by hand. In such a domain a statement must fit one of the five shapes, and a statement that fits a shape still fails when:</p>
212
+ <table>
213
+ <thead>
214
+ <tr>
215
+ <th>Rule</th>
216
+ <th>Fails</th>
217
+ <th>Passes</th>
218
+ </tr>
219
+ </thead>
220
+ <tbody>
221
+ <tr>
222
+ <td>Semicolon clause</td>
223
+ <td><code dir="auto">; </code> anywhere after the first <code dir="auto">shall</code></td>
224
+ <td><code dir="auto">When a; b holds, X shall …</code></td>
225
+ </tr>
226
+ <tr>
227
+ <td>Second subject</td>
228
+ <td><code dir="auto">, and &lt;subject&gt; shall</code> after the first <code dir="auto">shall</code></td>
229
+ <td><code dir="auto">X shall A, and shall B</code></td>
230
+ </tr>
231
+ <tr>
232
+ <td>Rationale</td>
233
+ <td><code dir="auto">because</code>, <code dir="auto">so that</code>, <code dir="auto">in order to</code> anywhere</td>
234
+ <td>a trailing <code dir="auto">, so &lt;observable consequence&gt;</code></td>
235
+ </tr>
236
+ </tbody>
237
+ </table>
238
+ <ul>
239
+ <li><code dir="auto">spec check</code> reports a nonconforming Active or Draft statement as <code dir="auto">STATEMENT_GRAMMAR</code> at the declared severity, one row per statement, the detail naming the first failed shape or rule.</li>
240
+ <li><code dir="auto">spec req</code>, <code dir="auto">amend</code>, <code dir="auto">supersede</code>, and <code dir="auto">move</code> judge new text at write time: they warn and write under warning, and refuse (exit 2) under error.</li>
241
+ <li>The lint is a pattern, not a reading: a statement that passes can still chain two behaviors, and the reviewer still owns that call.</li>
242
+ <li>The TERM domain is exempt.</li>
243
+ </ul>
244
+ <div class="sl-heading-wrapper level-h3"><h3 id="the-statement-rules">The statement rules</h3><a class="sl-anchor-link" href="#the-statement-rules"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “The statement rules”</span></a></div>
245
+ <p>Every domain is judged by these rules, whatever its grammar. <code dir="auto">spec req</code>, <code dir="auto">amend</code>, <code dir="auto">supersede</code>, <code dir="auto">move</code> (with new text), and <code dir="auto">accept</code> refuse a statement that breaks a rule graded <code dir="auto">error</code> (the default): exit 2, one <code dir="auto">&lt;rule&gt;: &lt;detail&gt; — &lt;reason&gt;</code> line per finding, rule 0 last. A rule graded <code dir="auto">warning</code> writes the statement and prints the same finding lines on stderr. There is no override flag; the <code dir="auto">statementRules</code> block of <code dir="auto">check.json</code> grades a rule, tunes it, or turns it off. <code dir="auto">spec check</code> reports an Active statement that breaks one as <code dir="auto">STATEMENT_RULES</code>, one row per rule per statement. The TERM domain is never judged.</p>
246
+ <table>
247
+ <thead>
248
+ <tr>
249
+ <th>Rule</th>
250
+ <th>Reports</th>
251
+ <th>Printed reason</th>
252
+ </tr>
253
+ </thead>
254
+ <tbody>
255
+ <tr>
256
+ <td><code dir="auto">one-sentence</code></td>
257
+ <td>more than one sentence, no closing period, or a line break</td>
258
+ <td>a statement is one testable sentence</td>
259
+ </tr>
260
+ <tr>
261
+ <td><code dir="auto">length</code></td>
262
+ <td>over <code dir="auto">maxLength</code> characters (280 by default)</td>
263
+ <td>a sentence a reader holds at once</td>
264
+ </tr>
265
+ <tr>
266
+ <td><code dir="auto">no-file-path</code></td>
267
+ <td>a repository path or file name</td>
268
+ <td>which file holds what is layout, not behaviour</td>
269
+ </tr>
270
+ <tr>
271
+ <td><code dir="auto">no-test-narration</code></td>
272
+ <td>how a test is written</td>
273
+ <td>how a test is written is not what the product does</td>
274
+ </tr>
275
+ <tr>
276
+ <td><code dir="auto">no-toolchain-layout</code></td>
277
+ <td>CI or tooling layout</td>
278
+ <td>how CI and tooling are arranged is not a product behaviour</td>
279
+ </tr>
280
+ <tr>
281
+ <td><code dir="auto">no-history</code></td>
282
+ <td>how the requirement changed</td>
283
+ <td>a statement says what holds now, not how it got there</td>
284
+ </tr>
285
+ <tr>
286
+ <td><code dir="auto">no-jargon</code></td>
287
+ <td>a word from the jargon list</td>
288
+ <td>a reader without the codebase cannot parse project jargon</td>
289
+ </tr>
290
+ <tr>
291
+ <td><code dir="auto">no-tool-statement</code></td>
292
+ <td>which tool is in use</td>
293
+ <td>naming the tool in use is a decision record, not a requirement</td>
294
+ </tr>
295
+ </tbody>
296
+ </table>
297
+ <div class="sl-heading-wrapper level-h2"><h2 id="authoring-requirements-brief--mint">Authoring requirements (brief → mint)</h2><a class="sl-anchor-link" href="#authoring-requirements-brief--mint"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Authoring requirements (brief → mint)”</span></a></div>
298
+ <!-- @spec AUTHOR-013 -->
299
+ <!-- @spec AUTHOR-012 -->
300
+ <p>The procedure for turning a brief or ticket into requirements. Each draft is written to the requirement authoring standard above.</p>
301
+ <ol>
302
+ <li><strong>Split the brief.</strong> One requirement per behavior, never one per ticket.</li>
303
+ <li><strong>Place each.</strong> <code dir="auto">spec domain list --json</code> prints every charter. File the requirement where the promise lives.</li>
304
+ <li><strong>Dedup.</strong> <code dir="auto">spec query "&lt;phrase&gt;" . --json</code> before minting. Overlap means <code dir="auto">spec amend</code> or relate the existing requirement.</li>
305
+ <li><strong>Draft to the standard.</strong> Every draft quotes the phrase of the brief it derives from.</li>
306
+ <li><strong>Mint.</strong> <code dir="auto">spec req &lt;domain&gt; --text "&lt;statement&gt;" --why "&lt;what breaks&gt;" [--issue &lt;ticket&gt;]</code>.</li>
307
+ <li><strong>Provenance, not identity.</strong> The ticket goes in <code dir="auto">--issue</code>, never in an id or a tag.</li>
308
+ <li><strong>Verify.</strong> <code dir="auto">spec index . &amp;&amp; spec check . --ci</code> (exit 0) and <code dir="auto">spec guard .</code> before the PR.</li>
309
+ </ol>
310
+ <div class="sl-heading-wrapper level-h2"><h2 id="command-reference">Command reference</h2><a class="sl-anchor-link" href="#command-reference"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Command reference”</span></a></div>
311
+ <div class="sl-heading-wrapper level-h3"><h3 id="invocation">Invocation</h3><a class="sl-anchor-link" href="#invocation"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Invocation”</span></a></div>
312
+ <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">&lt;command&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> [...] </span><span style="--0:#919F9F;--1:#5F636F"># after: bun add -g @spec-engine/spec-engine</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">bunx</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">@spec-engine/spec-engine</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">&lt;command&gt;</span><span style="--0:#D6DEEB;--1:#403F53"> [...] </span><span style="--0:#919F9F;--1:#5F636F"># without a global install (requires Bun)</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="spec <command> [...] # after: bun add -g @spec-engine/spec-enginebunx @spec-engine/spec-engine <command> [...] # without a global install (requires Bun)"><div></div></button></div></figure></div>
313
+ <ul>
314
+ <li>Almost every command takes an optional trailing <code dir="auto">platformDir</code> positional (default: cwd). The platform dir is the directory that contains <code dir="auto">spec-engine/</code>.</li>
315
+ <li>Every command is non-interactive when stdin is not a TTY.</li>
316
+ <li><code dir="auto">--json</code> goes to <strong>stdout only</strong>. Guidance, warnings, and diagnostics chrome go to <strong>stderr</strong>. Parse stdout, surface stderr.</li>
317
+ </ul>
318
+ <div class="sl-heading-wrapper level-h3"><h3 id="platforms-members-and-pins">Platforms, members, and pins</h3><a class="sl-anchor-link" href="#platforms-members-and-pins"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Platforms, members, and pins”</span></a></div>
319
+ <ul>
320
+ <li>A platform is the directory holding <code dir="auto">spec-engine/</code>. When declared, its platform file <code dir="auto">platform-map.json</code> is <code dir="auto">{ name, members, ignore }</code>.</li>
321
+ <li>A member is a repository named in that file, carrying a <code dir="auto">{ platform, member }</code> marker. A monorepo’s coverage columns are its workspace packages, named by path (<code dir="auto">packages/engine</code>). A lone repository with no platform file is one column or its packages. A monorepo’s files outside every package are one more column, named by the member or, for a lone monorepo, the directory.</li>
322
+ <li><code dir="auto">spec-engine.member.json</code> adds one fact: the member’s pin, <code dir="auto">spec-engine@N</code>. <code dir="auto">spec init &lt;name&gt;</code> declares and pins in one step.</li>
323
+ <li>A requirement domain’s version is derived from its supersede history. The platform version is the max domain version. Drift compares a member’s pin against each referenced requirement’s <code dir="auto">changed_at_version</code>. A stray <code dir="auto">spec-engine.platform.json</code> is ignored with a warning.</li>
324
+ <li>A member’s <code dir="auto">dependsOn</code> (the platform’s own package names in its manifest) orders <code dir="auto">spec propagation</code>: providers before consumers.</li>
325
+ </ul>
326
+ <div class="sl-heading-wrapper level-h3"><h3 id="tags">Tags</h3><a class="sl-anchor-link" href="#tags"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Tags”</span></a></div>
327
+ <ul>
328
+ <li>The tag is <code dir="auto">@spec KEY-NNN</code>, optionally followed by a level token <code dir="auto">unit</code> | <code dir="auto">integration</code> | <code dir="auto">e2e</code> on a test tag. Never write “implements” or “verifies” in the tag.</li>
329
+ <li>The scanner reads <code dir="auto">.ts</code>, <code dir="auto">.tsx</code>, <code dir="auto">.js</code>, <code dir="auto">.jsx</code>, <code dir="auto">.mjs</code>, <code dir="auto">.sh</code>, <code dir="auto">.lua</code>, <code dir="auto">.luau</code>, <code dir="auto">.go</code>, <code dir="auto">.rs</code>, <code dir="auto">.tf</code>, <code dir="auto">.toml</code>, <code dir="auto">.sql</code>, <code dir="auto">.css</code>, <code dir="auto">.yml</code>, <code dir="auto">.yaml</code>, and <code dir="auto">.astro</code> files, any file named <code dir="auto">Dockerfile</code> or <code dir="auto">Dockerfile.&lt;suffix&gt;</code>, and <code dir="auto">.md</code> documents, whose tags document.</li>
330
+ <li>The tag regex ignores the comment leader, so <code dir="auto">--</code>, <code dir="auto">//</code>, <code dir="auto">#</code>, <code dir="auto">&lt;!-- --&gt;</code>, and <code dir="auto">/* … */</code> all work.</li>
331
+ <li>Dot-prefixed directories are skipped except <code dir="auto">.github/</code>. <code dir="auto">fixtures/</code> directories are ignored everywhere.</li>
332
+ <li>A tag in implementation code implements; a tag in a test path verifies. A test path contains <code dir="auto">.test.</code>, <code dir="auto">.spec.</code>, <code dir="auto">__tests__/</code>, <code dir="auto">/tests/</code>, <code dir="auto">/e2e/</code>, or <code dir="auto">.e2e.</code>, or ends in <code dir="auto">_test.go</code>, <code dir="auto">_test.rs</code>, <code dir="auto">_spec.lua</code>, or <code dir="auto">_test.lua</code>, or lives under <code dir="auto">spec/</code> (busted).</li>
333
+ <li>Rust tests inline under <code dir="auto">#[cfg(test)]</code> sit in the implementation file and count as implements; put a verifying tag in <code dir="auto">tests/</code> or a <code dir="auto">*_test.rs</code> file.</li>
334
+ </ul>
335
+ <div class="sl-heading-wrapper level-h3"><h3 id="exit-code-contract">Exit code contract</h3><a class="sl-anchor-link" href="#exit-code-contract"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Exit code contract”</span></a></div>
336
+ <table>
337
+ <thead>
338
+ <tr>
339
+ <th>Code</th>
340
+ <th>Meaning</th>
341
+ </tr>
342
+ </thead>
343
+ <tbody>
344
+ <tr>
345
+ <td>0</td>
346
+ <td>Success. Empty results are success (<code dir="auto">[]</code> on stdout, guidance on stderr).</td>
347
+ </tr>
348
+ <tr>
349
+ <td>1</td>
350
+ <td>Data-level failure: <code dir="auto">check</code> found an error-severity diagnostic; <code dir="auto">guard</code> found a loss; <code dir="auto">gate</code> failed; <code dir="auto">index</code> crashed mid-build.</td>
351
+ </tr>
352
+ <tr>
353
+ <td>2</td>
354
+ <td>Usage/environment error: bad args, not a Spec Engine platform (no <code dir="auto">spec-engine/</code>), path-containment violation, invalid <code dir="auto">--limit</code>/<code dir="auto">--port</code>, FTS5 syntax error.</td>
355
+ </tr>
356
+ </tbody>
357
+ </table>
358
+ <p>Branch on exit codes, not on output text.</p>
359
+ <div class="sl-heading-wrapper level-h3"><h3 id="commands">Commands</h3><a class="sl-anchor-link" href="#commands"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Commands”</span></a></div>
360
+ <table>
361
+ <thead>
362
+ <tr>
363
+ <th>Command</th>
364
+ <th>Synopsis</th>
365
+ <th><code dir="auto">--json</code></th>
366
+ <th>Exits</th>
367
+ </tr>
368
+ </thead>
369
+ <tbody>
370
+ <tr>
371
+ <td><code dir="auto">spec index [platformDir]</code></td>
372
+ <td>Build or refresh the derived index</td>
373
+ <td>yes (IndexResult)</td>
374
+ <td>0 / 1 / 2</td>
375
+ </tr>
376
+ <tr>
377
+ <td><code dir="auto">spec check [platformDir] [--ci]</code></td>
378
+ <td>Integrity + coverage + drift diagnostics</td>
379
+ <td>yes (array)</td>
380
+ <td>0 / 1 / 2</td>
381
+ </tr>
382
+ <tr>
383
+ <td><code dir="auto">spec guard [platformDir] [--against &lt;ref&gt;]</code></td>
384
+ <td>Block a change about to lose a requirement, its last implementation, or its last test</td>
385
+ <td>yes (array)</td>
386
+ <td>0 / 1 / 2</td>
387
+ </tr>
388
+ <tr>
389
+ <td><code dir="auto">spec map [platformDir]</code></td>
390
+ <td>Requirement × repo coverage matrix</td>
391
+ <td>yes (array)</td>
392
+ <td>0 / 2</td>
393
+ </tr>
394
+ <tr>
395
+ <td><code dir="auto">spec get &lt;KEY-NNN&gt; [platformDir]</code></td>
396
+ <td>One requirement’s full record and its tag sites; unknown id is <code dir="auto">[]</code></td>
397
+ <td>yes (object, or <code dir="auto">[]</code>)</td>
398
+ <td>0 / 2</td>
399
+ </tr>
400
+ <tr>
401
+ <td><code dir="auto">spec list [platformDir] [--domain KEY] [--status S]</code></td>
402
+ <td>Every requirement record in (key, seq) order, history included</td>
403
+ <td>yes (array)</td>
404
+ <td>0 / 2</td>
405
+ </tr>
406
+ <tr>
407
+ <td><code dir="auto">spec query &lt;text&gt; [platformDir]</code></td>
408
+ <td>Full-text retrieval over requirements</td>
409
+ <td>yes (array)</td>
410
+ <td>0 / 2</td>
411
+ </tr>
412
+ <tr>
413
+ <td><code dir="auto">spec relations [platformDir]</code></td>
414
+ <td>Mermaid graph of <code dir="auto">relates</code> links</td>
415
+ <td>yes (array)</td>
416
+ <td>0 / 2</td>
417
+ </tr>
418
+ <tr>
419
+ <td><code dir="auto">spec provenance [issueId] [platformDir]</code></td>
420
+ <td>Per-requirement provenance matrix</td>
421
+ <td>yes (array)</td>
422
+ <td>0 / 2</td>
423
+ </tr>
424
+ <tr>
425
+ <td><code dir="auto">spec resolve &lt;files…&gt; [platformDir]</code></td>
426
+ <td>Requirements tagged in the given files</td>
427
+ <td>yes (array)</td>
428
+ <td>0 / 2</td>
429
+ </tr>
430
+ <tr>
431
+ <td><code dir="auto">spec propagation &lt;KEY-NNN&gt; [platformDir]</code></td>
432
+ <td>Per-repo migrated/drifted state for a superseded requirement</td>
433
+ <td>yes (array)</td>
434
+ <td>0 / 2</td>
435
+ </tr>
436
+ <tr>
437
+ <td><code dir="auto">spec gate &lt;repo&gt; &lt;KEY-NNN&gt; [platformDir]</code></td>
438
+ <td>Approval gate: pass iff Active and the pin covers it</td>
439
+ <td>yes (object)</td>
440
+ <td>0 / 1 / 2</td>
441
+ </tr>
442
+ <tr>
443
+ <td><code dir="auto">spec init [repo]</code></td>
444
+ <td>Declare a member through platform-map and write its <code dir="auto">spec-engine.member.json</code> pin</td>
445
+ <td>yes (object)</td>
446
+ <td>0 / 2</td>
447
+ </tr>
448
+ <tr>
449
+ <td><code dir="auto">spec domain new &lt;KEY&gt;</code> / <code dir="auto">spec domain list</code> / <code dir="auto">spec domain grammar &lt;KEY&gt;</code></td>
450
+ <td>Scaffold / list <code dir="auto">spec-engine/&lt;KEY&gt;/SPEC.json</code> / set its statement grammar and severity</td>
451
+ <td><code dir="auto">list</code>: yes (array); <code dir="auto">grammar</code>: yes (object)</td>
452
+ <td>0 / 2</td>
453
+ </tr>
454
+ <tr>
455
+ <td><code dir="auto">spec migrate [platformDir]</code></td>
456
+ <td>Strip the retired <code dir="auto">livesIn</code> and <code dir="auto">verifiedIn</code> fields from every <code dir="auto">SPEC.json</code> (idempotent)</td>
457
+ <td>yes (object)</td>
458
+ <td>0 / 2</td>
459
+ </tr>
460
+ <tr>
461
+ <td><code dir="auto">spec req &lt;domain-prefix&gt; [platformDir]</code></td>
462
+ <td>Piped: next unused id. <code dir="auto">--text</code>: author non-interactively</td>
463
+ <td>yes (object)</td>
464
+ <td>0 / 2</td>
465
+ </tr>
466
+ <tr>
467
+ <td><code dir="auto">spec term …</code></td>
468
+ <td>Author, list, revise, and confirm glossary terms</td>
469
+ <td>yes</td>
470
+ <td>0 / 2</td>
471
+ </tr>
472
+ <tr>
473
+ <td><code dir="auto">spec glossary [platformDir]</code></td>
474
+ <td>Generate <code dir="auto">GLOSSARY.md</code> from the TERM store; <code dir="auto">--migrate</code>, <code dir="auto">--check</code></td>
475
+ <td>yes (object)</td>
476
+ <td>0 / 1 / 2</td>
477
+ </tr>
478
+ <tr>
479
+ <td><code dir="auto">spec deprecate &lt;KEY-NNN&gt; --reason "…" [platformDir]</code></td>
480
+ <td>End an Active/Draft requirement with a recorded reason</td>
481
+ <td>yes (object)</td>
482
+ <td>0 / 2</td>
483
+ </tr>
484
+ <tr>
485
+ <td><code dir="auto">spec supersede &lt;KEY-NNN&gt; [platformDir]</code></td>
486
+ <td>Flip to superseded, mint the successor, retag every site to it</td>
487
+ <td>yes (object)</td>
488
+ <td>0 / 2</td>
489
+ </tr>
490
+ <tr>
491
+ <td><code dir="auto">spec move &lt;KEY-NNN&gt; &lt;NEW-DOMAIN&gt; [platformDir]</code></td>
492
+ <td>Cross-domain supersede</td>
493
+ <td>yes (object)</td>
494
+ <td>0 / 2</td>
495
+ </tr>
496
+ <tr>
497
+ <td><code dir="auto">spec amend &lt;KEY-NNN&gt; [platformDir]</code></td>
498
+ <td>Revise an unshipped entry in place</td>
499
+ <td>yes (object)</td>
500
+ <td>0 / 2</td>
501
+ </tr>
502
+ <tr>
503
+ <td><code dir="auto">spec accept &lt;KEY-NNN&gt; [platformDir]</code></td>
504
+ <td>Promote a Draft requirement to Active</td>
505
+ <td>yes (object)</td>
506
+ <td>0 / 2</td>
507
+ </tr>
508
+ <tr>
509
+ <td><code dir="auto">spec lint [KEY | KEY-NNN] [platformDir] [--all]</code></td>
510
+ <td>Every statement-rule finding for the platform, a domain, or a requirement</td>
511
+ <td>yes (array)</td>
512
+ <td>0 / 1 / 2</td>
513
+ </tr>
514
+ <tr>
515
+ <td><code dir="auto">spec serve [platformDir] [--port N] [--host IP]</code></td>
516
+ <td>Local webapp + <code dir="auto">/api/*</code> over the index</td>
517
+ <td>n/a (HTTP)</td>
518
+ <td>0 / 1 / 2</td>
519
+ </tr>
520
+ <tr>
521
+ <td><code dir="auto">spec docs [--port N]</code></td>
522
+ <td>Serve the bundled docs site offline</td>
523
+ <td>n/a (HTTP)</td>
524
+ <td>0 / 1 / 2</td>
525
+ </tr>
526
+ <tr>
527
+ <td><code dir="auto">spec mcp [platformDir]</code></td>
528
+ <td>MCP server over stdio</td>
529
+ <td>n/a (JSON-RPC)</td>
530
+ <td>0 / 2</td>
531
+ </tr>
532
+ </tbody>
533
+ </table>
534
+ <p>Common flags on the index-touching commands (<code dir="auto">index</code>, <code dir="auto">check</code>, <code dir="auto">map</code>, <code dir="auto">get</code>, <code dir="auto">list</code>, <code dir="auto">query</code>, <code dir="auto">relations</code>, <code dir="auto">resolve</code>, <code dir="auto">propagation</code>, <code dir="auto">provenance</code>, <code dir="auto">gate</code>, <code dir="auto">serve</code>):</p>
535
+ <table>
536
+ <thead>
537
+ <tr>
538
+ <th>Flag</th>
539
+ <th>Effect</th>
540
+ </tr>
541
+ </thead>
542
+ <tbody>
543
+ <tr>
544
+ <td><code dir="auto">--out &lt;path&gt;</code></td>
545
+ <td>Index DB path override. Must resolve inside <code dir="auto">platformDir</code> (exit 2 otherwise).</td>
546
+ </tr>
547
+ <tr>
548
+ <td><code dir="auto">--no-prompt</code></td>
549
+ <td>Suppress the member-onboarding prompt.</td>
550
+ </tr>
551
+ <tr>
552
+ <td><code dir="auto">--json</code></td>
553
+ <td>Machine output (all except <code dir="auto">serve</code>).</td>
554
+ </tr>
555
+ <tr>
556
+ <td><code dir="auto">--fresh</code></td>
557
+ <td>Read commands only: delete the DB plus WAL/SHM and reindex before answering.</td>
558
+ </tr>
559
+ </tbody>
560
+ </table>
561
+ <p><code dir="auto">init</code>, <code dir="auto">domain list</code>, <code dir="auto">req</code> register <code dir="auto">--json</code> only. <code dir="auto">domain new</code> registers none.</p>
562
+ <p><strong>Index freshness.</strong> Read commands (<code dir="auto">map</code>, <code dir="auto">get</code>, <code dir="auto">list</code>, <code dir="auto">query</code>, <code dir="auto">relations</code>, <code dir="auto">resolve</code>, <code dir="auto">propagation</code>, <code dir="auto">provenance</code>) build the index when it is missing and otherwise trust it. A warm index older than a changed <code dir="auto">SPEC.json</code> produces a stderr warning. Tag-only code edits are not detected. <code dir="auto">gate</code> always rebuilds cold, and so does <code dir="auto">check --ci</code>. After editing specs or tags, pass <code dir="auto">--fresh</code>.</p>
563
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-index">spec index</h3><a class="sl-anchor-link" href="#spec-index"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec index”</span></a></div>
564
+ <p>Build or refresh the derived index.</p>
565
+ <p><code dir="auto">--json</code>: <code dir="auto">{ build_id, repos, domains, requirements, tags, diagnostics }</code>.</p>
566
+ <p>Exit: 0 built / 1 an invalid SPEC.json refused the build, or the build crashed / 2 not a platform or bad args.</p>
567
+ <ul>
568
+ <li><code dir="auto">build_id</code> is deterministic for identical inputs (cold-rebuild equivalence).</li>
569
+ <li>A <code dir="auto">SPEC.json</code> that is not JSON or fails the shared schema refuses the whole build, and the index on disk is left as it was. Every file is parsed first, so one run reports every invalid file. Under <code dir="auto">--json</code> stdout holds only the <code dir="auto">INVALID_DOMAIN_FILE</code> rows in the <code dir="auto">spec check --json</code> row shape; text mode prints only those rows on stderr. Every other index-building command prints only the same rows and exits 1 (<code dir="auto">check</code>, <code dir="auto">gate</code>, <code dir="auto">guard</code>, <code dir="auto">serve</code>, and the read commands); a lifecycle command that reindexes for its retag (<code dir="auto">supersede</code>, <code dir="auto">amend</code>, <code dir="auto">deprecate</code>, <code dir="auto">move</code>, <code dir="auto">term confirm</code>) exits 2 after its write.</li>
570
+ <li>Also writes <code dir="auto">.spec-engine/doctor.md</code>, a list of doc lines that mention a requirement id without an explicit <code dir="auto">&lt;!-- @spec ID --&gt;</code> binding.</li>
571
+ </ul>
572
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-check">spec check</h3><a class="sl-anchor-link" href="#spec-check"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec check”</span></a></div>
573
+ <p>Integrity, coverage, drift, and governance diagnostics.</p>
574
+ <table>
575
+ <thead>
576
+ <tr>
577
+ <th>Flag</th>
578
+ <th>Effect</th>
579
+ </tr>
580
+ </thead>
581
+ <tbody>
582
+ <tr>
583
+ <td><code dir="auto">--ci</code></td>
584
+ <td>Delete the DB first. Correctness never trusts a warm index.</td>
585
+ </tr>
586
+ <tr>
587
+ <td><code dir="auto">--results &lt;junit.xml&gt;</code></td>
588
+ <td>The trusted-red gate: a verifying tag counts only when its test passed. Path resolves inside <code dir="auto">platformDir</code>.</td>
589
+ </tr>
590
+ <tr>
591
+ <td><code dir="auto">--base &lt;ref&gt;</code></td>
592
+ <td>Diff the requirement set against a git ref (deletion and status-flip governance).</td>
593
+ </tr>
594
+ <tr>
595
+ <td><code dir="auto">--approved-by &lt;list&gt;</code></td>
596
+ <td>Approver set for the status-flip check.</td>
597
+ </tr>
598
+ <tr>
599
+ <td><code dir="auto">--require-owner-approval</code></td>
600
+ <td>Escalate <code dir="auto">UNAPPROVED_STATUS_FLIP</code> from warning to error.</td>
601
+ </tr>
602
+ <tr>
603
+ <td><code dir="auto">--unsourced-change</code></td>
604
+ <td>Also warn on superseded requirements with no <code dir="auto">supersedes-via</code> issue.</td>
605
+ </tr>
606
+ </tbody>
607
+ </table>
608
+ <p><code dir="auto">--json</code> rows: <code dir="auto">{ code, severity, repo, source_file, line, req_id, detail }</code>, nullable except <code dir="auto">code</code>, <code dir="auto">severity</code>, <code dir="auto">detail</code>, sorted deterministically.</p>
609
+ <p>Exit: 0 clean or warnings only / 1 any error-severity row / 2 usage.</p>
610
+ <!-- @spec CHCK-053 -->
611
+ <p>Codes, in <code dir="auto">DiagnosticCode</code> order:</p>
612
+ <table>
613
+ <thead>
614
+ <tr>
615
+ <th>Code</th>
616
+ <th>Severity</th>
617
+ <th>Condition</th>
618
+ </tr>
619
+ </thead>
620
+ <tbody>
621
+ <tr>
622
+ <td><code dir="auto">DUP_ID</code></td>
623
+ <td>error</td>
624
+ <td>The same requirement id appears twice</td>
625
+ </tr>
626
+ <tr>
627
+ <td><code dir="auto">BROKEN_SUPERSEDE</code></td>
628
+ <td>error</td>
629
+ <td>A <code dir="auto">supersededBy</code> names a missing id, or a <code dir="auto">supersedes</code> names a missing id or the entry itself</td>
630
+ </tr>
631
+ <tr>
632
+ <td><code dir="auto">BAD_STATUS</code></td>
633
+ <td>error</td>
634
+ <td>An entry status outside <code dir="auto">active</code> / <code dir="auto">draft</code> / <code dir="auto">superseded</code> / <code dir="auto">deprecated</code></td>
635
+ </tr>
636
+ <tr>
637
+ <td><code dir="auto">DANGLING_TAG</code></td>
638
+ <td>error</td>
639
+ <td>A tag references no requirement</td>
640
+ </tr>
641
+ <tr>
642
+ <td><code dir="auto">SUPERSEDED_REFERENCED</code></td>
643
+ <td>error</td>
644
+ <td>Code still tags a superseded requirement (retag to the successor)</td>
645
+ </tr>
646
+ <tr>
647
+ <td><code dir="auto">DEPRECATED_REFERENCED</code></td>
648
+ <td>error</td>
649
+ <td>Code still tags a deprecated requirement</td>
650
+ </tr>
651
+ <tr>
652
+ <td><code dir="auto">STATEMENT_GRAMMAR</code></td>
653
+ <td>per domain, default warning</td>
654
+ <td>A statement fails its domain’s declared grammar</td>
655
+ </tr>
656
+ <tr>
657
+ <td><code dir="auto">STATEMENT_RULES</code></td>
658
+ <td>error; per rule from <code dir="auto">statementRules.severity</code></td>
659
+ <td>An Active statement breaks a statement rule: one row per rule per statement, the detail <code dir="auto">&lt;rule&gt;: &lt;detail&gt; — &lt;reason&gt;</code></td>
660
+ </tr>
661
+ <tr>
662
+ <td><code dir="auto">DRAFT_REFERENCED</code></td>
663
+ <td>error on a verifying tag, warning on an implementing or documenting tag</td>
664
+ <td>A tag references a Draft requirement</td>
665
+ </tr>
666
+ <tr>
667
+ <td><code dir="auto">GLOSSARY_DRIFT</code></td>
668
+ <td>warning</td>
669
+ <td>The committed glossary differs from the one generated from the term store</td>
670
+ </tr>
671
+ <tr>
672
+ <td><code dir="auto">ORPHAN_REQ</code></td>
673
+ <td>error</td>
674
+ <td>An Active requirement has no implementing tag</td>
675
+ </tr>
676
+ <tr>
677
+ <td><code dir="auto">UNVERIFIED_REQ</code></td>
678
+ <td>warning</td>
679
+ <td>An Active requirement is implemented but has no verifying tag</td>
680
+ </tr>
681
+ <tr>
682
+ <td><code dir="auto">DRIFT</code></td>
683
+ <td>error</td>
684
+ <td>A member is pinned behind the version at which a requirement it references changed</td>
685
+ </tr>
686
+ <tr>
687
+ <td><code dir="auto">NO_SPEC_CONFIG</code></td>
688
+ <td>warning</td>
689
+ <td>A declared member is present on disk with no <code dir="auto">spec-engine.member.json</code></td>
690
+ </tr>
691
+ <tr>
692
+ <td><code dir="auto">BROKEN_RELATES</code></td>
693
+ <td>warning</td>
694
+ <td>A <code dir="auto">relates</code> entry names a nonexistent requirement</td>
695
+ </tr>
696
+ <tr>
697
+ <td><code dir="auto">RELATES_SUPERSEDED</code></td>
698
+ <td>warning</td>
699
+ <td>A <code dir="auto">relates</code> entry names a superseded requirement</td>
700
+ </tr>
701
+ <tr>
702
+ <td><code dir="auto">CYCLIC_SUPERSEDE</code></td>
703
+ <td>error</td>
704
+ <td>A circular supersession chain</td>
705
+ </tr>
706
+ <tr>
707
+ <td><code dir="auto">SELF_RELATES</code></td>
708
+ <td>warning</td>
709
+ <td>A <code dir="auto">relates</code> entry names its own requirement (dropped, but surfaced)</td>
710
+ </tr>
711
+ <tr>
712
+ <td><code dir="auto">UNKNOWN_ROLE</code></td>
713
+ <td>warning</td>
714
+ <td>An <code dir="auto">issues</code> entry uses a role outside <code dir="auto">created</code> / <code dir="auto">supersedes-via</code> / <code dir="auto">amends-via</code> (surfaced and dropped, never stored)</td>
715
+ </tr>
716
+ <tr>
717
+ <td><code dir="auto">UNSOURCED_CHANGE</code></td>
718
+ <td>warning, only with <code dir="auto">--unsourced-change</code></td>
719
+ <td>A superseded requirement carries no <code dir="auto">supersedes-via</code> issue</td>
720
+ </tr>
721
+ <tr>
722
+ <td><code dir="auto">INVALID_DOMAIN_FILE</code></td>
723
+ <td>error</td>
724
+ <td>A <code dir="auto">SPEC.json</code> is not JSON or fails the shared schema; one row per failing field</td>
725
+ </tr>
726
+ <tr>
727
+ <td><code dir="auto">MIGRATION_PENDING</code></td>
728
+ <td>warning</td>
729
+ <td>A <code dir="auto">SPEC.json</code> still carries the retired <code dir="auto">livesIn</code> or <code dir="auto">verifiedIn</code> fields; run <code dir="auto">spec migrate</code></td>
730
+ </tr>
731
+ <tr>
732
+ <td><code dir="auto">UNPROVEN_REQ</code></td>
733
+ <td>error, only with <code dir="auto">--results</code></td>
734
+ <td>An Active requirement has verifying tags but no passing correlated test</td>
735
+ </tr>
736
+ <tr>
737
+ <td><code dir="auto">PROOFS_UNCONFIRMED</code></td>
738
+ <td>warning, only without <code dir="auto">--results</code></td>
739
+ <td>The proof gate fell back to presence-only mode</td>
740
+ </tr>
741
+ <tr>
742
+ <td><code dir="auto">REQUIREMENT_REMOVED</code></td>
743
+ <td>error</td>
744
+ <td>An Active requirement present at the base ref is gone with no same-change supersession</td>
745
+ </tr>
746
+ <tr>
747
+ <td><code dir="auto">IMPL_LOST</code></td>
748
+ <td>error, when a base ref resolves</td>
749
+ <td>The change removes the last implementing tag of a surviving Active requirement</td>
750
+ </tr>
751
+ <tr>
752
+ <td><code dir="auto">VERIFY_LOST</code></td>
753
+ <td>warning, when a base ref resolves</td>
754
+ <td>The change removes the last verifying tag of a surviving Active requirement</td>
755
+ </tr>
756
+ <tr>
757
+ <td><code dir="auto">SPEC_FILE_DELETED</code></td>
758
+ <td>error, when a base ref resolves</td>
759
+ <td>A <code dir="auto">SPEC.json</code> present at the base ref is gone</td>
760
+ </tr>
761
+ <tr>
762
+ <td><code dir="auto">UNAPPROVED_STATUS_FLIP</code></td>
763
+ <td>warning; error with <code dir="auto">--require-owner-approval</code></td>
764
+ <td>A flip to superseded or deprecated whose CODEOWNERS domain owner is absent from <code dir="auto">--approved-by</code></td>
765
+ </tr>
766
+ <tr>
767
+ <td><code dir="auto">PARTIAL_PROPAGATION</code></td>
768
+ <td>error, needs <code dir="auto">--base</code> and <code dir="auto">--results</code></td>
769
+ <td>A changed requirement where some bound tests pass and some fail</td>
770
+ </tr>
771
+ <tr>
772
+ <td><code dir="auto">MALFORMED_FILE</code></td>
773
+ <td>platform-map’s severity: error, or warning for a manifest</td>
774
+ <td>A platform file, a package or workspace manifest, or platform-map’s per-user file failed to parse or validate</td>
775
+ </tr>
776
+ <tr>
777
+ <td><code dir="auto">MEMBER_MISSING</code></td>
778
+ <td>warning</td>
779
+ <td>A declared member is not on this machine; it is not scanned</td>
780
+ </tr>
781
+ <tr>
782
+ <td><code dir="auto">MARKER_MISSING</code></td>
783
+ <td>warning</td>
784
+ <td>A declared member has no marker</td>
785
+ </tr>
786
+ <tr>
787
+ <td><code dir="auto">MARKER_MISMATCH</code></td>
788
+ <td>error</td>
789
+ <td>A member’s marker names a different platform</td>
790
+ </tr>
791
+ <tr>
792
+ <td><code dir="auto">UNLISTED_REPO</code></td>
793
+ <td>warning (platform-map info, promoted)</td>
794
+ <td>A repository in the platform folder is not a member; its tags are not scanned</td>
795
+ </tr>
796
+ <tr>
797
+ <td><code dir="auto">PLATFORM_NOT_LOCATED</code></td>
798
+ <td>warning</td>
799
+ <td>A marker names a platform this machine cannot find</td>
800
+ </tr>
801
+ <tr>
802
+ <td><code dir="auto">UNDECLARED_PLATFORM</code></td>
803
+ <td>warning (platform-map info, promoted)</td>
804
+ <td>A folder of repositories with no platform file; nothing in it is a member</td>
805
+ </tr>
806
+ <tr>
807
+ <td><code dir="auto">SCAN_TRUNCATED</code></td>
808
+ <td>warning</td>
809
+ <td>A workspace walk hit its depth or entry cap</td>
810
+ </tr>
811
+ <tr>
812
+ <td><code dir="auto">UNDEFINED_TERM</code></td>
813
+ <td>error</td>
814
+ <td>A <code dir="auto">cites</code> entry resolves to no glossary term</td>
815
+ </tr>
816
+ <tr>
817
+ <td><code dir="auto">ORPHAN_TERM</code></td>
818
+ <td>warning</td>
819
+ <td>An Active term that nothing cites</td>
820
+ </tr>
821
+ <tr>
822
+ <td><code dir="auto">TERM_DRIFT</code></td>
823
+ <td>warning</td>
824
+ <td>A citation’s pin is behind the term’s current version</td>
825
+ </tr>
826
+ <tr>
827
+ <td><code dir="auto">SUPERSEDED_TERM_REFERENCED</code></td>
828
+ <td>error</td>
829
+ <td>A citation names a superseded term</td>
830
+ </tr>
831
+ </tbody>
832
+ </table>
833
+ <p>The four TERM codes are the last four rows. Only <code dir="auto">error</code> rows drive exit 1.</p>
834
+ <p>The eight codes before them are platform-map’s, surfaced with the platform-map code: every platform-map error and warning keeps its severity; <code dir="auto">UNLISTED_REPO</code> and <code dir="auto">UNDECLARED_PLATFORM</code> are promoted from info to warning because they mean a repository’s tags are not scanned; <code dir="auto">UNMATCHED_PATTERN</code> and <code dir="auto">AMBIGUOUS_ECOSYSTEM</code> are not surfaced. <code dir="auto">repo</code> is the diagnostic’s subject when it names a member; <code dir="auto">source_file</code> is the subject. <code dir="auto">NO_SPEC_CONFIG</code> stays Spec Engine’s own code: a declared member present on disk with no <code dir="auto">spec-engine.member.json</code>.</p>
835
+ <ul>
836
+ <li>Deletion detection runs by default: with no <code dir="auto">--base</code>, the working tree is diffed against <code dir="auto">HEAD</code> whenever git resolves. An Active requirement absent from the tree without a same-change supersession is <code dir="auto">REQUIREMENT_REMOVED</code>. History (superseded and deprecated entries) may never be deleted. Outside git the check stays quiet.</li>
837
+ <li>Without <code dir="auto">--results</code>, <code dir="auto">check</code> emits <code dir="auto">PROOFS_UNCONFIRMED</code> and skips proof enforcement. The results file is never hashed into <code dir="auto">build_id</code>. Generate it with the test runner’s JUnit reporter, for example <code dir="auto">bun test --reporter=junit --reporter-outfile=.spec-engine/results.xml</code>.</li>
838
+ <li>Term citations: <code dir="auto">UNDEFINED_TERM</code>, <code dir="auto">SUPERSEDED_TERM_REFERENCED</code>, and <code dir="auto">TERM_DRIFT</code> clear with <code dir="auto">spec term confirm</code>.</li>
839
+ <li>Losses: whenever a base ref resolves (<code dir="auto">--base</code>, else <code dir="auto">HEAD</code>), <code dir="auto">check</code> reports the <code dir="auto">IMPL_LOST</code>, <code dir="auto">VERIFY_LOST</code>, and <code dir="auto">SPEC_FILE_DELETED</code> rows <code dir="auto">spec guard</code> would report against that ref. Guard’s <code dir="auto">REQUIREMENT_REMOVED</code> rows are dropped; the removal check already reports that id.</li>
840
+ <li>Under <code dir="auto">--ci</code> with no <code dir="auto">--base</code>, a working tree whose tracked files equal <code dir="auto">HEAD</code> makes every loss check pass vacuously, so <code dir="auto">check</code> warns on stderr with the line to run instead: <code dir="auto">spec check . --ci --base "$(git merge-base HEAD origin/&lt;default&gt;)"</code>. The exit code and stdout are unchanged. <!-- @spec GUARD-031 --></li>
841
+ <li>Coverage counts only Active requirements: <code dir="auto">ORPHAN_REQ</code> and <code dir="auto">UNVERIFIED_REQ</code> fire for Active entries only. <!-- @spec CHCK-032 --></li>
842
+ </ul>
843
+ <div class="sl-heading-wrapper level-h4"><h4 id="checkjson">check.json</h4><a class="sl-anchor-link" href="#checkjson"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “check.json”</span></a></div>
844
+ <p><code dir="auto">spec-engine/check.json</code> regrades diagnostics. Absent means no rules.</p>
845
+ <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"severity"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"code"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">UNVERIFIED_REQ</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"severity"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">error</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> },</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"code"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">UNVERIFIED_REQ</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"severity"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">warning</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"match"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">LEGACY</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">BILLING-004</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">] }</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">] }</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &quot;severity&quot;: [ { &quot;code&quot;: &quot;UNVERIFIED_REQ&quot;, &quot;severity&quot;: &quot;error&quot; }, { &quot;code&quot;: &quot;UNVERIFIED_REQ&quot;, &quot;severity&quot;: &quot;warning&quot;, &quot;match&quot;: [&quot;LEGACY&quot;, &quot;BILLING-004&quot;] }] }"><div></div></button></div></figure></div>
846
+ <ul>
847
+ <li><code dir="auto">code</code> is any diagnostic code; <code dir="auto">severity</code> is <code dir="auto">error</code>, <code dir="auto">warning</code>, or <code dir="auto">off</code>.</li>
848
+ <li><code dir="auto">match</code> lists domain keys and requirement ids. A scoped rule applies only to rows whose <code dir="auto">req_id</code> is listed or carries a listed key.</li>
849
+ <li>The most specific rule wins: an id match, then a domain match, then an unscoped rule. Among equals, the later rule wins.</li>
850
+ <li><code dir="auto">off</code> drops the row from every output.</li>
851
+ <li>Grading runs inside the operation, so the CLI, <code dir="auto">spec_check</code>, and every other surface agree, and exit 1 follows the graded severities.</li>
852
+ <li>A file that fails the schema (unknown code or key, a bad severity, a malformed match entry) exits 2 with <code dir="auto">spec-engine/check.json: &lt;path&gt;: &lt;reason&gt;</code>.</li>
853
+ </ul>
854
+ <p><code dir="auto">statementRules</code> configures the statement rules on every surface: the write-time refusal, <code dir="auto">STATEMENT_RULES</code>, and <code dir="auto">spec lint</code>.</p>
855
+ <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{ </span><span style="--0:#7FDBCA;--1:#096E72">"severity"</span><span style="--0:#D6DEEB;--1:#403F53">: [],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"statementRules"</span><span style="--0:#D6DEEB;--1:#403F53">: {</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"maxLength"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#F78C6C;--1:#AA0982">280</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"jargon"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">seam</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">rung</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"exemptNames"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">SPEC.json</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">spec-engine/</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"severity"</span><span style="--0:#D6DEEB;--1:#403F53">: { </span><span style="--0:#7FDBCA;--1:#096E72">"no-jargon"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">off</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"length"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">error</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> }</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">} }</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &quot;severity&quot;: [], &quot;statementRules&quot;: { &quot;maxLength&quot;: 280, &quot;jargon&quot;: [&quot;seam&quot;, &quot;rung&quot;], &quot;exemptNames&quot;: [&quot;SPEC.json&quot;, &quot;spec-engine/&quot;], &quot;severity&quot;: { &quot;no-jargon&quot;: &quot;off&quot;, &quot;length&quot;: &quot;error&quot; }} }"><div></div></button></div></figure></div>
856
+ <ul>
857
+ <li><code dir="auto">maxLength</code> defaults to 280. <code dir="auto">jargon</code> replaces the default list (<code dir="auto">seam</code>, <code dir="auto">seams</code>, <code dir="auto">rung</code>, <code dir="auto">rungs</code>, <code dir="auto">residue</code>, <code dir="auto">matrix entry</code>, <code dir="auto">stage runner</code>, <code dir="auto">the stage</code>).</li>
858
+ <li><code dir="auto">exemptNames</code> are product names <code dir="auto">no-file-path</code> never reports. A name ending in <code dir="auto">/</code> exempts every path under it.</li>
859
+ <li><code dir="auto">severity</code> maps a rule to <code dir="auto">error</code>, <code dir="auto">warning</code>, or <code dir="auto">off</code>. A rule with no entry is <code dir="auto">error</code>. <code dir="auto">error</code> refuses the write and grades the <code dir="auto">STATEMENT_RULES</code> row error; <code dir="auto">warning</code> writes, prints the finding on stderr, and grades the row warning; <code dir="auto">off</code> turns the rule off everywhere. The <code dir="auto">severity</code> array regrades by code after it.</li>
860
+ <li>The rules: <code dir="auto">one-sentence</code>, <code dir="auto">length</code>, <code dir="auto">no-file-path</code>, <code dir="auto">no-test-narration</code>, <code dir="auto">no-toolchain-layout</code>, <code dir="auto">no-history</code>, <code dir="auto">no-jargon</code>, <code dir="auto">no-tool-statement</code>. Unknown keys exit 2.</li>
861
+ </ul>
862
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-guard">spec guard</h3><a class="sl-anchor-link" href="#spec-guard"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec guard”</span></a></div>
863
+ <p>The pre-commit loss gate. Diffs the requirement derivation at <code dir="auto">--against &lt;ref&gt;</code> (default <code dir="auto">HEAD</code>) against the working tree, scoped to the files in <code dir="auto">git diff</code>. Rebuilds the worktree index cold every run.</p>
864
+ <p><code dir="auto">--json</code> rows: <code dir="auto">{ kind, req_id, file, line, detail }</code>, sorted by <code dir="auto">(req_id, kind, file, line)</code>. <code dir="auto">kind</code> is one of <code dir="auto">REQUIREMENT_REMOVED</code>, <code dir="auto">IMPL_LOST</code>, <code dir="auto">VERIFY_LOST</code>, <code dir="auto">SPEC_FILE_DELETED</code> (<code dir="auto">req_id</code> null).</p>
865
+ <p>Exit: 0 clean / 1 any loss / 2 usage.</p>
866
+ <ul>
867
+ <li>A loss is suppressed only by ending the requirement in the same change: supersede it (either direction) or <code dir="auto">spec deprecate</code> it. There is no override comment.</li>
868
+ <li>A non-git tree, a repo with no <code dir="auto">HEAD</code>, or an unknown ref prints <code dir="auto">NOT_A_GIT_REPO</code> to stderr and exits 0.</li>
869
+ <li>A ref that begins with <code dir="auto">-</code> or holds a character outside <code dir="auto">A-Z a-z 0-9 . _ / -</code> is refused before git runs: <code dir="auto">guard</code> treats it as an unknown ref, and <code dir="auto">check --base</code> exits 2.</li>
870
+ <li>Text mode prints a <code dir="auto">🛑 spec-guard:</code> block per requirement, written in the second person. Relay it verbatim to the user before proceeding.</li>
871
+ </ul>
872
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-map">spec map</h3><a class="sl-anchor-link" href="#spec-map"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec map”</span></a></div>
873
+ <p><code dir="auto">--json</code> rows keyed <code dir="auto">(req_id, repo)</code>: <code dir="auto">{ req_id, domain_key, req_status, req_spec_version, req_changed_at_version, repo, repo_pin, implemented: 0|1, verified: 0|1, test_levels }</code>. Text mode renders <code dir="auto">src</code>, <code dir="auto">test</code>, <code dir="auto">src+test</code>, <code dir="auto">—</code>.</p>
874
+ <p>Exit: 0 / 2.</p>
875
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-get">spec get</h3><a class="sl-anchor-link" href="#spec-get"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec get”</span></a></div>
876
+ <p>One requirement’s full record from the derived index, with the code sites its tags name. Tags are the only record of where a requirement is implemented and verified.</p>
877
+ <p><code dir="auto">--json</code>: the <code dir="auto">Requirement</code> row <code dir="auto">{ id, key, seq, status, superseded_by, text, why, source_file, line, spec_version, changed_at_version, superseded_at_version }</code> plus <code dir="auto">implemented_in: [{ repo, file, line, kind }]</code> (implementing and documenting tags) and <code dir="auto">verified_in: [{ repo, file, line, level }]</code> (verifying tags), each sorted by (repo, file, line). The same object is served by <code dir="auto">GET /api/requirements/:id</code> and <code dir="auto">spec_get</code>, and shown on the webapp <code dir="auto">/requirements/:id</code> page. Text mode is a one-row table (<code dir="auto">ID STATUS KEY SEQ CHANGED_AT TEXT</code>) followed by an <code dir="auto">implemented in:</code> and a <code dir="auto">verified in:</code> list of <code dir="auto">file:line</code>.</p>
878
+ <p>Exit: 0 / 2. An unknown id prints <code dir="auto">[]</code> on stdout, guidance on stderr, and exits 0. A malformed id exits 2.</p>
879
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-list">spec list</h3><a class="sl-anchor-link" href="#spec-list"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec list”</span></a></div>
880
+ <p>Every requirement record in (key, seq) order. Unlike <code dir="auto">query</code>, superseded and deprecated rows are included and nothing is ranked.</p>
881
+ <table>
882
+ <thead>
883
+ <tr>
884
+ <th>Flag</th>
885
+ <th>Effect</th>
886
+ </tr>
887
+ </thead>
888
+ <tbody>
889
+ <tr>
890
+ <td><code dir="auto">--domain KEY</code></td>
891
+ <td>Only that domain (case-insensitive key).</td>
892
+ </tr>
893
+ <tr>
894
+ <td><code dir="auto">--status S</code></td>
895
+ <td>Only that status: <code dir="auto">active</code>, <code dir="auto">draft</code>, <code dir="auto">superseded</code>, <code dir="auto">deprecated</code>. Any other word exits 2.</td>
896
+ </tr>
897
+ </tbody>
898
+ </table>
899
+ <p><code dir="auto">--json</code>: an array of <code dir="auto">Requirement</code> rows, the same rows <code dir="auto">GET /api/requirements</code> serves. Text mode is the <code dir="auto">ID STATUS KEY SEQ CHANGED_AT TEXT</code> table.</p>
900
+ <p>Exit: 0 / 2. An empty result is <code dir="auto">[]</code> under <code dir="auto">--json</code>, guidance on stderr in text mode, exit 0.</p>
901
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-query">spec query</h3><a class="sl-anchor-link" href="#spec-query"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec query”</span></a></div>
902
+ <p>FTS5 <code dir="auto">MATCH</code> syntax. <code dir="auto">--limit N</code> (default 10, max 1000).</p>
903
+ <p>Exit: 0 / 2 (bare <code dir="auto">AND</code> or <code dir="auto">OR</code> is an FTS5 grammar error).</p>
904
+ <ul>
905
+ <li>Wrap multi-word phrases in double quotes inside the shell-quoted arg.</li>
906
+ <li>Results rank ascending (best first). Superseded requirements are excluded.</li>
907
+ <li>Glossary term definitions ride the same index. A matching term appears in a separate Terms group in text mode, discriminated by <code dir="auto">key</code> under <code dir="auto">--json</code>.</li>
908
+ </ul>
909
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-relations">spec relations</h3><a class="sl-anchor-link" href="#spec-relations"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec relations”</span></a></div>
910
+ <p>Text mode: mermaid <code dir="auto">graph LR</code>, one node per requirement in a <code dir="auto">relates</code> field, one undirected deduped edge per pair. <code dir="auto">--json</code> rows: <code dir="auto">{ from_id, to_id, source_file, line }</code>, sorted by (from, to).</p>
911
+ <p>Exit: 0 / 2. Empty graph is <code dir="auto">[]</code> and exit 0.</p>
912
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-provenance">spec provenance</h3><a class="sl-anchor-link" href="#spec-provenance"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec provenance”</span></a></div>
913
+ <p>Per requirement: the creating issue, the revising issues (<code dir="auto">supersedes-via</code>, <code dir="auto">amends-via</code>), the backing tests, and the <code dir="auto">source_file:line</code> pointer.</p>
914
+ <table>
915
+ <thead>
916
+ <tr>
917
+ <th>Flag</th>
918
+ <th>Effect</th>
919
+ </tr>
920
+ </thead>
921
+ <tbody>
922
+ <tr>
923
+ <td><code dir="auto">[issueId]</code> positional</td>
924
+ <td>Display-only reverse filter. Never a join key.</td>
925
+ </tr>
926
+ <tr>
927
+ <td><code dir="auto">--resolve-issues</code></td>
928
+ <td>Overlay tracker title/status/URL. Needs <code dir="auto">SPEC_TRACKER_TOKEN</code>; degrades to bare ids without it.</td>
929
+ </tr>
930
+ </tbody>
931
+ </table>
932
+ <p><code dir="auto">--json</code> rows: <code dir="auto">{ req_id, role, issue_id, source_file, line, req_status, implemented, verified, test_levels }</code>, sorted on the full composite key.</p>
933
+ <p>Exit: 0 / 2. Empty matrix is <code dir="auto">[]</code> and exit 0.</p>
934
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-resolve">spec resolve</h3><a class="sl-anchor-link" href="#spec-resolve"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec resolve”</span></a></div>
935
+ <p>Accepts multiple positionals, comma-split inside one positional, and absolute paths under the platform dir.</p>
936
+ <table>
937
+ <thead>
938
+ <tr>
939
+ <th>Flag</th>
940
+ <th>Effect</th>
941
+ </tr>
942
+ </thead>
943
+ <tbody>
944
+ <tr>
945
+ <td><code dir="auto">--req KEY-NNN</code></td>
946
+ <td>Reverse query: every tag site for that requirement. Takes no file positionals.</td>
947
+ </tr>
948
+ </tbody>
949
+ </table>
950
+ <p><code dir="auto">--json</code> rows (<code dir="auto">--req</code>): <code dir="auto">{ req_id, repo, file, line, kind, level }</code> sorted by (repo, file, line). <code dir="auto">kind</code> is <code dir="auto">implements</code>, <code dir="auto">verifies</code>, or <code dir="auto">documents</code>.</p>
951
+ <p>Exit: 0 / 2. Empty result is <code dir="auto">[]</code> and exit 0. Unknown id under <code dir="auto">--req</code> is <code dir="auto">[]</code>.</p>
952
+ <p>Path rules. <code dir="auto">tags.file</code> is platform-relative: <code dir="auto">&lt;member&gt;/&lt;path&gt;</code> for a file inside a declared member or a workspace package, and the bare <code dir="auto">&lt;path&gt;</code> for a file a lone repository owns itself (its directory-named or <code dir="auto">root</code> column). The key never carries the checkout’s folder name.</p>
953
+ <ul>
954
+ <li>Multi-repo platform: pass <code dir="auto">&lt;repo&gt;/&lt;path&gt;</code>, e.g. <code dir="auto">spec resolve api/src/renew.ts .</code></li>
955
+ <li>Single repo: pass the path from the platform root, e.g. <code dir="auto">spec resolve src/orders.ts .</code> or <code dir="auto">spec resolve .github/workflows/ci.yml .</code></li>
956
+ <li>Absolute paths are normalized against <code dir="auto">platformDir</code>. A path outside it exits 2.</li>
957
+ </ul>
958
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-propagation">spec propagation</h3><a class="sl-anchor-link" href="#spec-propagation"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec propagation”</span></a></div>
959
+ <p>One row per member repo: <code dir="auto">{ repo, state, via_req_id, drifted }</code>. <code dir="auto">state</code> is one of <code dir="auto">MIGRATED_VERIFIED</code>, <code dir="auto">MIGRATED_UNVERIFIED</code>, <code dir="auto">ON_PREDECESSOR</code>, <code dir="auto">ON_OTHER_DOMAIN_REQ</code>, <code dir="auto">NO_DOMAIN_REFERENCE</code>.</p>
960
+ <p>Rows are in dependency order: a member comes after every member it depends on, read from platform-map’s <code dir="auto">dependsOn</code> (the platform’s own package names in a member’s manifest), and members at the same depth come by name. The same order is served by <code dir="auto">/api/propagation/:id</code>, <code dir="auto">spec_propagation</code>, and the webapp’s <code dir="auto">/propagation/:id</code> page. A platform with no in-platform dependencies lists members by name.</p>
961
+ <p>Exit: 0 / 2.</p>
962
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-gate">spec gate</h3><a class="sl-anchor-link" href="#spec-gate"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec gate”</span></a></div>
963
+ <p><code dir="auto">--json</code>: <code dir="auto">{ pass, reason, repo, req_id, detail, status, changed_at_version, pinned_spec_version }</code>. <code dir="auto">reason</code> is one of <code dir="auto">PASS</code>, <code dir="auto">NOT_FOUND</code>, <code dir="auto">DRAFT</code>, <code dir="auto">SUPERSEDED</code>, <code dir="auto">DEPRECATED</code>, <code dir="auto">VERSION_PIN</code>, decided in that order. Pin equality passes.</p>
964
+ <p>Exit: 0 pass / 1 fail / 2 unknown repo name or bad args.</p>
965
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-init">spec init</h3><a class="sl-anchor-link" href="#spec-init"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec init”</span></a></div>
966
+ <p>Declares a member through platform-map and writes <code dir="auto">spec-engine.member.json</code>.</p>
967
+ <table>
968
+ <thead>
969
+ <tr>
970
+ <th>Flag</th>
971
+ <th>Effect</th>
972
+ </tr>
973
+ </thead>
974
+ <tbody>
975
+ <tr>
976
+ <td><code dir="auto">--specs spec-engine@N</code></td>
977
+ <td>Override the pin.</td>
978
+ </tr>
979
+ <tr>
980
+ <td><code dir="auto">--force</code></td>
981
+ <td>Rewrite an existing config, preserving <code dir="auto">ignore</code>. Any other key refuses, as does a malformed <code dir="auto">ignore</code>.</td>
982
+ </tr>
983
+ <tr>
984
+ <td><code dir="auto">--platform &lt;dir&gt;</code></td>
985
+ <td>The platform root, for a declared member checked out outside the platform folder. Its location is recorded in platform-map’s per-user file first.</td>
986
+ </tr>
987
+ </tbody>
988
+ </table>
989
+ <p><code dir="auto">--json</code>: <code dir="auto">{ action: "wrote", path, pin, source, declared, linked, agent_surface }</code>, <code dir="auto">{ action: "already-configured", path, pin, extra_fields, declared, linked, agent_surface }</code>, or, on a platform directory, <code dir="auto">{ action: "surface-only", agent_surface }</code>. <code dir="auto">declared</code> lists the platform-map files written (the platform file, the marker), empty when the member was already declared; <code dir="auto">linked</code> is the per-user file written under <code dir="auto">--platform</code>, else null; <code dir="auto">agent_surface</code> lists the agent-surface files written or changed, <code dir="auto">[]</code> on a re-run.</p>
990
+ <p>Exit: 0 wrote or already configured / 2. Branch on <code dir="auto">action</code>, not the exit code.</p>
991
+ <ul>
992
+ <li>Where the target sits decides what is written:
993
+ <ul>
994
+ <li>a repository directly under a platform folder (a directory holding <code dir="auto">spec-engine/</code>) that the platform file does not list is declared: a <code dir="auto">platform-map.json</code> platform file entry at the root (the file is created when absent) and a <code dir="auto">{ platform, member }</code> marker in the repository, then the pin. platform-map declares only a directory with a <code dir="auto">.git</code> entry or a <code dir="auto">package.json</code>; a plain folder is refused with exit 2;</li>
995
+ <li>a declared member only gets the pin (a missing marker is written back);</li>
996
+ <li>a workspace package of a monorepo gets a nested pin and no declaration; a repository inside a lone monorepo that is not a workspace package is refused;</li>
997
+ <li>a directory under no platform gets the <code dir="auto">spec-engine@1</code> fallback pin;</li>
998
+ <li>a platform directory (holding <code dir="auto">spec-engine/</code>) gets the agent surface and no pin.</li>
999
+ </ul>
1000
+ </li>
1001
+ <li>Every action then installs the agent surface into the repository (the member; the monorepo root for a workspace package), bundled in the npm package under <code dir="auto">dist/agent/</code>. Hints for anything left alone go to stderr.
1002
+ <ul>
1003
+ <li><code dir="auto">AGENTS.md</code>: the agent reference between a <code dir="auto">spec-engine:agent-reference:begin</code> and a <code dir="auto">spec-engine:agent-reference:end</code> HTML comment marker, appended or created, replaced in place when the markers exist; text outside them is never touched. A <code dir="auto">CLAUDE.md</code> that does not mention <code dir="auto">AGENTS.md</code> gets a hint only.</li>
1004
+ <li><code dir="auto">.claude/skills/req-author/</code>: written when absent; a differing copy is replaced only under <code dir="auto">--force</code>.</li>
1005
+ <li><code dir="auto">.mcp.json</code>: a <code dir="auto">spec</code> server entry (<code dir="auto">spec mcp &lt;platform&gt;</code>) merged in; other keys and an existing <code dir="auto">spec</code> entry are kept.</li>
1006
+ <li><code dir="auto">pre-commit</code>: <code dir="auto">spec guard &lt;platform&gt;</code>, written to <code dir="auto">.githooks/</code> or else <code dir="auto">.git/hooks/</code> when no hook exists there; an existing hook gets a hint.</li>
1007
+ <li>With no platform located, the MCP entry and the hook are skipped.</li>
1008
+ </ul>
1009
+ </li>
1010
+ <li>The platform root is platform-map’s <code dir="auto">locate()</code>: the nearest <code dir="auto">platform-map.json</code> or <code dir="auto">.git</code> boundary, a marker resolved through the parent directory or the per-user file; the parent directory when it holds <code dir="auto">spec-engine/</code>. A <code dir="auto">.git</code> boundary between the target and an outer <code dir="auto">spec-engine/</code> hides that platform.</li>
1011
+ <li>Default pin: <code dir="auto">--specs</code>, else the derived platform version, else <code dir="auto">spec-engine@1</code> with a printed note.</li>
1012
+ <li>A resolved path with a <code dir="auto">spec-engine</code> segment is refused, except on the engine’s own checkout (root package named <code dir="auto">@spec-engine/spec-engine</code>), where only the path below the platform root is judged.</li>
1013
+ <li><code dir="auto">ignore: ["dir", …]</code> excludes repo-relative directory prefixes from that repo’s scans: <code dir="auto">vendor</code> hides <code dir="auto">vendor/x.ts</code> but not <code dir="auto">src/vendor/x.ts</code>. A monorepo root column excludes its workspace package paths the same way. A monorepo’s columns are its workspace manifest’s packages.</li>
1014
+ </ul>
1015
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-domain">spec domain</h3><a class="sl-anchor-link" href="#spec-domain"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec domain”</span></a></div>
1016
+ <p><code dir="auto">spec domain new &lt;KEY&gt;</code> scaffolds <code dir="auto">spec-engine/&lt;KEY&gt;/SPEC.json</code>. <code dir="auto">spec domain list</code> prints one key per line; <code dir="auto">--json</code> prints a sorted array of <code dir="auto">{ key, scope }</code> (scope is the charter sentence or null), read from the filesystem.</p>
1017
+ <p><code dir="auto">spec domain grammar &lt;KEY&gt; [platformDir] [--grammar ears|freeform] [--severity warning|error]</code> sets the envelope’s <code dir="auto">grammar</code> and/or <code dir="auto">grammarSeverity</code> through the validating write path; every other field is written back as read. <code dir="auto">--json</code> prints <code dir="auto">{ key, grammar, grammar_severity, file }</code> (an unset field is null). An unknown domain, neither flag, or a value outside the enums exits 2. CLI only: no MCP tool or HTTP route.</p>
1018
+ <div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="shell"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">domain</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">grammar</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">CHCK</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--severity</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">error</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="spec domain grammar CHCK --severity error"><div></div></button></div></figure></div>
1019
+ <p>Exit: 0 / 2.</p>
1020
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-migrate">spec migrate</h3><a class="sl-anchor-link" href="#spec-migrate"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec migrate”</span></a></div>
1021
+ <p>Rewrites every <code dir="auto">SPEC.json</code> that still carries the retired <code dir="auto">livesIn</code> or <code dir="auto">verifiedIn</code> fields through the write seam, which drops them. A file without them is not touched, so a second run writes nothing. Until a file is migrated, the fields are read and ignored, and the next write of a file drops them.</p>
1022
+ <p><code dir="auto">--json</code>: <code dir="auto">{ files }</code>, the platform-relative paths rewritten.</p>
1023
+ <p>Exit: 0 / 2.</p>
1024
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-req">spec req</h3><a class="sl-anchor-link" href="#spec-req"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec req”</span></a></div>
1025
+ <table>
1026
+ <thead>
1027
+ <tr>
1028
+ <th>Mode</th>
1029
+ <th>Behavior</th>
1030
+ </tr>
1031
+ </thead>
1032
+ <tbody>
1033
+ <tr>
1034
+ <td>stdin not a TTY, no <code dir="auto">--text</code></td>
1035
+ <td>Print the bare next unused id (e.g. <code dir="auto">BILLING-010</code>), exit 0. No prompts, no writes.</td>
1036
+ </tr>
1037
+ <tr>
1038
+ <td><code dir="auto">--json</code>, no <code dir="auto">--text</code></td>
1039
+ <td>Print <code dir="auto">{ domain, next_id }</code>. Same zero-write contract, even on a TTY.</td>
1040
+ </tr>
1041
+ <tr>
1042
+ <td><code dir="auto">--text "&lt;statement&gt;"</code></td>
1043
+ <td>Append an Active entry with zero prompts. <code dir="auto">--why</code>, <code dir="auto">--relates</code>, <code dir="auto">--issue &lt;ticket&gt;</code> fill the other fields and error without <code dir="auto">--text</code>. <code dir="auto">--relates</code> repeats and comma-separates.</td>
1044
+ </tr>
1045
+ <tr>
1046
+ <td><code dir="auto">--draft</code></td>
1047
+ <td>Author the entry as Draft (with <code dir="auto">--text</code>, or on a TTY). Off a TTY without <code dir="auto">--text</code> it exits 2.</td>
1048
+ </tr>
1049
+ <tr>
1050
+ <td>TTY, no flags</td>
1051
+ <td>Interactive authoring.</td>
1052
+ </tr>
1053
+ </tbody>
1054
+ </table>
1055
+ <p><code dir="auto">--json</code> with <code dir="auto">--text</code>: <code dir="auto">{ id, file }</code>, plus <code dir="auto">clauses: { pattern, condition, system, response }</code> when the statement parses as EARS.</p>
1056
+ <p>Exit: 0 / 2 (ambiguous prefix, unknown domain, grammar refusal, statement-rule refusal).</p>
1057
+ <ul>
1058
+ <li>The domain prefix is case-insensitive: <code dir="auto">bil</code> resolves to <code dir="auto">BILLING</code>.</li>
1059
+ <li>The resolved domain’s charter is echoed to stderr on every authoring path.</li>
1060
+ <li>Unresolvable <code dir="auto">@&lt;path&gt;</code> refs warn on stderr and never block.</li>
1061
+ </ul>
1062
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-term">spec term</h3><a class="sl-anchor-link" href="#spec-term"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec term”</span></a></div>
1063
+ <p>A term is a requirement row in the reserved <code dir="auto">TERM</code> domain: definition in <code dir="auto">statement</code>, headword in <code dir="auto">term</code>, synonyms in <code dir="auto">aliases</code>.</p>
1064
+ <table>
1065
+ <thead>
1066
+ <tr>
1067
+ <th>Form</th>
1068
+ <th>Effect</th>
1069
+ </tr>
1070
+ </thead>
1071
+ <tbody>
1072
+ <tr>
1073
+ <td><code dir="auto">spec term "&lt;name&gt;" --def "&lt;definition&gt;" [--aliases a,b] [--section s]</code></td>
1074
+ <td>Append a <code dir="auto">TERM-NNN</code>. <code dir="auto">--json</code>: <code dir="auto">{ id, file }</code>.</td>
1075
+ </tr>
1076
+ <tr>
1077
+ <td><code dir="auto">spec term</code> with no <code dir="auto">--def</code></td>
1078
+ <td>Pure id query, mirrors <code dir="auto">spec req</code>: bare next <code dir="auto">TERM</code> id, or <code dir="auto">{ domain, next_id }</code>.</td>
1079
+ </tr>
1080
+ <tr>
1081
+ <td><code dir="auto">spec term list</code></td>
1082
+ <td><code dir="auto">id name status</code> per line, or <code dir="auto">[{ id, term, status }]</code>. Reads the filesystem.</td>
1083
+ </tr>
1084
+ <tr>
1085
+ <td><code dir="auto">spec term revise &lt;TERM-NNN&gt; --def "…"</code></td>
1086
+ <td>Rewrite the definition in place and bump the TERM envelope’s <code dir="auto">specVersion</code>. <code dir="auto">--no-bump</code> opts out. <code dir="auto">--json</code>: <code dir="auto">{ id, file, spec_version }</code>.</td>
1087
+ </tr>
1088
+ <tr>
1089
+ <td><code dir="auto">spec term confirm &lt;KEY-NNN&gt; &lt;TERM-NNN&gt;</code></td>
1090
+ <td>Re-pin a citation to the term’s current version (clears <code dir="auto">TERM_DRIFT</code>); re-point to the successor when the term is superseded (clears <code dir="auto">SUPERSEDED_TERM_REFERENCED</code>). <code dir="auto">--json</code>: <code dir="auto">{ req_id, term_id, pinned, file }</code>.</td>
1091
+ </tr>
1092
+ </tbody>
1093
+ </table>
1094
+ <p><code dir="auto">spec supersede TERM-NNN</code> and <code dir="auto">spec amend TERM-NNN --term/--aliases</code> work on term ids as on requirements.</p>
1095
+ <p>Exit: 0 / 2.</p>
1096
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-glossary">spec glossary</h3><a class="sl-anchor-link" href="#spec-glossary"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec glossary”</span></a></div>
1097
+ <table>
1098
+ <thead>
1099
+ <tr>
1100
+ <th>Form</th>
1101
+ <th>Effect</th>
1102
+ </tr>
1103
+ </thead>
1104
+ <tbody>
1105
+ <tr>
1106
+ <td><code dir="auto">spec glossary</code></td>
1107
+ <td>Overwrite <code dir="auto">GLOSSARY.md</code> from the TERM store. Byte-stable: fixed header, terms in id order, <code dir="auto">## {section}</code> once per change, one <code dir="auto">- **{term}** — {statement}</code> bullet each.</td>
1108
+ </tr>
1109
+ <tr>
1110
+ <td><code dir="auto">--migrate</code></td>
1111
+ <td>One-time parse of an existing <code dir="auto">GLOSSARY.md</code> into <code dir="auto">TERM-001..N</code>. Skips when the TERM domain already has entries.</td>
1112
+ </tr>
1113
+ <tr>
1114
+ <td><code dir="auto">--check</code></td>
1115
+ <td>Regenerate into a buffer and diff against the committed file. Exit 1 on drift.</td>
1116
+ </tr>
1117
+ </tbody>
1118
+ </table>
1119
+ <p><code dir="auto">--json</code>: <code dir="auto">{ generated }</code>, <code dir="auto">{ migrated }</code>, or <code dir="auto">{ ok }</code>.</p>
1120
+ <p>Exit: 0 / 1 (<code dir="auto">--check</code> drift) / 2.</p>
1121
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-deprecate">spec deprecate</h3><a class="sl-anchor-link" href="#spec-deprecate"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec deprecate”</span></a></div>
1122
+ <p>Marks an Active/Draft requirement <code dir="auto">deprecated</code>, records <code dir="auto">--reason</code> on the entry, and removes every <code dir="auto">@spec</code> tag of the id from the code. A line that holds only the tag (and its comment leader) is deleted; on a line with other text, only the tag and its level token go. Text mode prints the removed sites.</p>
1123
+ <p><code dir="auto">--json</code>: <code dir="auto">{ id, file, reason, removed: [{ req_id, repo, file, line, kind, level }] }</code>, the sites as they stood before the removal.</p>
1124
+ <p>Tag sites come from the freshly built index, so <code dir="auto">fixtures/</code> and dot-prefixed directories are never touched. Each rewritten line keeps its comment leader, level token, and line ending, and no other line changes. Running the command again finds no sites.</p>
1125
+ <p>Exit: 0 / 2.</p>
1126
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-supersede">spec supersede</h3><a class="sl-anchor-link" href="#spec-supersede"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec supersede”</span></a></div>
1127
+ <p>Requires an Active target. Flips it to <code dir="auto">superseded by NEW</code>, mints the successor in the domain file that holds the target, reindexes fresh, and rewrites every <code dir="auto">@spec OLD</code> tag in the code to <code dir="auto">@spec NEW</code>, keeping the level token and the rest of the line. Text mode prints the rewritten sites. Tag sites come from the freshly built index, so <code dir="auto">fixtures/</code> and dot-prefixed directories are never touched. Each rewritten line keeps its comment leader, level token, and line ending, and no other line changes. Running the command again finds no sites.</p>
1128
+ <p>The successor copies the predecessor’s <code dir="auto">why</code>, <code dir="auto">relates</code>, <code dir="auto">cites</code>, <code dir="auto">aliases</code>, <code dir="auto">term</code>, and <code dir="auto">section</code> unless a flag overrides the field. Its <code dir="auto">issues</code> hold only the <code dir="auto">created</code> row from <code dir="auto">--issue</code>.</p>
1129
+ <table>
1130
+ <thead>
1131
+ <tr>
1132
+ <th>Flag</th>
1133
+ <th>Effect</th>
1134
+ </tr>
1135
+ </thead>
1136
+ <tbody>
1137
+ <tr>
1138
+ <td><code dir="auto">--text</code></td>
1139
+ <td>The successor statement. Required when not a TTY.</td>
1140
+ </tr>
1141
+ <tr>
1142
+ <td><code dir="auto">--why</code>, <code dir="auto">--relates</code></td>
1143
+ <td>Successor fields. Default to copies of the predecessor’s. <code dir="auto">--relates</code> repeats or comma-separates and replaces the whole list; an empty value clears it.</td>
1144
+ </tr>
1145
+ <tr>
1146
+ <td><code dir="auto">--issue &lt;ticket&gt;</code></td>
1147
+ <td>Recorded as <code dir="auto">supersedes-via</code> on the predecessor and <code dir="auto">created</code> on the successor.</td>
1148
+ </tr>
1149
+ <tr>
1150
+ <td><code dir="auto">--no-bump</code></td>
1151
+ <td>TERM domain only: keep the authored <code dir="auto">specVersion</code>. No-op elsewhere.</td>
1152
+ </tr>
1153
+ </tbody>
1154
+ </table>
1155
+ <p><code dir="auto">--json</code>: <code dir="auto">{ old_id, new_id, file, spec_version, retagged: [{ req_id, repo, file, line, kind, level }] }</code>, the rewritten sites (<code dir="auto">req_id</code> is the old id). On a requirement domain <code dir="auto">spec_version</code> is the derived domain version after the supersession, and the same number is stamped as the predecessor’s <code dir="auto">supersededAtVersion</code>.</p>
1156
+ <p>Exit: 0 / 2. All guards run before the first byte is written.</p>
1157
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-move">spec move</h3><a class="sl-anchor-link" href="#spec-move"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec move”</span></a></div>
1158
+ <p>Cross-domain supersede. Mints the successor as the next id in <code dir="auto">&lt;NEW-DOMAIN&gt;</code> (which must exist), copies <code dir="auto">statement</code>, <code dir="auto">why</code>, <code dir="auto">relates</code>, <code dir="auto">cites</code>, <code dir="auto">aliases</code>, <code dir="auto">term</code>, and <code dir="auto">section</code> (<code dir="auto">--text</code> and <code dir="auto">--why</code> override), flips the source to superseded with a cross-domain <code dir="auto">supersededBy</code>, and rewrites every tag of the source to the successor as <code dir="auto">spec supersede</code> does. The successor carries no <code dir="auto">issues</code>.</p>
1159
+ <p><code dir="auto">--json</code>: <code dir="auto">{ old_id, new_id, from_file, to_file, source_spec_version, target_spec_version, retagged: [...] }</code>.</p>
1160
+ <p>Exit: 0 / 2 (non-Active source, or the target is the source’s own domain).</p>
1161
+ <ul>
1162
+ <li>Both envelopes validate before either is written.</li>
1163
+ <li>The source version advances; the target version is unchanged.</li>
1164
+ </ul>
1165
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-amend">spec amend</h3><a class="sl-anchor-link" href="#spec-amend"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec amend”</span></a></div>
1166
+ <p>Revise an Active or Draft entry in place. Same id, no version change.</p>
1167
+ <table>
1168
+ <thead>
1169
+ <tr>
1170
+ <th>Flag</th>
1171
+ <th>Effect</th>
1172
+ </tr>
1173
+ </thead>
1174
+ <tbody>
1175
+ <tr>
1176
+ <td><code dir="auto">--text</code>, <code dir="auto">--why</code>, <code dir="auto">--relates</code></td>
1177
+ <td>Field to change. At least one flag is required. Untouched fields stay byte-identical. <code dir="auto">--relates</code> repeats or comma-separates and replaces the whole list (the <code dir="auto">--aliases</code> idiom); an empty value clears it.</td>
1178
+ </tr>
1179
+ <tr>
1180
+ <td><code dir="auto">--issue &lt;ticket&gt;</code></td>
1181
+ <td>Append <code dir="auto">amends-via</code> provenance.</td>
1182
+ </tr>
1183
+ <tr>
1184
+ <td><code dir="auto">--term</code>, <code dir="auto">--aliases</code></td>
1185
+ <td>TERM ids only.</td>
1186
+ </tr>
1187
+ </tbody>
1188
+ </table>
1189
+ <p><code dir="auto">--json</code>: <code dir="auto">{ id, file, fields_changed }</code>.</p>
1190
+ <p>A released requirement is an Active entry present in its domain file at the newest <code dir="auto">v*</code> tag merged into HEAD. Its promise does not change in place: a <code dir="auto">--text</code>, <code dir="auto">--why</code>, <code dir="auto">--term</code>, or <code dir="auto">--aliases</code> amend refuses with exit 2, naming the tag. Two paths stay open: <code dir="auto">spec supersede</code> changes the promise, and an amend of only <code dir="auto">--relates</code> or <code dir="auto">--issue</code> writes in place. With no <code dir="auto">v*</code> tag merged into HEAD, or outside git, every entry is unreleased. A Draft entry always amends.</p>
1191
+ <p>Exit: 0 / 2 (superseded or deprecated target; a released entry’s promise fields).</p>
1192
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-accept">spec accept</h3><a class="sl-anchor-link" href="#spec-accept"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec accept”</span></a></div>
1193
+ <p>Promotes a Draft requirement to Active. Every other field stays byte-identical and the domain version does not move. Reword a draft with <code dir="auto">spec amend</code> first; end one with <code dir="auto">spec deprecate</code>.</p>
1194
+ <p><code dir="auto">--json</code>: <code dir="auto">{ id, file }</code>.</p>
1195
+ <p>Exit: 0 / 2 (the target is not Draft: nothing is written).</p>
1196
+ <ul>
1197
+ <li>A Draft never raises <code dir="auto">ORPHAN_REQ</code> or <code dir="auto">UNVERIFIED_REQ</code> and is not counted in the coverage report. A code tag on one is <code dir="auto">DRAFT_REFERENCED</code> (warning).</li>
1198
+ </ul>
1199
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-lint">spec lint</h3><a class="sl-anchor-link" href="#spec-lint"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec lint”</span></a></div>
1200
+ <p>Every statement-rule finding. Reads <code dir="auto">SPEC.json</code> directly, not the index.</p>
1201
+ <table>
1202
+ <thead>
1203
+ <tr>
1204
+ <th>Form</th>
1205
+ <th>Scope</th>
1206
+ </tr>
1207
+ </thead>
1208
+ <tbody>
1209
+ <tr>
1210
+ <td><code dir="auto">spec lint [platformDir]</code></td>
1211
+ <td>the platform</td>
1212
+ </tr>
1213
+ <tr>
1214
+ <td><code dir="auto">spec lint &lt;KEY&gt; [platformDir]</code></td>
1215
+ <td>one domain</td>
1216
+ </tr>
1217
+ <tr>
1218
+ <td><code dir="auto">spec lint &lt;KEY-NNN&gt; [platformDir]</code></td>
1219
+ <td>one requirement</td>
1220
+ </tr>
1221
+ <tr>
1222
+ <td><code dir="auto">--all</code></td>
1223
+ <td>Draft requirements as well as Active ones</td>
1224
+ </tr>
1225
+ </tbody>
1226
+ </table>
1227
+ <p>Text mode prints <code dir="auto">&lt;id&gt; &lt;rule&gt;: &lt;detail&gt; — &lt;reason&gt;</code> per finding on stdout, then rule 0 on stderr. <code dir="auto">--json</code> prints a sorted array of <code dir="auto">{ req_id, rule, detail, reason, source_file, line }</code>. The TERM domain is never judged. A lone positional that holds <code dir="auto">spec-engine/</code> is the platform dir. CLI only: no MCP tool or HTTP route.</p>
1228
+ <p>Exit: 0 clean / 1 any finding / 2 usage, unknown domain or id, or a bad <code dir="auto">check.json</code>.</p>
1229
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-serve">spec serve</h3><a class="sl-anchor-link" href="#spec-serve"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec serve”</span></a></div>
1230
+ <p>Binds one address: <code dir="auto">127.0.0.1</code>, or <code dir="auto">--host &lt;ip&gt;</code> (a literal loopback, RFC 1918, or <code dir="auto">100.64/10</code> address only; anything else exits 2). <code dir="auto">--port 0</code> (default) picks a free port. <code dir="auto">--probe</code> boots on loopback, fetches <code dir="auto">/</code>, and exits 0 or 1. The startup line, <code dir="auto">/api/platform</code> (<code dir="auto">name</code>), and every page’s title and sidebar name the platform (platform file <code dir="auto">name</code>, else the directory name), never a path; <code dir="auto">spec mcp</code> logs the same name on stderr.</p>
1231
+ <p>A non-loopback <code dir="auto">--host</code> is LAN mode: stdout prints <code dir="auto">http://&lt;ip&gt;:&lt;port&gt;/?token=…</code>; every request needs that token (the query parameter is exchanged for an <code dir="auto">HttpOnly; SameSite=Strict</code> cookie, then a 302 drops it), else 401. A <code dir="auto">Host</code> other than loopback or the bound address is 403. Read-only unless <code dir="auto">--allow-writes</code>: non-GET requests, <code dir="auto">/provenance</code>, and <code dir="auto">/api/provenance?resolve=1</code> answer 403. <code dir="auto">--token</code> / <code dir="auto">SPEC_SERVE_TOKEN</code> pin the token (≥ 16 of <code dir="auto">A-Z a-z 0-9 _ -</code>); otherwise it is 256 random bits.</p>
1232
+ <p>When the reindex after a write refuses on an invalid <code dir="auto">SPEC.json</code> elsewhere on the platform, the route answers 422 <code dir="auto">{ error: "invalid_domain_file", diagnostics: [...] }</code>, the rows in the <code dir="auto">check --json</code> shape (the CLI exits 2 with the same rows).</p>
1233
+ <p>Always-on routes: <code dir="auto">/</code>, <code dir="auto">/requirements[/:id]</code>, <code dir="auto">/propagation/:id</code>, <code dir="auto">/api/coverage</code>, <code dir="auto">/api/report</code>, <code dir="auto">/api/repos</code>, <code dir="auto">/api/platform</code>, <code dir="auto">/api/requirements[/:id]</code>, <code dir="auto">/api/propagation/:id</code>, <code dir="auto">/api/resolve?files=…</code>.</p>
1234
+ <p>Flag-gated, 404 until enabled. <code dir="auto">SPEC_FLAGS</code> is comma-separated; unknown keys are ignored.</p>
1235
+ <table>
1236
+ <thead>
1237
+ <tr>
1238
+ <th><code dir="auto">SPEC_FLAGS</code> key</th>
1239
+ <th>Routes</th>
1240
+ </tr>
1241
+ </thead>
1242
+ <tbody>
1243
+ <tr>
1244
+ <td><code dir="auto">query</code></td>
1245
+ <td><code dir="auto">/query</code>, <code dir="auto">/api/query?q=&amp;limit=</code></td>
1246
+ </tr>
1247
+ <tr>
1248
+ <td><code dir="auto">relations</code></td>
1249
+ <td><code dir="auto">/relations</code>, <code dir="auto">/api/relations[?format=mermaid]</code></td>
1250
+ </tr>
1251
+ <tr>
1252
+ <td><code dir="auto">provenance</code></td>
1253
+ <td><code dir="auto">/provenance</code>, <code dir="auto">/api/provenance[?resolve=1]</code>, <code dir="auto">/api/provenance/by-issue?issue=</code></td>
1254
+ </tr>
1255
+ <tr>
1256
+ <td><code dir="auto">editor</code></td>
1257
+ <td><code dir="auto">/editor</code>, <code dir="auto">POST</code> and <code dir="auto">PUT /api/requirements</code>, <code dir="auto">POST /api/requirements/:id/supersede</code>, <code dir="auto">POST /api/requirements/:id/move</code>, <code dir="auto">POST /api/requirements/:id/deprecate</code>, <code dir="auto">POST /api/requirements/:id/accept</code></td>
1258
+ </tr>
1259
+ <tr>
1260
+ <td><code dir="auto">glossary</code>, <code dir="auto">logs</code></td>
1261
+ <td>Placeholder pages</td>
1262
+ </tr>
1263
+ </tbody>
1264
+ </table>
1265
+ <p>Exit: 0 / 1 / 2.</p>
1266
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-docs">spec docs</h3><a class="sl-anchor-link" href="#spec-docs"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec docs”</span></a></div>
1267
+ <p>Serves the prebuilt docs site on <code dir="auto">127.0.0.1</code>, fully offline.</p>
1268
+ <ul>
1269
+ <li>Takes no platform dir. Registers none of <code dir="auto">--json</code>, <code dir="auto">--out</code>, <code dir="auto">--no-prompt</code>.</li>
1270
+ <li>The npm artifact ships the site at <code dir="auto">dist/docs/</code>. A source checkout serves <code dir="auto">packages/site/dist</code>; run <code dir="auto">bun run build:site</code> first, else exit 2.</li>
1271
+ <li><code dir="auto">--probe</code> mirrors <code dir="auto">serve --probe</code>.</li>
102
1272
  </ul>
103
- <div class="sl-heading-wrapper level-h2"><h2 id="gotchas">Gotchas</h2><a class="sl-anchor-link" href="#gotchas"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Gotchas”</span></a></div>
1273
+ <p>Exit: 0 / 1 / 2.</p>
1274
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-mcp">spec mcp</h3><a class="sl-anchor-link" href="#spec-mcp"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “spec mcp”</span></a></div>
1275
+ <p>MCP server over stdio. Register it as <code dir="auto">{ "command": "spec", "args": ["mcp", "&lt;platformDir&gt;"] }</code>.</p>
1276
+ <table>
1277
+ <thead>
1278
+ <tr>
1279
+ <th>Tool</th>
1280
+ <th>Returns</th>
1281
+ </tr>
1282
+ </thead>
1283
+ <tbody>
1284
+ <tr>
1285
+ <td><code dir="auto">spec_query</code></td>
1286
+ <td>FTS hits, the <code dir="auto">spec query --json</code> shape</td>
1287
+ </tr>
1288
+ <tr>
1289
+ <td><code dir="auto">spec_resolve</code></td>
1290
+ <td>files → requirements</td>
1291
+ </tr>
1292
+ <tr>
1293
+ <td><code dir="auto">spec_req_tags</code></td>
1294
+ <td>requirement → tag sites</td>
1295
+ </tr>
1296
+ <tr>
1297
+ <td><code dir="auto">spec_coverage_report</code></td>
1298
+ <td>per-domain rollup <code dir="auto">{ domain, active, implemented, verified, orphans, unverified }</code></td>
1299
+ </tr>
1300
+ <tr>
1301
+ <td><code dir="auto">spec_check</code></td>
1302
+ <td>diagnostic rows</td>
1303
+ </tr>
1304
+ <tr>
1305
+ <td><code dir="auto">spec_propagation</code></td>
1306
+ <td>per-repo migration state</td>
1307
+ </tr>
1308
+ <tr>
1309
+ <td><code dir="auto">spec_next_id</code></td>
1310
+ <td><code dir="auto">{ domain, next_id }</code>, nothing written</td>
1311
+ </tr>
1312
+ <tr>
1313
+ <td><code dir="auto">spec_get</code></td>
1314
+ <td>one <code dir="auto">Requirement</code> row with <code dir="auto">implemented_in</code> and <code dir="auto">verified_in</code>, or <code dir="auto">[]</code> for an unknown id; the <code dir="auto">spec get --json</code> shape</td>
1315
+ </tr>
1316
+ <tr>
1317
+ <td><code dir="auto">spec_list</code></td>
1318
+ <td><code dir="auto">Requirement</code> rows in (key, seq) order, <code dir="auto">domain</code> and <code dir="auto">status</code> filters; the <code dir="auto">spec list --json</code> shape</td>
1319
+ </tr>
1320
+ <tr>
1321
+ <td><code dir="auto">spec_supersede</code></td>
1322
+ <td><code dir="auto">{ old_id, new_id, file, spec_version, retagged }</code>, the <code dir="auto">spec supersede --json</code> shape; optional <code dir="auto">why</code>, <code dir="auto">relates</code>, <code dir="auto">issue</code> arguments</td>
1323
+ </tr>
1324
+ <tr>
1325
+ <td><code dir="auto">spec_deprecate</code></td>
1326
+ <td><code dir="auto">{ id, file, reason, removed }</code>, the <code dir="auto">spec deprecate --json</code> shape</td>
1327
+ </tr>
1328
+ <tr>
1329
+ <td><code dir="auto">spec_accept</code></td>
1330
+ <td><code dir="auto">{ id, file }</code>, the <code dir="auto">spec accept --json</code> shape</td>
1331
+ </tr>
1332
+ <tr>
1333
+ <td><code dir="auto">spec_req</code></td>
1334
+ <td><code dir="auto">{ id, file, clauses? }</code>, the <code dir="auto">spec req --text --json</code> shape; <code dir="auto">domain</code> (key or prefix) and <code dir="auto">text</code> required, optional <code dir="auto">why</code>, <code dir="auto">relates</code>, <code dir="auto">issue</code>, <code dir="auto">draft</code></td>
1335
+ </tr>
1336
+ <tr>
1337
+ <td><code dir="auto">spec_amend</code></td>
1338
+ <td><code dir="auto">{ id, file, fields_changed }</code>, the <code dir="auto">spec amend --json</code> shape; <code dir="auto">id</code> required, optional <code dir="auto">text</code>, <code dir="auto">why</code>, <code dir="auto">relates</code>, <code dir="auto">issue</code></td>
1339
+ </tr>
1340
+ <tr>
1341
+ <td><code dir="auto">spec_move</code></td>
1342
+ <td><code dir="auto">{ old_id, new_id, from_file, to_file, source_spec_version, target_spec_version, retagged }</code>, the <code dir="auto">spec move --json</code> shape; <code dir="auto">id</code> and <code dir="auto">newDomain</code> required, optional <code dir="auto">text</code>, <code dir="auto">why</code></td>
1343
+ </tr>
1344
+ </tbody>
1345
+ </table>
1346
+ <p>Every tool call reindexes fresh. stdout is the protocol channel; chrome goes to stderr. One prompt, <code dir="auto">author_requirements</code> (<code dir="auto">brief</code> required, <code dir="auto">domain</code> optional), returns a static authoring playbook with the brief and the domain’s charter substituted. The engine runs no model.</p>
1347
+ <p>Exit: 0 / 2.</p>
1348
+ <div class="sl-heading-wrapper level-h3"><h3 id="gotchas">Gotchas</h3><a class="sl-anchor-link" href="#gotchas"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore>Section titled “Gotchas”</span></a></div>
104
1349
  <ul>
105
- <li><strong><code dir="auto">fixtures/</code> is globally ignored</strong> by the tag scanner — planted fixture tags are test data, not coverage claims</li>
106
- <li><strong>The DB owns nothing</strong> — never edit <code dir="auto">.spec-engine/index.sqlite</code>; edit the SPEC.json source and re-run <code dir="auto">spec index</code></li>
107
- <li><strong>Empty ≠ error</strong> — read commands exit 0 with <code dir="auto">[]</code> on stdout and guidance on stderr</li>
108
- <li><strong>Determinism is contractual</strong> — JSON outputs are deterministically sorted, byte-stable across runs</li>
1350
+ <li><strong><code dir="auto">fixtures/</code> is globally ignored by the tag scanner.</strong> Tags under any <code dir="auto">fixtures/</code> folder are test data, not coverage claims.</li>
1351
+ <li><strong>The index owns nothing.</strong> Never edit <code dir="auto">.spec-engine/index.sqlite</code>. Edit the <code dir="auto">SPEC.json</code> source and re-run <code dir="auto">spec index</code>.</li>
1352
+ <li><strong><code dir="auto">storage_unavailable</code> / <code dir="auto">SQLITE_IOERR*</code> means your environment.</strong> The index runs in WAL mode and needs real file locks. A sandboxed process gets <code dir="auto">SQLITE_IOERR_VNODE</code>; re-run unsandboxed or grant write and lock access to <code dir="auto">&lt;platformDir&gt;/.spec-engine/</code>. <code dir="auto">SQLITE_BUSY</code> means another <code dir="auto">spec</code> process holds the index.</li>
1353
+ <li><strong>Empty is not an error.</strong> Read commands exit 0 with <code dir="auto">[]</code> and guidance on stderr.</li>
1354
+ <li><strong>Determinism is contractual.</strong> JSON outputs are sorted and byte-stable.</li>
1355
+ <li>A directory passed as the last positional is <code dir="auto">platformDir</code> only if it contains <code dir="auto">spec-engine/</code>. Otherwise it is treated as a file, with a warning.</li>
109
1356
  </ul>
110
1357
  </div><footer class="sl-flex astro-yaxnhex4"><div class="meta sl-flex astro-yaxnhex4"><a href="https://github.com/spec-engine/spec-engine/edit/main/packages/site/src/content/docs/agent-reference.md" class="sl-flex print:hidden astro-uulcuzsf"><svg aria-hidden="true" class="astro-uulcuzsf astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.2em;"><path d="M22 7.24a1 1 0 0 0-.29-.71l-4.24-4.24a1 1 0 0 0-1.1-.22 1 1 0 0 0-.32.22l-2.83 2.83L2.29 16.05a1 1 0 0 0-.29.71V21a1 1 0 0 0 1 1h4.24a1 1 0 0 0 .76-.29l10.87-10.93L21.71 8c.1-.1.17-.2.22-.33a1 1 0 0 0 0-.24v-.14l.07-.05ZM6.83 20H4v-2.83l9.93-9.93 2.83 2.83L6.83 20ZM18.17 8.66l-2.83-2.83 1.42-1.41 2.82 2.82-1.41 1.42Z"/></svg>Edit page</a></div><div class="pagination-links print:hidden astro-kestnrba" dir="ltr"><a href="/guides/single-repo/" rel="prev" class="astro-kestnrba"><svg aria-hidden="true" class="astro-kestnrba astro-ica5m7eu" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17 11H9.41l3.3-3.29a1.004 1.004 0 1 0-1.42-1.42l-5 5a1 1 0 0 0-.21.33 1 1 0 0 0 0 .76 1 1 0 0 0 .21.33l5 5a1.002 1.002 0 0 0 1.639-.325 1 1 0 0 0-.219-1.095L9.41 13H17a1 1 0 0 0 0-2Z"/></svg><span class="astro-kestnrba">Previous<br class="astro-kestnrba"><span class="link-title astro-kestnrba">Single Repo (Rung 1)</span></span></a></div></footer></div></div></main></div></div></div></div></body></html>