@spec-engine/spec-engine 0.3.0 → 0.4.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 (38) hide show
  1. package/README.md +12 -7
  2. package/dist/docs/agent-reference/index.html +10 -9
  3. package/dist/docs/architecture/index.html +441 -12
  4. package/dist/docs/checks/index.html +43 -3
  5. package/dist/docs/commands/index.html +10 -6
  6. package/dist/docs/format/index.html +2 -2
  7. package/dist/docs/getting-started/index.html +3 -5
  8. package/dist/docs/guides/adoption/index.html +3 -3
  9. package/dist/docs/guides/platform-setup/index.html +6 -6
  10. package/dist/docs/guides/single-repo/index.html +2 -2
  11. package/dist/docs/overview/index.html +2 -2
  12. package/dist/docs/pagefind/fragment/en_01934b1.pf_fragment +0 -0
  13. package/dist/docs/pagefind/fragment/en_1f98c2d.pf_fragment +0 -0
  14. package/dist/docs/pagefind/fragment/en_4560516.pf_fragment +0 -0
  15. package/dist/docs/pagefind/fragment/en_4bd5a86.pf_fragment +0 -0
  16. package/dist/docs/pagefind/fragment/en_73bb90d.pf_fragment +0 -0
  17. package/dist/docs/pagefind/fragment/en_8e3910f.pf_fragment +0 -0
  18. package/dist/docs/pagefind/fragment/en_ace7862.pf_fragment +0 -0
  19. package/dist/docs/pagefind/fragment/en_c3e92c6.pf_fragment +0 -0
  20. package/dist/docs/pagefind/fragment/en_ef547ec.pf_fragment +0 -0
  21. package/dist/docs/pagefind/index/en_a2165b2.pf_index +0 -0
  22. package/dist/docs/pagefind/pagefind-entry.json +1 -1
  23. package/dist/docs/pagefind/pagefind.en_b7799d257e.pf_meta +0 -0
  24. package/dist/docs/platform-mapping/index.html +258 -0
  25. package/dist/docs/sitemap-0.xml +1 -1
  26. package/dist/docs/tags/index.html +43 -3
  27. package/dist/impl.js +3994 -3328
  28. package/package.json +2 -1
  29. package/dist/docs/pagefind/fragment/en_10bec64.pf_fragment +0 -0
  30. package/dist/docs/pagefind/fragment/en_2d1adec.pf_fragment +0 -0
  31. package/dist/docs/pagefind/fragment/en_3b7d939.pf_fragment +0 -0
  32. package/dist/docs/pagefind/fragment/en_6c144c1.pf_fragment +0 -0
  33. package/dist/docs/pagefind/fragment/en_79e59c8.pf_fragment +0 -0
  34. package/dist/docs/pagefind/fragment/en_8d65739.pf_fragment +0 -0
  35. package/dist/docs/pagefind/fragment/en_8ef15c5.pf_fragment +0 -0
  36. package/dist/docs/pagefind/fragment/en_adb4528.pf_fragment +0 -0
  37. package/dist/docs/pagefind/index/en_90d86c1.pf_index +0 -0
  38. package/dist/docs/pagefind/pagefind.en_4cf42761c7.pf_meta +0 -0
@@ -0,0 +1,258 @@
1
+ <!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-ioexmpvj"> <head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Platform Mapping | Spec Engine</title><link rel="canonical" href="https://docs.spec-engine.dev/platform-mapping/"/><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 v6.4.8"/><meta name="generator" content="Starlight v0.40.0"/><meta property="og:title" content="Platform Mapping"/><meta property="og:type" content="article"/><meta property="og:url" content="https://docs.spec-engine.dev/platform-mapping/"/><meta property="og:locale" content="en"/><meta property="og:description" content="How Spec Engine decides what a platform is, which repositories belong to it, and what each coverage column is named"/><meta property="og:site_name" content="Spec Engine"/><meta name="twitter:card" content="summary_large_image"/><meta name="description" content="How Spec Engine decides what a platform is, which repositories belong to it, and what each coverage column is named"/><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
+ window.StarlightThemeProvider = (() => {
3
+ const storedTheme =
4
+ typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
5
+ const theme =
6
+ storedTheme ||
7
+ (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
8
+ document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
9
+ return {
10
+ updatePickers(theme = storedTheme || 'auto') {
11
+ document.querySelectorAll('starlight-theme-select').forEach((picker) => {
12
+ const select = picker.querySelector('select');
13
+ if (select) select.value = theme;
14
+ /** @type {HTMLTemplateElement | null} */
15
+ const tmpl = document.querySelector(`#theme-icons`);
16
+ const newIcon = tmpl && tmpl.content.querySelector('.' + theme);
17
+ if (newIcon) {
18
+ const oldIcon = picker.querySelector('svg.label-icon');
19
+ if (oldIcon) {
20
+ oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
21
+ }
22
+ }
23
+ });
24
+ },
25
+ };
26
+ })();
27
+ </script><template id="theme-icons"><svg aria-hidden="true" class="light astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M5 12a1 1 0 0 0-1-1H3a1 1 0 0 0 0 2h1a1 1 0 0 0 1-1Zm.64 5-.71.71a1 1 0 0 0 0 1.41 1 1 0 0 0 1.41 0l.71-.71A1 1 0 0 0 5.64 17ZM12 5a1 1 0 0 0 1-1V3a1 1 0 0 0-2 0v1a1 1 0 0 0 1 1Zm5.66 2.34a1 1 0 0 0 .7-.29l.71-.71a1 1 0 1 0-1.41-1.41l-.66.71a1 1 0 0 0 0 1.41 1 1 0 0 0 .66.29Zm-12-.29a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.71-.71a1.004 1.004 0 1 0-1.43 1.41l.73.71ZM21 11h-1a1 1 0 0 0 0 2h1a1 1 0 0 0 0-2Zm-2.64 6A1 1 0 0 0 17 18.36l.71.71a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.76-.66ZM12 6.5a5.5 5.5 0 1 0 5.5 5.5A5.51 5.51 0 0 0 12 6.5Zm0 9a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7Zm0 3.5a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1a1 1 0 0 0-1-1Z"/></svg><svg aria-hidden="true" class="dark astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8.049 8.049 0 0 1-3.37.73 8.15 8.15 0 0 1-8.14-8.1 8.59 8.59 0 0 1 .25-2A1 1 0 0 0 8 2.36a10.14 10.14 0 1 0 14 11.69 1 1 0 0 0-.36-1.05Zm-9.5 6.69A8.14 8.14 0 0 1 7.08 5.22v.27a10.15 10.15 0 0 0 10.14 10.14 9.784 9.784 0 0 0 2.1-.22 8.11 8.11 0 0 1-7.18 4.32v-.04Z"/></svg><svg aria-hidden="true" class="auto astro-lm5mbbcm" 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></template><link rel="stylesheet" href="/_astro/print.DNXP8c50.css" media="print"><link rel="stylesheet" href="/_astro/common.DslAiFsT.css"><link rel="stylesheet" href="/_astro/Code.CAimF0uL.css"><script type="module" src="/_astro/page.B_tncCx8.js"></script></head> <body class="astro-ioexmpvj"> <a href="#_top" class="astro-exkej44h">Skip to content</a> <div class="page sl-flex astro-to3xypmc"> <header class="header astro-to3xypmc"><div class="header astro-shhymrmb"> <div class="title-wrapper sl-flex astro-shhymrmb"> <a href="/" class="site-title sl-flex astro-6qdjqr3f"> <img class="light:sl-hidden print:hidden astro-6qdjqr3f" alt="Spec Engine" src="/_astro/spec-mark-white.DJBFLLUP.svg" width="512" height="512"> <img class="dark:sl-hidden print:block astro-6qdjqr3f" alt="Spec Engine" src="/_astro/spec-mark-black.ZCN22lJi.svg" width="512" height="512"> <span class="astro-6qdjqr3f" translate="no"> Spec Engine </span> </a> </div> <div class="sl-flex print:hidden astro-shhymrmb"> <site-search class="astro-shhymrmb astro-7vagb36t" data-translations="{&quot;placeholder&quot;:&quot;Search&quot;}"> <button data-open-modal disabled aria-label="Search" aria-keyshortcuts="Control+K" class="astro-7vagb36t"> <svg aria-hidden="true" class="astro-7vagb36t astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.71 20.29 18 16.61A9 9 0 1 0 16.61 18l3.68 3.68a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.39ZM11 18a7 7 0 1 1 0-14 7 7 0 0 1 0 14Z"/></svg> <span class="sl-hidden md:sl-block astro-7vagb36t" aria-hidden="true">Search</span> <kbd class="sl-hidden md:sl-flex astro-7vagb36t" style="display: none;"> <kbd class="astro-7vagb36t">Ctrl</kbd><kbd class="astro-7vagb36t">K</kbd> </kbd> </button> <dialog style="padding:0" aria-label="Search" class="astro-7vagb36t"> <div class="dialog-frame sl-flex astro-7vagb36t"> <button data-close-modal class="sl-flex md:sl-hidden astro-7vagb36t"> Cancel </button> <div class="search-container astro-7vagb36t"> <div id="starlight__search" class="astro-7vagb36t"></div> </div> </div> </dialog> </site-search> <script>
28
+ (() => {
29
+ const openBtn = document.querySelector('button[data-open-modal]');
30
+ const shortcut = openBtn?.querySelector('kbd');
31
+ if (!openBtn || !(shortcut instanceof HTMLElement)) return;
32
+ const platformKey = shortcut.querySelector('kbd');
33
+ if (platformKey && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
34
+ platformKey.textContent = '⌘';
35
+ openBtn.setAttribute('aria-keyshortcuts', 'Meta+K');
36
+ }
37
+ shortcut.style.display = '';
38
+ })();
39
+ </script> <script type="module" src="/_astro/Search.astro_astro_type_script_index_0_lang.lq3t8uE2.js"></script> </div> <div class="sl-hidden md:sl-flex print:hidden right-group astro-shhymrmb"> <div class="sl-flex social-icons astro-shhymrmb"> <a href="https://github.com/spec-engine/spec-engine" rel="me" class="sl-flex astro-opyhioab"><span class="sr-only astro-opyhioab">GitHub</span><svg aria-hidden="true" class="astro-opyhioab astro-lm5mbbcm" 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-oxpvutiw"> <span class="sr-only astro-oxpvutiw">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-oxpvutiw astro-lm5mbbcm" 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-oxpvutiw"> <option value="dark" class="astro-oxpvutiw">Dark</option><option value="light" class="astro-oxpvutiw">Light</option><option value="auto" selected class="astro-oxpvutiw">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-oxpvutiw astro-lm5mbbcm" 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>
40
+ StarlightThemeProvider.updatePickers();
41
+ </script> <script type="module">const r="starlight-theme",o=e=>e==="auto"||e==="dark"||e==="light"?e:"auto",c=()=>o(typeof localStorage<"u"&&localStorage.getItem(r));function n(e){typeof localStorage<"u"&&localStorage.setItem(r,e==="light"||e==="dark"?e:"")}const l=()=>matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";function t(e){StarlightThemeProvider.updatePickers(e),document.documentElement.dataset.theme=e==="auto"?l():e,n(e)}matchMedia("(prefers-color-scheme: light)").addEventListener("change",()=>{c()==="auto"&&t("auto")});class s extends HTMLElement{constructor(){super(),t(c()),this.querySelector("select")?.addEventListener("change",a=>{a.currentTarget instanceof HTMLSelectElement&&t(o(a.currentTarget.value))})}}customElements.define("starlight-theme-select",s);</script> <script type="module">class s extends HTMLElement{constructor(){super();const e=this.querySelector("select");e&&(e.addEventListener("change",t=>{t.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=t.currentTarget.value)}),window.addEventListener("pageshow",t=>{if(!t.persisted)return;const n=e.querySelector("option[selected]")?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}}customElements.define("starlight-lang-select",s);</script> </div> </div></header> <nav class="sidebar print:hidden astro-to3xypmc" aria-label="Main"> <starlight-menu-button class="print:hidden astro-l43anhaf"> <button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-l43anhaf"> <svg aria-hidden="true" class="open-menu astro-l43anhaf astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg> <svg aria-hidden="true" class="close-menu astro-l43anhaf astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg> </button> </starlight-menu-button> <script type="module">class s extends HTMLElement{constructor(){super(),this.btn=this.querySelector("button"),this.btn.addEventListener("click",()=>this.toggleExpanded());const t=this.closest("nav");t&&t.addEventListener("keyup",e=>this.closeOnEscape(e))}setExpanded(t){this.setAttribute("aria-expanded",String(t)),document.body.toggleAttribute("data-mobile-menu-expanded",t)}toggleExpanded(){this.setExpanded(this.getAttribute("aria-expanded")!=="true")}closeOnEscape(t){t.code==="Escape"&&(this.setExpanded(!1),this.btn.focus())}}customElements.define("starlight-menu-button",s);</script> <div id="starlight__sidebar" class="sidebar-pane astro-to3xypmc"> <div class="sidebar-content sl-flex astro-to3xypmc"> <sl-sidebar-state-persist data-hash="1dxffrg" class="astro-znqwvy6e"> <script aria-hidden="true">
42
+ (() => {
43
+ try {
44
+ if (!matchMedia('(min-width: 50em)').matches) return;
45
+ /** @type {HTMLElement | null} */
46
+ const target = document.querySelector('sl-sidebar-state-persist');
47
+ const state = JSON.parse(sessionStorage.getItem('sl-sidebar-state') || '0');
48
+ if (!target || !state || target.dataset.hash !== state.hash) return;
49
+ window._starlightScrollRestore = state.scroll;
50
+ customElements.define(
51
+ 'sl-sidebar-restore',
52
+ class SidebarRestore extends HTMLElement {
53
+ connectedCallback() {
54
+ try {
55
+ const idx = parseInt(this.dataset.index || '');
56
+ const details = this.closest('details');
57
+ if (details && typeof state.open[idx] === 'boolean') details.open = state.open[idx];
58
+ } catch {}
59
+ }
60
+ }
61
+ );
62
+ } catch {}
63
+ })();
64
+ </script> <ul class="top-level astro-ux7joveq"> <li class="astro-ux7joveq"> <a href="/overview/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Overview</span> </a> </li><li class="astro-ux7joveq"> <a href="/getting-started/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Getting Started</span> </a> </li><li class="astro-ux7joveq"> <a href="/architecture/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Architecture</span> </a> </li><li class="astro-ux7joveq"> <a href="/platform-mapping/" aria-current="page" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Platform Mapping</span> </a> </li><li class="astro-ux7joveq"> <a href="/commands/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Commands</span> </a> </li><li class="astro-ux7joveq"> <a href="/format/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">SPEC.json Format</span> </a> </li><li class="astro-ux7joveq"> <a href="/tags/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Tags</span> </a> </li><li class="astro-ux7joveq"> <a href="/checks/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Integrity Checks</span> </a> </li><li class="astro-ux7joveq"> <details open class="astro-ux7joveq"> <summary class="astro-ux7joveq"> <span class="group-label astro-ux7joveq"> <span class="large astro-ux7joveq">Guides</span> </span> <svg aria-hidden="true" class="caret astro-ux7joveq astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><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> </summary> <sl-sidebar-restore data-index="0"></sl-sidebar-restore> <ul class="astro-ux7joveq"> <li class="astro-ux7joveq"> <a href="/guides/adoption/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Adoption Guide</span> </a> </li><li class="astro-ux7joveq"> <a href="/guides/platform-setup/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Platform Setup</span> </a> </li><li class="astro-ux7joveq"> <a href="/guides/single-repo/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Single Repo (Rung 1)</span> </a> </li> </ul> </details> </li><li class="astro-ux7joveq"> <a href="/agent-reference/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Agent Reference</span> </a> </li> </ul> <script aria-hidden="true">
65
+ (() => {
66
+ const scroller = document.getElementById('starlight__sidebar');
67
+ if (!window._starlightScrollRestore || !scroller) return;
68
+ scroller.scrollTop = window._starlightScrollRestore;
69
+ delete window._starlightScrollRestore;
70
+ })();
71
+ </script> </sl-sidebar-state-persist> <div class="md:sl-hidden"> <div class="mobile-preferences sl-flex astro-2lx56rho"> <div class="social-icons astro-2lx56rho"> <a href="https://github.com/spec-engine/spec-engine" rel="me" class="sl-flex astro-opyhioab"><span class="sr-only astro-opyhioab">GitHub</span><svg aria-hidden="true" class="astro-opyhioab astro-lm5mbbcm" 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-oxpvutiw"> <span class="sr-only astro-oxpvutiw">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-oxpvutiw astro-lm5mbbcm" 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-oxpvutiw"> <option value="dark" class="astro-oxpvutiw">Dark</option><option value="light" class="astro-oxpvutiw">Light</option><option value="auto" selected class="astro-oxpvutiw">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-oxpvutiw astro-lm5mbbcm" 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
+ StarlightThemeProvider.updatePickers();
73
+ </script> </div> </div> </div> </div> </nav> <div class="main-frame astro-to3xypmc"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-blhl7r6q"> <aside class="right-sidebar-container print:hidden astro-blhl7r6q"> <div class="right-sidebar astro-blhl7r6q"> <div class="lg:sl-hidden astro-l5m3cfel"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-goxitxj7"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-goxitxj7"><details id="starlight__mobile-toc" class="astro-goxitxj7"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-goxitxj7"><span class="toggle sl-flex astro-goxitxj7">On this page<svg aria-hidden="true" class="caret astro-goxitxj7 astro-lm5mbbcm" 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-goxitxj7"></span></summary><div class="dropdown astro-goxitxj7"><ul class="isMobile astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#two-files-one-convention" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Two files, one convention</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#the-platform-root" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">The platform root</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#members" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Members</span> </a> <ul class="isMobile astro-xz67lhkg" style="--depth: 1;"> <li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#two-ignore-lists" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">Two ignore lists</span> </a> </li> </ul> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#monorepo-members-and-their-packages" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Monorepo members and their packages</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#a-lone-repository" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">A lone repository</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#names" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Names</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#diagnostics" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Diagnostics</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#where-spec-init-may-write" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Where spec init may write</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#the-webapps-scan-mode-tile" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">The webapp’s scan-mode tile</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#determinism" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Determinism</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#declaring-a-platform" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Declaring a platform</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#a-future-input-dependson" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">A future input: dependsOn</span> </a> </li> </ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.hwBsy0Mo.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-l5m3cfel"><div class="sl-container astro-l5m3cfel"><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-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#two-files-one-convention" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Two files, one convention</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#the-platform-root" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">The platform root</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#members" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Members</span> </a> <ul class="astro-xz67lhkg" style="--depth: 1;"> <li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#two-ignore-lists" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">Two ignore lists</span> </a> </li> </ul> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#monorepo-members-and-their-packages" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Monorepo members and their packages</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#a-lone-repository" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">A lone repository</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#names" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Names</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#diagnostics" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Diagnostics</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#where-spec-init-may-write" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Where spec init may write</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#the-webapps-scan-mode-tile" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">The webapp’s scan-mode tile</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#determinism" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Determinism</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#declaring-a-platform" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Declaring a platform</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#a-future-input-dependson" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">A future input: dependsOn</span> </a> </li> </ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.FuRcXuRY.js"></script></div></div> </div> </aside> <div class="main-pane astro-blhl7r6q"> <main data-pagefind-body class="astro-ioexmpvj" lang="en" dir="ltr"> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <h1 id="_top" class="astro-4b4mtvou">Platform Mapping</h1> </div> </div> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <div class="sl-markdown-content"> <p>Spec Engine does not have its own idea of what a platform is. It takes membership and shape from <a href="https://www.npmjs.com/package/@spec-engine/platform-map"><code dir="auto">@spec-engine/platform-map</code></a>, the same package every tool built on Spec Engine uses, and adds exactly one fact of its own per member: the version pin. <!-- @spec INIT-037 --> platform-map is a dependency of the engine and is called as a library: <code dir="auto">spec init</code> declares members through it, <code dir="auto">spec check</code> runs its checks underneath, and no <code dir="auto">spec</code> workflow asks you to run a platform-map command yourself. This page states the model in platform-map’s vocabulary and names the requirement each rule carries.</p>
74
+ <div class="sl-heading-wrapper level-h2"><h2 id="two-files-one-convention">Two files, one convention</h2><a class="sl-anchor-link" href="#two-files-one-convention"><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 “Two files, one convention”</span></a></div>
75
+ <p>platform-map works from two small committed files that share one name, <code dir="auto">platform-map.json</code>. Which one a file is depends on its keys.</p>
76
+ <p><strong>The platform file</strong> lives at the platform root and declares the members:</p>
77
+ <div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.v4551.css"><script type="module" src="/_astro/ec.0vx5m.js"></script><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">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">acme</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"members"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">api</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">webapp</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">mobile</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">shared</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">], </span><span style="--0:#7FDBCA;--1:#096E72">"ignore"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">scratch</span><span style="--0:#D9F5DD;--1:#111111">"</span><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="{ &#x22;name&#x22;: &#x22;acme&#x22;, &#x22;members&#x22;: [&#x22;api&#x22;, &#x22;webapp&#x22;, &#x22;mobile&#x22;, &#x22;shared&#x22;], &#x22;ignore&#x22;: [&#x22;scratch&#x22;] }"><div></div></button></div></figure></div>
78
+ <p><strong>The leaf marker</strong> lives inside each member and points back at the platform:</p>
79
+ <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">"platform"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">acme</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"member"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">api</span><span style="--0:#D9F5DD;--1:#111111">"</span><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="{ &#x22;platform&#x22;: &#x22;acme&#x22;, &#x22;member&#x22;: &#x22;api&#x22; }"><div></div></button></div></figure></div>
80
+ <p>Neither file contains a path. Members are child directories of the platform root, named after their member name. A checkout that lives somewhere else is recorded in the per-user file <code dir="auto">~/.config/platform-map/platforms.json</code>, which <code dir="auto">spec init --platform &#x3C;dir></code> writes through platform-map’s <code dir="auto">link</code> and which is never committed or edited by hand. <!-- @spec INIT-039 --></p>
81
+ <p>Spec Engine adds one more committed file per member, <code dir="auto">spec-engine.member.json</code>. It carries the member’s pin and its scan ignore list, and it says nothing about membership:</p>
82
+ <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">"specs"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">spec-engine@17</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"ignore"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">generated</span><span style="--0:#D9F5DD;--1:#111111">"</span><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="{ &#x22;specs&#x22;: &#x22;spec-engine@17&#x22;, &#x22;ignore&#x22;: [&#x22;generated&#x22;] }"><div></div></button></div></figure></div>
83
+ <div class="sl-heading-wrapper level-h2"><h2 id="the-platform-root">The platform root</h2><a class="sl-anchor-link" href="#the-platform-root"><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 platform root”</span></a></div>
84
+ <p>The platform root is the directory that holds the canonical <code dir="auto">spec-engine/</code> tree. When the platform is declared, the same directory holds the platform file. Every <code dir="auto">spec</code> command resolves its root through platform-map’s <code dir="auto">locate()</code>: from wherever the command ran, walk upward to the nearest directory holding a <code dir="auto">platform-map.json</code> or a <code dir="auto">.git</code> entry, stopping at the home directory or the filesystem root, then resolve a marker to its platform through the parent directory or the per-user file. A root that <code dir="auto">locate()</code> finds but that holds no <code dir="auto">spec-engine/</code> directory is not a Spec Engine platform, and the command exits 2. <!-- @spec INIT-034 --></p>
85
+ <p>The index-building commands take the platform directory as an argument and ask platform-map to describe that directory. A directory that platform-map does not recognize as a starting point, one with neither a <code dir="auto">.git</code> entry nor a <code dir="auto">platform-map.json</code> inside a larger repository (a spec tree kept in a subfolder, such as a committed fixture), is mapped as a lone single repository: one coverage column named by the directory, and no platform-map diagnostics.</p>
86
+ <p>The platform version is derived, never authored. It is the maximum of the domain versions under <code dir="auto">spec-engine/</code>. A domain file that fails to parse contributes nothing to that number and is reported as <code dir="auto">INVALID_DOMAIN_FILE</code> at parse time; the walk itself never fails on it. <!-- @spec INIT-029 --></p>
87
+ <div class="sl-heading-wrapper level-h2"><h2 id="members">Members</h2><a class="sl-anchor-link" href="#members"><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 “Members”</span></a></div>
88
+ <p>A member is a name in the platform file’s <code dir="auto">members</code> list. Nothing else makes a directory a member: not a <code dir="auto">.git</code> entry, not a <code dir="auto">package.json</code>, not a <code dir="auto">spec-engine.member.json</code>. A repository sitting in the platform folder that nobody declared is platform-map’s <code dir="auto">UNLISTED_REPO</code>, and <code dir="auto">spec check</code> reports it so the repository’s <code dir="auto">@spec</code> tags never go unscanned in silence.</p>
89
+ <p><code dir="auto">spec init &#x3C;name></code> is the one command that makes a repository a member. It declares the repository through platform-map’s <code dir="auto">init</code> (an entry in the platform file and a marker in the repository), then writes the repository’s <code dir="auto">spec-engine.member.json</code>. Run on a repository that is already declared, it only writes the pin, and puts back a missing marker. <!-- @spec INIT-038 --></p>
90
+ <p>A declared member that has no <code dir="auto">spec-engine.member.json</code> is a member without a pin. The index reports it as <code dir="auto">NO_SPEC_CONFIG</code>, one warning per member, and suggests <code dir="auto">spec init &#x3C;name></code>. <!-- @spec INIT-032 --> When stdin is a terminal and <code dir="auto">--no-prompt</code> is absent, an index-building command offers to run <code dir="auto">spec init &#x3C;name></code> before it proceeds, for each declared member without a pin and for each undeclared repository in the platform folder. <!-- @spec INIT-033 --></p>
91
+ <p>A member’s pin is the <code dir="auto">specs</code> string in its <code dir="auto">spec-engine.member.json</code>, one platform-wide scalar <code dir="auto">spec-engine@N</code>.</p>
92
+ <div class="sl-heading-wrapper level-h3"><h3 id="two-ignore-lists">Two <code dir="auto">ignore</code> lists</h3><a class="sl-anchor-link" href="#two-ignore-lists"><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 “Two ignore lists”</span></a></div>
93
+ <p>Two files carry a field named <code dir="auto">ignore</code>, and they mean different things.</p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+ <table><thead><tr><th>File</th><th>Field</th><th>What it skips</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">platform-map.json</code> at the platform root</td><td><code dir="auto">ignore</code></td><td>Directories platform-map never considers a repository, so they are never candidates and never <code dir="auto">UNLISTED_REPO</code></td><td><code dir="auto">"ignore": ["scratch"]</code> keeps a scratch folder out of the platform</td></tr><tr><td><code dir="auto">spec-engine.member.json</code> in a member</td><td><code dir="auto">ignore</code></td><td>Repo-relative directory prefixes the tag scanner skips inside that one member</td><td><code dir="auto">"ignore": ["generated"]</code> stops tags in generated code from counting</td></tr></tbody></table>
118
+ <p>Neither list removes a member from the platform. To stop scanning a member entirely, remove it from the platform file.</p>
119
+ <div class="sl-heading-wrapper level-h2"><h2 id="monorepo-members-and-their-packages">Monorepo members and their packages</h2><a class="sl-anchor-link" href="#monorepo-members-and-their-packages"><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 “Monorepo members and their packages”</span></a></div>
120
+ <p>platform-map classifies every member as <code dir="auto">single-repo</code> or <code dir="auto">monorepo</code> from its ecosystem’s workspace manifest: <code dir="auto">pnpm-workspace.yaml</code>, <code dir="auto">package.json</code> <code dir="auto">workspaces</code>, <code dir="auto">lerna.json</code>, a <code dir="auto">uv</code> workspace, a Cargo workspace, or <code dir="auto">go.work</code>. A monorepo member’s packages are the directories its workspace manifest lists.</p>
121
+ <p>Each package is its own coverage column, named by the package’s path relative to the member (<code dir="auto">packages/ui</code>), prefixed by the member name when the member belongs to a declared platform (<code dir="auto">shared/packages/ui</code>). A package inherits its member’s pin unless it carries a nested <code dir="auto">spec-engine.member.json</code>, whose pin and <code dir="auto">ignore</code> then apply to that package alone. That is how one package can sit on <code dir="auto">spec-engine@2</code> while a sibling lags on <code dir="auto">@1</code>.</p>
122
+ <p>There is no <code dir="auto">members</code> glob in <code dir="auto">spec-engine.member.json</code>. The workspace manifest is the one place a monorepo names its packages.</p>
123
+ <div class="sl-heading-wrapper level-h2"><h2 id="a-lone-repository">A lone repository</h2><a class="sl-anchor-link" href="#a-lone-repository"><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 “A lone repository”</span></a></div>
124
+ <p>A repository that holds its own <code dir="auto">spec-engine/</code> tree and is not a declared platform is a lone repository. platform-map maps it as <code dir="auto">single-repo</code> or <code dir="auto">monorepo</code> with one repo entry, and Spec Engine scans that repo without any member config or CI gate. <!-- @spec INIT-035 --></p>
125
+ <p>A lone <code dir="auto">single-repo</code> is one coverage column named by the directory name. A lone <code dir="auto">monorepo</code> has one coverage column per workspace package, named by the package’s repo-relative path (<code dir="auto">packages/engine</code>), with no member prefix. Every lone-repository column, and the canonical <code dir="auto">spec-engine</code> row itself, is pinned to the derived platform version, so a repository is never reported drifted against its own working-tree domains. A nested <code dir="auto">spec-engine.member.json</code> inside a package still overrides that package’s pin. <!-- @spec INIT-028 --></p>
126
+ <p>This repository is a lone <code dir="auto">monorepo</code>. Its coverage columns are <code dir="auto">packages/engine</code>, <code dir="auto">packages/shared</code>, <code dir="auto">packages/site</code>, <code dir="auto">packages/tracker</code>, <code dir="auto">packages/webapp</code>, and <code dir="auto">scripts</code>, each a workspace package of the root <code dir="auto">package.json</code>.</p>
127
+ <div class="sl-heading-wrapper level-h2"><h2 id="names">Names</h2><a class="sl-anchor-link" href="#names"><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 “Names”</span></a></div>
128
+ <p>One name serves everywhere. For a declared member it is the member name from the platform file. For a package it is the member name and the package path joined by <code dir="auto">/</code>, or the bare package path in a lone monorepo. That same string is:</p>
129
+ <ul>
130
+ <li>the coverage column in <code dir="auto">spec map</code>,</li>
131
+ <li>the prefix of every <code dir="auto">tags.file</code> value, so <code dir="auto">spec resolve &#x3C;name>/&#x3C;path></code> finds the tag,</li>
132
+ <li>the <code dir="auto">repo</code> argument of <code dir="auto">spec gate &#x3C;repo> &#x3C;KEY-NNN></code>,</li>
133
+ <li>the <code dir="auto">repo</code> field of every <code dir="auto">spec propagation</code> row.</li>
134
+ </ul>
135
+ <p>The canonical <code dir="auto">spec-engine/</code> tree is one more row named <code dir="auto">spec-engine</code>. It holds requirement text, never code, and the webapp excludes it from every member count.</p>
136
+ <div class="sl-heading-wrapper level-h2"><h2 id="diagnostics">Diagnostics</h2><a class="sl-anchor-link" href="#diagnostics"><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 “Diagnostics”</span></a></div>
137
+ <p>platform-map reports what it finds as diagnostics on the map. <code dir="auto">spec check</code> surfaces the ones that describe a broken or incomplete platform as rows of its own, with the platform-map code, so a broken platform declaration fails the same gate a broken requirement does.</p>
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+ <table><thead><tr><th>platform-map code</th><th>platform-map severity</th><th><code dir="auto">spec check</code> severity</th><th>Meaning</th></tr></thead><tbody><tr><td><code dir="auto">MALFORMED_FILE</code></td><td>error, or warning for a manifest</td><td>same</td><td>A <code dir="auto">platform-map.json</code>, a manifest, or the per-user file failed to parse or validate</td></tr><tr><td><code dir="auto">MARKER_MISMATCH</code></td><td>error</td><td>error</td><td>A member’s marker names a different platform</td></tr><tr><td><code dir="auto">MEMBER_MISSING</code></td><td>warning</td><td>warning</td><td>A declared member is not on this machine; it is not scanned</td></tr><tr><td><code dir="auto">MARKER_MISSING</code></td><td>warning</td><td>warning</td><td>A declared member has no marker</td></tr><tr><td><code dir="auto">PLATFORM_NOT_LOCATED</code></td><td>warning</td><td>warning</td><td>A marker names a platform this machine cannot find</td></tr><tr><td><code dir="auto">SCAN_TRUNCATED</code></td><td>warning</td><td>warning</td><td>A directory walk hit its depth or entry cap</td></tr><tr><td><code dir="auto">UNLISTED_REPO</code></td><td>info</td><td>warning</td><td>A repository in the platform folder is not a member; its tags are not scanned until <code dir="auto">spec init &#x3C;name></code> declares it</td></tr><tr><td><code dir="auto">UNDECLARED_PLATFORM</code></td><td>info</td><td>warning</td><td>A folder of repositories with no platform file; nothing in it is a member until <code dir="auto">spec init &#x3C;name></code> declares the first one</td></tr><tr><td><code dir="auto">UNMATCHED_PATTERN</code></td><td>info</td><td>not surfaced</td><td>A workspace glob matched no package</td></tr><tr><td><code dir="auto">AMBIGUOUS_ECOSYSTEM</code></td><td>info</td><td>not surfaced</td><td>A repo has manifests from more than one ecosystem</td></tr></tbody></table>
210
+ <p>The rule behind the table: every platform-map error and warning keeps its severity, and an info diagnostic is promoted to a warning only when it means a repository’s tags are silently not being scanned. <!-- @spec CHCK-031 --> A row’s <code dir="auto">source_file</code> is the diagnostic’s subject; <code dir="auto">repo</code> is the subject when it names a member of the platform. A message that would tell you to run a platform-map command is reworded to the <code dir="auto">spec init</code> form that does the same. <code dir="auto">NO_SPEC_CONFIG</code> stays Spec Engine’s own code, because the pin is Spec Engine’s own file.</p>
211
+ <div class="sl-heading-wrapper level-h2"><h2 id="where-spec-init-may-write">Where <code dir="auto">spec init</code> may write</h2><a class="sl-anchor-link" href="#where-spec-init-may-write"><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 “Where spec init may write”</span></a></div>
212
+ <p><code dir="auto">spec init &#x3C;dir></code> declares a member and writes its <code dir="auto">spec-engine.member.json</code>. Where the target sits decides what is written:</p>
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+ <table><thead><tr><th>The target</th><th>What <code dir="auto">spec init</code> writes</th></tr></thead><tbody><tr><td>A repository directly under a platform folder (a directory holding <code dir="auto">spec-engine/</code>) that the platform file does not list</td><td>The platform file entry (the file is created when absent), the marker, and the pin</td></tr><tr><td>A declared member</td><td>The pin; a missing marker is written back</td></tr><tr><td>A workspace package of a monorepo, lone or member</td><td>A nested pin, no declaration</td></tr><tr><td>A plain folder platform-map cannot declare (no <code dir="auto">.git</code> entry, no <code dir="auto">package.json</code>), or a repository inside a lone monorepo that is not a workspace package</td><td>Nothing: exit 2, because a pin there would index nothing</td></tr><tr><td>A directory under no platform</td><td>The <code dir="auto">spec-engine@1</code> fallback pin</td></tr><tr><td>A declared member’s checkout outside the platform folder, with <code dir="auto">--platform &#x3C;dir></code></td><td>The per-user location file, then the pin</td></tr></tbody></table>
247
+ <p>It resolves symlinks, locates the platform root, and refuses two targets with exit 2: a directory inside a <code dir="auto">spec-engine/</code> tree, because a pin there registers the requirement source as a member of itself, and the platform root itself, because the root is not a member. <!-- @spec INIT-018 --></p>
248
+ <p>The refusal reads every path segment for the name <code dir="auto">spec-engine</code>. On the engine’s own checkout, whose root package is named <code dir="auto">@spec-engine/spec-engine</code>, only the path below the platform root is judged, so a checkout named <code dir="auto">spec-engine</code> can still scaffold its own packages. <!-- @spec INIT-030 --></p>
249
+ <p>The default pin is the derived platform version. With no located platform, <code dir="auto">spec init</code> writes <code dir="auto">spec-engine@1</code> and says so.</p>
250
+ <div class="sl-heading-wrapper level-h2"><h2 id="the-webapps-scan-mode-tile">The webapp’s scan-mode tile</h2><a class="sl-anchor-link" href="#the-webapps-scan-mode-tile"><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 webapp’s scan-mode tile”</span></a></div>
251
+ <p>The Setup page’s scan-mode tile is <code dir="auto">map().mode</code> rendered in words: <code dir="auto">monorepo</code> renders Monorepo, <code dir="auto">multi-repo</code> renders Platform, and <code dir="auto">single-repo</code> renders Single repo. <!-- @spec SERV-018 --></p>
252
+ <div class="sl-heading-wrapper level-h2"><h2 id="determinism">Determinism</h2><a class="sl-anchor-link" href="#determinism"><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 “Determinism”</span></a></div>
253
+ <p><code dir="auto">map()</code> contains no absolute path and sorts every array with plain string comparison, so the same files and the same disk produce the same map from any directory. Spec Engine reads paths from <code dir="auto">locate()</code>, which is never part of the map, and hashes none of them into <code dir="auto">build_id</code>. Deleting <code dir="auto">.spec-engine/</code> and rebuilding produces the identical index and the identical <code dir="auto">build_id</code>. <!-- @spec INDX-007 --> <code dir="auto">spec index</code> walks the spec directory and every present member and package, and reports the <code dir="auto">build_id</code> and the row counts. <!-- @spec INDX-009 --></p>
254
+ <div class="sl-heading-wrapper level-h2"><h2 id="declaring-a-platform">Declaring a platform</h2><a class="sl-anchor-link" href="#declaring-a-platform"><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 “Declaring a platform”</span></a></div>
255
+ <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="bash"><code><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">cd</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">~/clients/acme</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># the folder holding the repos</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">domain</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">new</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">BILLING</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># the canonical tree makes it a Spec Engine platform</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">init</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">api</span><span style="--0:#D6DEEB;--1:#403F53"> &#x26;&#x26; </span><span style="--0:#82AAFF;--1:#3B61B0">spec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">init</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">webapp</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#919F9F;--1:#5F636F"># each: platform file entry, marker, pin</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"> &#x26;&#x26; </span><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:#919F9F;--1:#5F636F"># membership and pins are now one gate</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="cd ~/clients/acme # the folder holding the reposspec domain new BILLING # the canonical tree makes it a Spec Engine platformspec init api &#x26;&#x26; spec init webapp # each: platform file entry, marker, pinspec index . &#x26;&#x26; spec check . --ci # membership and pins are now one gate"><div></div></button></div></figure></div>
256
+ <p><code dir="auto">spec check . --ci</code> runs platform-map’s own check underneath, so one gate fails when a requirement, a tag, a pin, or the platform declaration is broken.</p>
257
+ <div class="sl-heading-wrapper level-h2"><h2 id="a-future-input-dependson">A future input: <code dir="auto">dependsOn</code></h2><a class="sl-anchor-link" href="#a-future-input-dependson"><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 “A future input: dependsOn”</span></a></div>
258
+ <p>Every repo and package in the map carries <code dir="auto">dependsOn</code>, the platform’s own package names it depends on. Spec Engine does not read it yet. It is the natural ordering input for propagation across members and is recorded here so the migration leaves room for it.</p> </div> <footer class="sl-flex astro-5h22m7ac"> <div class="meta sl-flex astro-5h22m7ac"> <a href="https://github.com/spec-engine/spec-engine/edit/main/packages/site/src/content/docs/platform-mapping.md" class="sl-flex print:hidden astro-txs4wy42"><svg aria-hidden="true" class="astro-txs4wy42 astro-lm5mbbcm" 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-7kn6nbmm" dir="ltr"> <a href="/architecture/" rel="prev" class="astro-7kn6nbmm"> <svg aria-hidden="true" class="astro-7kn6nbmm astro-lm5mbbcm" 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-7kn6nbmm"> Previous <br class="astro-7kn6nbmm"> <span class="link-title astro-7kn6nbmm">Architecture</span> </span> </a> <a href="/commands/" rel="next" class="astro-7kn6nbmm"> <svg aria-hidden="true" class="astro-7kn6nbmm astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg> <span class="astro-7kn6nbmm"> Next <br class="astro-7kn6nbmm"> <span class="link-title astro-7kn6nbmm">Commands</span> </span> </a> </div> </footer> </div> </div> </main> </div> </div> </div> </div> </body></html>
@@ -1 +1 @@
1
- <?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://docs.spec-engine.dev/</loc></url><url><loc>https://docs.spec-engine.dev/agent-reference/</loc></url><url><loc>https://docs.spec-engine.dev/architecture/</loc></url><url><loc>https://docs.spec-engine.dev/checks/</loc></url><url><loc>https://docs.spec-engine.dev/commands/</loc></url><url><loc>https://docs.spec-engine.dev/format/</loc></url><url><loc>https://docs.spec-engine.dev/getting-started/</loc></url><url><loc>https://docs.spec-engine.dev/guides/adoption/</loc></url><url><loc>https://docs.spec-engine.dev/guides/platform-setup/</loc></url><url><loc>https://docs.spec-engine.dev/guides/single-repo/</loc></url><url><loc>https://docs.spec-engine.dev/overview/</loc></url><url><loc>https://docs.spec-engine.dev/tags/</loc></url></urlset>
1
+ <?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://docs.spec-engine.dev/</loc></url><url><loc>https://docs.spec-engine.dev/agent-reference/</loc></url><url><loc>https://docs.spec-engine.dev/architecture/</loc></url><url><loc>https://docs.spec-engine.dev/checks/</loc></url><url><loc>https://docs.spec-engine.dev/commands/</loc></url><url><loc>https://docs.spec-engine.dev/format/</loc></url><url><loc>https://docs.spec-engine.dev/getting-started/</loc></url><url><loc>https://docs.spec-engine.dev/guides/adoption/</loc></url><url><loc>https://docs.spec-engine.dev/guides/platform-setup/</loc></url><url><loc>https://docs.spec-engine.dev/guides/single-repo/</loc></url><url><loc>https://docs.spec-engine.dev/overview/</loc></url><url><loc>https://docs.spec-engine.dev/platform-mapping/</loc></url><url><loc>https://docs.spec-engine.dev/tags/</loc></url></urlset>
@@ -38,7 +38,7 @@
38
38
  })();
39
39
  </script> <script type="module" src="/_astro/Search.astro_astro_type_script_index_0_lang.lq3t8uE2.js"></script> </div> <div class="sl-hidden md:sl-flex print:hidden right-group astro-shhymrmb"> <div class="sl-flex social-icons astro-shhymrmb"> <a href="https://github.com/spec-engine/spec-engine" rel="me" class="sl-flex astro-opyhioab"><span class="sr-only astro-opyhioab">GitHub</span><svg aria-hidden="true" class="astro-opyhioab astro-lm5mbbcm" 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-oxpvutiw"> <span class="sr-only astro-oxpvutiw">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-oxpvutiw astro-lm5mbbcm" 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-oxpvutiw"> <option value="dark" class="astro-oxpvutiw">Dark</option><option value="light" class="astro-oxpvutiw">Light</option><option value="auto" selected class="astro-oxpvutiw">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-oxpvutiw astro-lm5mbbcm" 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>
40
40
  StarlightThemeProvider.updatePickers();
41
- </script> <script type="module">const r="starlight-theme",o=e=>e==="auto"||e==="dark"||e==="light"?e:"auto",c=()=>o(typeof localStorage<"u"&&localStorage.getItem(r));function n(e){typeof localStorage<"u"&&localStorage.setItem(r,e==="light"||e==="dark"?e:"")}const l=()=>matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";function t(e){StarlightThemeProvider.updatePickers(e),document.documentElement.dataset.theme=e==="auto"?l():e,n(e)}matchMedia("(prefers-color-scheme: light)").addEventListener("change",()=>{c()==="auto"&&t("auto")});class s extends HTMLElement{constructor(){super(),t(c()),this.querySelector("select")?.addEventListener("change",a=>{a.currentTarget instanceof HTMLSelectElement&&t(o(a.currentTarget.value))})}}customElements.define("starlight-theme-select",s);</script> <script type="module">class s extends HTMLElement{constructor(){super();const e=this.querySelector("select");e&&(e.addEventListener("change",t=>{t.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=t.currentTarget.value)}),window.addEventListener("pageshow",t=>{if(!t.persisted)return;const n=e.querySelector("option[selected]")?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}}customElements.define("starlight-lang-select",s);</script> </div> </div></header> <nav class="sidebar print:hidden astro-to3xypmc" aria-label="Main"> <starlight-menu-button class="print:hidden astro-l43anhaf"> <button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-l43anhaf"> <svg aria-hidden="true" class="open-menu astro-l43anhaf astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg> <svg aria-hidden="true" class="close-menu astro-l43anhaf astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg> </button> </starlight-menu-button> <script type="module">class s extends HTMLElement{constructor(){super(),this.btn=this.querySelector("button"),this.btn.addEventListener("click",()=>this.toggleExpanded());const t=this.closest("nav");t&&t.addEventListener("keyup",e=>this.closeOnEscape(e))}setExpanded(t){this.setAttribute("aria-expanded",String(t)),document.body.toggleAttribute("data-mobile-menu-expanded",t)}toggleExpanded(){this.setExpanded(this.getAttribute("aria-expanded")!=="true")}closeOnEscape(t){t.code==="Escape"&&(this.setExpanded(!1),this.btn.focus())}}customElements.define("starlight-menu-button",s);</script> <div id="starlight__sidebar" class="sidebar-pane astro-to3xypmc"> <div class="sidebar-content sl-flex astro-to3xypmc"> <sl-sidebar-state-persist data-hash="0fmmri7" class="astro-znqwvy6e"> <script aria-hidden="true">
41
+ </script> <script type="module">const r="starlight-theme",o=e=>e==="auto"||e==="dark"||e==="light"?e:"auto",c=()=>o(typeof localStorage<"u"&&localStorage.getItem(r));function n(e){typeof localStorage<"u"&&localStorage.setItem(r,e==="light"||e==="dark"?e:"")}const l=()=>matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";function t(e){StarlightThemeProvider.updatePickers(e),document.documentElement.dataset.theme=e==="auto"?l():e,n(e)}matchMedia("(prefers-color-scheme: light)").addEventListener("change",()=>{c()==="auto"&&t("auto")});class s extends HTMLElement{constructor(){super(),t(c()),this.querySelector("select")?.addEventListener("change",a=>{a.currentTarget instanceof HTMLSelectElement&&t(o(a.currentTarget.value))})}}customElements.define("starlight-theme-select",s);</script> <script type="module">class s extends HTMLElement{constructor(){super();const e=this.querySelector("select");e&&(e.addEventListener("change",t=>{t.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=t.currentTarget.value)}),window.addEventListener("pageshow",t=>{if(!t.persisted)return;const n=e.querySelector("option[selected]")?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}}customElements.define("starlight-lang-select",s);</script> </div> </div></header> <nav class="sidebar print:hidden astro-to3xypmc" aria-label="Main"> <starlight-menu-button class="print:hidden astro-l43anhaf"> <button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-l43anhaf"> <svg aria-hidden="true" class="open-menu astro-l43anhaf astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg> <svg aria-hidden="true" class="close-menu astro-l43anhaf astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg> </button> </starlight-menu-button> <script type="module">class s extends HTMLElement{constructor(){super(),this.btn=this.querySelector("button"),this.btn.addEventListener("click",()=>this.toggleExpanded());const t=this.closest("nav");t&&t.addEventListener("keyup",e=>this.closeOnEscape(e))}setExpanded(t){this.setAttribute("aria-expanded",String(t)),document.body.toggleAttribute("data-mobile-menu-expanded",t)}toggleExpanded(){this.setExpanded(this.getAttribute("aria-expanded")!=="true")}closeOnEscape(t){t.code==="Escape"&&(this.setExpanded(!1),this.btn.focus())}}customElements.define("starlight-menu-button",s);</script> <div id="starlight__sidebar" class="sidebar-pane astro-to3xypmc"> <div class="sidebar-content sl-flex astro-to3xypmc"> <sl-sidebar-state-persist data-hash="1dxffrg" class="astro-znqwvy6e"> <script aria-hidden="true">
42
42
  (() => {
43
43
  try {
44
44
  if (!matchMedia('(min-width: 50em)').matches) return;
@@ -61,7 +61,7 @@
61
61
  );
62
62
  } catch {}
63
63
  })();
64
- </script> <ul class="top-level astro-ux7joveq"> <li class="astro-ux7joveq"> <a href="/overview/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Overview</span> </a> </li><li class="astro-ux7joveq"> <a href="/getting-started/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Getting Started</span> </a> </li><li class="astro-ux7joveq"> <a href="/architecture/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Architecture</span> </a> </li><li class="astro-ux7joveq"> <a href="/commands/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Commands</span> </a> </li><li class="astro-ux7joveq"> <a href="/format/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">SPEC.json Format</span> </a> </li><li class="astro-ux7joveq"> <a href="/tags/" aria-current="page" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Tags</span> </a> </li><li class="astro-ux7joveq"> <a href="/checks/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Integrity Checks</span> </a> </li><li class="astro-ux7joveq"> <details open class="astro-ux7joveq"> <summary class="astro-ux7joveq"> <span class="group-label astro-ux7joveq"> <span class="large astro-ux7joveq">Guides</span> </span> <svg aria-hidden="true" class="caret astro-ux7joveq astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><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> </summary> <sl-sidebar-restore data-index="0"></sl-sidebar-restore> <ul class="astro-ux7joveq"> <li class="astro-ux7joveq"> <a href="/guides/adoption/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Adoption Guide</span> </a> </li><li class="astro-ux7joveq"> <a href="/guides/platform-setup/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Platform Setup</span> </a> </li><li class="astro-ux7joveq"> <a href="/guides/single-repo/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Single Repo (Rung 1)</span> </a> </li> </ul> </details> </li><li class="astro-ux7joveq"> <a href="/agent-reference/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Agent Reference</span> </a> </li> </ul> <script aria-hidden="true">
64
+ </script> <ul class="top-level astro-ux7joveq"> <li class="astro-ux7joveq"> <a href="/overview/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Overview</span> </a> </li><li class="astro-ux7joveq"> <a href="/getting-started/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Getting Started</span> </a> </li><li class="astro-ux7joveq"> <a href="/architecture/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Architecture</span> </a> </li><li class="astro-ux7joveq"> <a href="/platform-mapping/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Platform Mapping</span> </a> </li><li class="astro-ux7joveq"> <a href="/commands/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Commands</span> </a> </li><li class="astro-ux7joveq"> <a href="/format/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">SPEC.json Format</span> </a> </li><li class="astro-ux7joveq"> <a href="/tags/" aria-current="page" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Tags</span> </a> </li><li class="astro-ux7joveq"> <a href="/checks/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Integrity Checks</span> </a> </li><li class="astro-ux7joveq"> <details open class="astro-ux7joveq"> <summary class="astro-ux7joveq"> <span class="group-label astro-ux7joveq"> <span class="large astro-ux7joveq">Guides</span> </span> <svg aria-hidden="true" class="caret astro-ux7joveq astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><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> </summary> <sl-sidebar-restore data-index="0"></sl-sidebar-restore> <ul class="astro-ux7joveq"> <li class="astro-ux7joveq"> <a href="/guides/adoption/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Adoption Guide</span> </a> </li><li class="astro-ux7joveq"> <a href="/guides/platform-setup/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Platform Setup</span> </a> </li><li class="astro-ux7joveq"> <a href="/guides/single-repo/" class="astro-ux7joveq"> <span class="astro-ux7joveq">Single Repo (Rung 1)</span> </a> </li> </ul> </details> </li><li class="astro-ux7joveq"> <a href="/agent-reference/" class="large astro-ux7joveq"> <span class="astro-ux7joveq">Agent Reference</span> </a> </li> </ul> <script aria-hidden="true">
65
65
  (() => {
66
66
  const scroller = document.getElementById('starlight__sidebar');
67
67
  if (!window._starlightScrollRestore || !scroller) return;
@@ -70,8 +70,10 @@
70
70
  })();
71
71
  </script> </sl-sidebar-state-persist> <div class="md:sl-hidden"> <div class="mobile-preferences sl-flex astro-2lx56rho"> <div class="social-icons astro-2lx56rho"> <a href="https://github.com/spec-engine/spec-engine" rel="me" class="sl-flex astro-opyhioab"><span class="sr-only astro-opyhioab">GitHub</span><svg aria-hidden="true" class="astro-opyhioab astro-lm5mbbcm" 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-oxpvutiw"> <span class="sr-only astro-oxpvutiw">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-oxpvutiw astro-lm5mbbcm" 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-oxpvutiw"> <option value="dark" class="astro-oxpvutiw">Dark</option><option value="light" class="astro-oxpvutiw">Light</option><option value="auto" selected class="astro-oxpvutiw">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-oxpvutiw astro-lm5mbbcm" 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-to3xypmc"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-blhl7r6q"> <aside class="right-sidebar-container print:hidden astro-blhl7r6q"> <div class="right-sidebar astro-blhl7r6q"> <div class="lg:sl-hidden astro-l5m3cfel"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-goxitxj7"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-goxitxj7"><details id="starlight__mobile-toc" class="astro-goxitxj7"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-goxitxj7"><span class="toggle sl-flex astro-goxitxj7">On this page<svg aria-hidden="true" class="caret astro-goxitxj7 astro-lm5mbbcm" 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-goxitxj7"></span></summary><div class="dropdown astro-goxitxj7"><ul class="isMobile astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-kind" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag kind</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#verification-levels" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Verification levels</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#coverage" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Coverage</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-scanner-notes" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag scanner notes</span> </a> </li> </ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.hwBsy0Mo.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-l5m3cfel"><div class="sl-container astro-l5m3cfel"><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-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-kind" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag kind</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#verification-levels" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Verification levels</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#coverage" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Coverage</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-scanner-notes" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag scanner notes</span> </a> </li> </ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.FuRcXuRY.js"></script></div></div> </div> </aside> <div class="main-pane astro-blhl7r6q"> <main data-pagefind-body class="astro-ioexmpvj" lang="en" dir="ltr"> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <h1 id="_top" class="astro-4b4mtvou">Tags</h1> </div> </div> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <div class="sl-markdown-content"> <p>A tag in implementation code <strong>implements</strong> a requirement; a tag in a test file <strong>verifies</strong> it, optionally at a level.</p>
73
+ </script> </div> </div> </div> </div> </nav> <div class="main-frame astro-to3xypmc"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-blhl7r6q"> <aside class="right-sidebar-container print:hidden astro-blhl7r6q"> <div class="right-sidebar astro-blhl7r6q"> <div class="lg:sl-hidden astro-l5m3cfel"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-goxitxj7"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-goxitxj7"><details id="starlight__mobile-toc" class="astro-goxitxj7"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-goxitxj7"><span class="toggle sl-flex astro-goxitxj7">On this page<svg aria-hidden="true" class="caret astro-goxitxj7 astro-lm5mbbcm" 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-goxitxj7"></span></summary><div class="dropdown astro-goxitxj7"><ul class="isMobile astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#scanned-files" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Scanned files</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-kind" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag kind</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#verification-levels" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Verification levels</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#coverage" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Coverage</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-scanner-notes" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag scanner notes</span> </a> </li> </ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.hwBsy0Mo.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-l5m3cfel"><div class="sl-container astro-l5m3cfel"><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-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#scanned-files" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Scanned files</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-kind" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag kind</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#verification-levels" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Verification levels</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#coverage" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Coverage</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#tag-scanner-notes" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Tag scanner notes</span> </a> </li> </ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.FuRcXuRY.js"></script></div></div> </div> </aside> <div class="main-pane astro-blhl7r6q"> <main data-pagefind-body class="astro-ioexmpvj" lang="en" dir="ltr"> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <h1 id="_top" class="astro-4b4mtvou">Tags</h1> </div> </div> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <div class="sl-markdown-content"> <p>A tag in implementation code <strong>implements</strong> a requirement; a tag in a test file <strong>verifies</strong> it, optionally at a level.</p>
74
74
  <div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.v4551.css"><script type="module" src="/_astro/ec.0vx5m.js"></script><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 at 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">=></span><span style="--0:#D6DEEB;--1:#403F53"> {}) </span><span style="--0:#919F9F;--1:#5D6376">// @spec BILLING-009 unit</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">renews end to end</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">=></span><span style="--0:#D6DEEB;--1:#403F53"> {}) </span><span style="--0:#919F9F;--1:#5D6376">// @spec BILLING-009 e2e</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(&#x22;charges at current price&#x22;, () => {}) // @spec BILLING-009 unitit(&#x22;renews end to end&#x22;, () => {}) // @spec BILLING-009 e2e"><div></div></button></div></figure></div>
75
+ <div class="sl-heading-wrapper level-h2"><h2 id="scanned-files">Scanned files</h2><a class="sl-anchor-link" href="#scanned-files"><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 “Scanned files”</span></a></div>
76
+ <p>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>, and <code dir="auto">.rs</code> files. The tag regex ignores the comment leader, so a Lua <code dir="auto">-- @spec BILLING-009</code>, a Go or Rust <code dir="auto">// @spec BILLING-009</code>, and a shell <code dir="auto"># @spec BILLING-009</code> all bind. Documentation is scanned from <code dir="auto">.md</code> files.</p>
75
77
  <div class="sl-heading-wrapper level-h2"><h2 id="tag-kind">Tag kind</h2><a class="sl-anchor-link" href="#tag-kind"><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 kind”</span></a></div>
76
78
  <p>Kind is <strong>path-derived</strong>, never authored:</p>
77
79
 
@@ -96,6 +98,44 @@
96
98
 
97
99
 
98
100
  <table><thead><tr><th>Location</th><th>Kind</th></tr></thead><tbody><tr><td><code dir="auto">src/</code> or similar implementation path</td><td><strong>implements</strong></td></tr><tr><td><code dir="auto">test/</code> or similar test path</td><td><strong>verifies</strong></td></tr><tr><td>a docs path (e.g. <code dir="auto">docs/*.md</code>)</td><td><strong>documents</strong></td></tr></tbody></table>
101
+ <p>A path is a test path when it contains one of these:</p>
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+ <table><thead><tr><th>Convention</th><th>Substring</th><th>Example</th></tr></thead><tbody><tr><td>TypeScript and JavaScript runners</td><td><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>, <code dir="auto">.e2e.</code></td><td><code dir="auto">test/renew.test.ts</code>, <code dir="auto">src/renew.spec.ts</code></td></tr><tr><td>Go</td><td><code dir="auto">_test.go</code></td><td><code dir="auto">renew_test.go</code></td></tr><tr><td>Rust</td><td><code dir="auto">/tests/</code>, <code dir="auto">_test.rs</code></td><td><code dir="auto">tests/renew.rs</code>, <code dir="auto">src/renew_test.rs</code></td></tr><tr><td>Lua with busted</td><td><code dir="auto">_spec.lua</code>, <code dir="auto">_test.lua</code>, <code dir="auto">/spec/</code></td><td><code dir="auto">spec/renew_spec.lua</code></td></tr><tr><td>Luau with TestEZ or Jest-Lua</td><td><code dir="auto">.spec.</code>, <code dir="auto">.test.</code></td><td><code dir="auto">renew.spec.luau</code>, <code dir="auto">renew.test.luau</code></td></tr></tbody></table>
138
+ <p>Rust unit tests written inline under <code dir="auto">#[cfg(test)]</code> live in the implementation file, so a tag there counts as <strong>implements</strong>; a verifying tag belongs in a <code dir="auto">tests/</code> integration test or a <code dir="auto">*_test.rs</code> file. A path rule cannot see inside a file, and Spec Engine does not read attributes.</p>
99
139
  <div class="sl-heading-wrapper level-h2"><h2 id="verification-levels">Verification levels</h2><a class="sl-anchor-link" href="#verification-levels"><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 “Verification levels”</span></a></div>
100
140
  <p>On test tags, an optional level token can be appended:</p>
101
141
  <ul>