@spec-engine/spec-engine 0.4.0 → 0.5.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.
- package/dist/docs/agent-reference/index.html +1 -1
- package/dist/docs/commands/index.html +1 -1
- package/dist/docs/pagefind/fragment/en_48f1b3a.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_a7dfb2d.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_b2867b9.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_6c7635b.pf_index +0 -0
- package/dist/docs/pagefind/pagefind-entry.json +1 -1
- package/dist/docs/pagefind/pagefind.en_20f81448c5.pf_meta +0 -0
- package/dist/docs/platform-mapping/index.html +6 -3
- package/dist/impl.js +138 -116
- package/package.json +1 -1
- package/dist/docs/pagefind/fragment/en_4bd5a86.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_ace7862.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_ef547ec.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_a2165b2.pf_index +0 -0
- package/dist/docs/pagefind/pagefind.en_b7799d257e.pf_meta +0 -0
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
<li>Code binds with <code dir="auto">// @spec KEY-NNN</code>. A test tag may add <code dir="auto">unit</code> | <code dir="auto">integration</code> | <code dir="auto">e2e</code>.</li>
|
|
78
78
|
<li>Tag kind is path-derived: implementation code implements, a test path verifies.</li>
|
|
79
79
|
<li>The index at <code dir="auto"><platformDir>/.spec-engine/index.sqlite</code> is derived and disposable.</li>
|
|
80
|
-
<li>Coverage, drift, and propagation are SQL projections over tags. Drift compares a member’s pin (<code dir="auto">spec-engine@N</code>) against each referenced requirement’s <code dir="auto">changed_at_version</code>; the platform version is the max domain version
|
|
80
|
+
<li>Coverage, drift, and propagation are SQL projections over tags. Drift compares a member’s pin (<code dir="auto">spec-engine@N</code>) against each referenced requirement’s <code dir="auto">changed_at_version</code>; the platform version is the max domain version. Propagation rows come in dependency order, providers before consumers, from platform-map’s <code dir="auto">dependsOn</code>.</li>
|
|
81
81
|
</ul>
|
|
82
82
|
<p>The repo’s <code dir="auto">AGENTS.md</code> is the canonical copy of this page, with every command’s flags and <code dir="auto">--json</code> shape.</p>
|
|
83
83
|
<div class="sl-heading-wrapper level-h2"><h2 id="invocation">Invocation</h2><a class="sl-anchor-link" href="#invocation"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Invocation”</span></a></div>
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
<p>Requirements tagged in the given files. Accepts multiple positionals, comma-split within a positional, and absolute paths under the platform tree. <code dir="auto">--req KEY-NNN</code> inverts the lookup: every tag site for that requirement across all repos.</p>
|
|
183
183
|
<p><strong>Exit codes:</strong> 0 / 2</p>
|
|
184
184
|
<div class="sl-heading-wrapper level-h3"><h3 id="spec-propagation-reqid-platformdir"><code dir="auto">spec propagation <reqId> [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-propagation-reqid-platformdir"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “spec propagation <reqId> [platformDir]”</span></a></div>
|
|
185
|
-
<p>Classify each member repo’s relationship to a superseded requirement: <code dir="auto">MIGRATED_VERIFIED</code>, <code dir="auto">MIGRATED_UNVERIFIED</code>, <code dir="auto">ON_PREDECESSOR</code>, <code dir="auto">ON_OTHER_DOMAIN_REQ</code>, <code dir="auto">NO_DOMAIN_REFERENCE</code> — plus a <code dir="auto">drifted</code> flag per member
|
|
185
|
+
<p>Classify each member repo’s relationship to a superseded requirement: <code dir="auto">MIGRATED_VERIFIED</code>, <code dir="auto">MIGRATED_UNVERIFIED</code>, <code dir="auto">ON_PREDECESSOR</code>, <code dir="auto">ON_OTHER_DOMAIN_REQ</code>, <code dir="auto">NO_DOMAIN_REFERENCE</code> — plus a <code dir="auto">drifted</code> flag per member. Rows are in dependency order, providers before consumers, read from platform-map’s <code dir="auto">dependsOn</code>; ties by name. See <a href="/platform-mapping/">Platform Mapping</a>.</p>
|
|
186
186
|
<p><strong>Exit codes:</strong> 0 / 2</p>
|
|
187
187
|
<div class="sl-heading-wrapper level-h3"><h3 id="spec-gate-repo-reqid-platformdir"><code dir="auto">spec gate <repo> <reqId> [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-gate-repo-reqid-platformdir"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “spec gate <repo> <reqId> [platformDir]”</span></a></div>
|
|
188
188
|
<p>Approval primitive. Passes iff <code dir="auto"><reqId></code> is Active AND <code dir="auto"><repo></code>’s pin covers the requirement’s <code dir="auto">changed_at_version</code>. <strong>Decision order:</strong> <code dir="auto">NOT_FOUND</code> → <code dir="auto">DRAFT</code> → <code dir="auto">SUPERSEDED</code> → <code dir="auto">VERSION_PIN</code> → <code dir="auto">PASS</code>; pin equality passes. An unknown repo is a usage error (exit 2), not a gate failure.</p>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.5.2","languages":{"en":{"hash":"
|
|
1
|
+
{"version":"1.5.2","languages":{"en":{"hash":"en_20f81448c5","wasm":"en","page_count":12}},"include_characters":["_","‿","⁀","⁔","︳","︴","﹍","﹎","﹏","_"]}
|
|
Binary file
|
|
@@ -70,7 +70,7 @@
|
|
|
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="#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="#
|
|
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="#propagation-order-dependson" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Propagation order: 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="#propagation-order-dependson" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Propagation order: 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
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
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
76
|
<p><strong>The platform file</strong> lives at the platform root and declares the members:</p>
|
|
@@ -254,5 +254,8 @@
|
|
|
254
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
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"> && </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"> && </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 && spec init webapp # each: platform file entry, marker, pinspec index . && spec check . --ci # membership and pins are now one gate"><div></div></button></div></figure></div>
|
|
256
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="
|
|
258
|
-
<p>Every repo and package in the map carries <code dir="auto">dependsOn</code>, the platform’s own package names
|
|
257
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="propagation-order-dependson">Propagation order: <code dir="auto">dependsOn</code></h2><a class="sl-anchor-link" href="#propagation-order-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 “Propagation order: 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 its manifest depends on, matched within one ecosystem. Spec Engine reads it as the order in which a superseded requirement propagates: a consumer can only move to the successor after the packages it depends on ship it. <code dir="auto">spec propagation</code>, <code dir="auto">/api/propagation/:id</code>, <code dir="auto">spec_propagation</code>, and the webapp’s propagation page list members in dependency order, a member after every member it depends on, and members at the same depth by name. <!-- @spec PROP-006 --></p>
|
|
259
|
+
<p>The depth is a coverage-column fact, computed at discovery and stored in the derived index. A single-repo member’s node is its map repo; a monorepo member’s package column, and a lone monorepo’s package column, is its map <code dir="auto">Package</code>. A column depends on another column when the other’s <code dir="auto">packageName</code> appears in its <code dir="auto">dependsOn</code>; a name that is no column (a monorepo root’s own package, an absent member) is no edge. A column with no dependency is depth 0; every other column is one deeper than its deepest dependency. A dependency cycle, and every column downstream of it, sits at one depth after every column outside it, by name.</p>
|
|
260
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">$ spec propagation BILLING-002 .</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">REPO STATE VIA DRIFT?</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">shared MIGRATED_VERIFIED — no</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">zed NO_DOMAIN_REFERENCE — no</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">api ON_PREDECESSOR BILLING-001 no</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">web MIGRATED_UNVERIFIED — no</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="$ spec propagation BILLING-002 .REPO STATE VIA DRIFT?shared MIGRATED_VERIFIED — nozed NO_DOMAIN_REFERENCE — noapi ON_PREDECESSOR BILLING-001 noweb MIGRATED_UNVERIFIED — no"><div></div></button></div></figure></div>
|
|
261
|
+
<p>Here <code dir="auto">api</code> depends on <code dir="auto">shared</code> and <code dir="auto">web</code> on <code dir="auto">api</code>, so <code dir="auto">api</code> is the member to migrate next. A platform with no in-platform dependencies lists members by name, as before.</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>
|
package/dist/impl.js
CHANGED
|
@@ -20,7 +20,7 @@ var package_default;
|
|
|
20
20
|
var init_package = __esm(() => {
|
|
21
21
|
package_default = {
|
|
22
22
|
name: "@spec-engine/spec-engine",
|
|
23
|
-
version: "0.
|
|
23
|
+
version: "0.5.0",
|
|
24
24
|
description: "Cross-repo spec engine \u2014 durable requirements with permanent IDs, bound to code via @spec tags, checked in CI. Ships the spec CLI, the local coverage webapp (spec serve), and offline docs (spec docs).",
|
|
25
25
|
license: "MIT",
|
|
26
26
|
type: "module",
|
|
@@ -432,19 +432,20 @@ function buildCoverageReport(rows) {
|
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
// ../shared/src/schema.ts
|
|
435
|
-
var SCHEMA_VERSION =
|
|
435
|
+
var SCHEMA_VERSION = 11, FILES_MAX = 1000, DEFAULT_QUERY_LIMIT = 10, LIMIT_MAX = 1000, TABLES_DDL = `
|
|
436
436
|
-- _schema_version: single-row table holding the integer schema_version on disk.
|
|
437
437
|
-- Engine reads this on open; if it doesn't match SCHEMA_VERSION, the DB is wiped
|
|
438
|
-
-- and rebuilt from scratch
|
|
438
|
+
-- and rebuilt from scratch.
|
|
439
439
|
CREATE TABLE IF NOT EXISTS _schema_version (
|
|
440
440
|
version INTEGER NOT NULL
|
|
441
441
|
);
|
|
442
442
|
|
|
443
|
-
--
|
|
443
|
+
-- No CHECK, FK, or UNIQUE constraint on any domain field. PRIMARY KEY only.
|
|
444
444
|
CREATE TABLE IF NOT EXISTS repos (
|
|
445
445
|
name TEXT PRIMARY KEY,
|
|
446
446
|
path TEXT NOT NULL,
|
|
447
|
-
pinned_spec_version INTEGER NOT NULL
|
|
447
|
+
pinned_spec_version INTEGER NOT NULL,
|
|
448
|
+
dependency_depth INTEGER NOT NULL
|
|
448
449
|
);
|
|
449
450
|
|
|
450
451
|
CREATE TABLE IF NOT EXISTS domains (
|
|
@@ -483,12 +484,10 @@ CREATE INDEX IF NOT EXISTS idx_tags_by_req ON tags(req_id);
|
|
|
483
484
|
CREATE INDEX IF NOT EXISTS idx_tags_by_repo ON tags(repo);
|
|
484
485
|
CREATE INDEX IF NOT EXISTS idx_tags_by_file ON tags(file);
|
|
485
486
|
|
|
486
|
-
-- relations
|
|
487
|
-
--
|
|
488
|
-
-- Invariant #4 there is NO FK \u2014 a to_id pointing at a missing requirement
|
|
487
|
+
-- relations: one row per relates link. from_id carries the field; to_id is
|
|
488
|
+
-- the linked requirement. No FK: a to_id pointing at a missing requirement
|
|
489
489
|
-- lands verbatim so spec check can surface BROKEN_RELATES; a to_id whose
|
|
490
|
-
-- requirement was superseded surfaces RELATES_SUPERSEDED.
|
|
491
|
-
-- line locate the authored Relates field line.
|
|
490
|
+
-- requirement was superseded surfaces RELATES_SUPERSEDED.
|
|
492
491
|
CREATE TABLE IF NOT EXISTS relations (
|
|
493
492
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
494
493
|
from_id TEXT NOT NULL,
|
|
@@ -499,11 +498,8 @@ CREATE TABLE IF NOT EXISTS relations (
|
|
|
499
498
|
CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_id);
|
|
500
499
|
CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_id);
|
|
501
500
|
|
|
502
|
-
-- term_aliases
|
|
503
|
-
-- from the
|
|
504
|
-
-- FK/CHECK/UNIQUE (SCHM-07 / Invariant #4, or the arch-fence trips). term_id is
|
|
505
|
-
-- the owning TERM's id; name the synonym string. Present-and-empty until Wave C
|
|
506
|
-
-- flattens the requirement aliases field into rows.
|
|
501
|
+
-- term_aliases: one row per glossary-term alias. id AUTOINCREMENT is excluded
|
|
502
|
+
-- from build_id. term_id is the owning TERM's id; name the synonym string.
|
|
507
503
|
CREATE TABLE IF NOT EXISTS term_aliases (
|
|
508
504
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
509
505
|
term_id TEXT NOT NULL,
|
|
@@ -512,15 +508,12 @@ CREATE TABLE IF NOT EXISTS term_aliases (
|
|
|
512
508
|
CREATE INDEX IF NOT EXISTS idx_term_aliases_term ON term_aliases(term_id);
|
|
513
509
|
CREATE INDEX IF NOT EXISTS idx_term_aliases_name ON term_aliases(name);
|
|
514
510
|
|
|
515
|
-
-- term_citations
|
|
516
|
-
--
|
|
517
|
-
--
|
|
518
|
-
--
|
|
519
|
-
--
|
|
520
|
-
--
|
|
521
|
-
-- still lands); cited_as the authored surface form; pinned_version the term
|
|
522
|
-
-- spec_version the citation was pinned to; source_file + line locate the cite.
|
|
523
|
-
-- Present-and-empty until Wave C wires the flatten.
|
|
511
|
+
-- term_citations: one row per pinned cites reference from a requirement to a
|
|
512
|
+
-- glossary TERM. id AUTOINCREMENT is excluded from build_id. No FK: a term_id
|
|
513
|
+
-- pointing at a missing or superseded TERM lands verbatim so spec check can
|
|
514
|
+
-- surface UNDEFINED_TERM / TERM_DRIFT. term_id is nullable (an unresolved cite
|
|
515
|
+
-- still lands); cited_as is the authored surface form; pinned_version the term
|
|
516
|
+
-- spec_version the citation was pinned to.
|
|
524
517
|
CREATE TABLE IF NOT EXISTS term_citations (
|
|
525
518
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
526
519
|
req_id TEXT NOT NULL,
|
|
@@ -533,30 +526,25 @@ CREATE TABLE IF NOT EXISTS term_citations (
|
|
|
533
526
|
CREATE INDEX IF NOT EXISTS idx_term_citations_req ON term_citations(req_id);
|
|
534
527
|
CREATE INDEX IF NOT EXISTS idx_term_citations_term ON term_citations(term_id);
|
|
535
528
|
|
|
536
|
-
-- provenance
|
|
537
|
-
--
|
|
538
|
-
--
|
|
539
|
-
--
|
|
540
|
-
--
|
|
541
|
-
-- Exactly ONE index, on req_id (SC3) \u2014 never on issue_id, which would read as
|
|
542
|
-
-- treating the opaque payload as a lookup key (PROV-02, grep-fenced in Plan 04).
|
|
529
|
+
-- provenance: one row per issues role:ID link. issue_id is the opaque
|
|
530
|
+
-- external tracker payload; role is a closed-allow-list label (created |
|
|
531
|
+
-- supersedes-via | amends-via), validated at parse time, never as a DB
|
|
532
|
+
-- constraint. Exactly one index, on req_id; never on issue_id, which would
|
|
533
|
+
-- treat the opaque payload as a lookup key.
|
|
543
534
|
-- @spec PROV-004
|
|
544
535
|
CREATE TABLE IF NOT EXISTS provenance (
|
|
545
536
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
546
537
|
req_id TEXT NOT NULL,
|
|
547
|
-
issue_id TEXT NOT NULL, --
|
|
538
|
+
issue_id TEXT NOT NULL, -- opaque payload string, never a PK/FK/UNIQUE/JOIN key
|
|
548
539
|
role TEXT NOT NULL, -- created | supersedes-via | amends-via
|
|
549
540
|
source_file TEXT NOT NULL,
|
|
550
541
|
line INTEGER NOT NULL
|
|
551
542
|
);
|
|
552
|
-
CREATE INDEX IF NOT EXISTS idx_provenance_by_req ON provenance(req_id); --
|
|
553
|
-
|
|
554
|
-
-- parse_diagnostics: structural diagnostics
|
|
555
|
-
--
|
|
556
|
-
--
|
|
557
|
-
-- req_id is nullable per WR-02 (03-REVIEW): the parser knows the implicated
|
|
558
|
-
-- id for BROKEN_SUPERSEDE and BAD_STATUS, and for the SECOND DUP_ID
|
|
559
|
-
-- occurrence. NULL when no specific id is implicated.
|
|
543
|
+
CREATE INDEX IF NOT EXISTS idx_provenance_by_req ON provenance(req_id); -- the only index
|
|
544
|
+
|
|
545
|
+
-- parse_diagnostics: structural diagnostics from the validate-on-parse pass
|
|
546
|
+
-- (DUP_ID, BROKEN_SUPERSEDE, BAD_STATUS), stored so spec check can render them
|
|
547
|
+
-- with file+line. req_id is NULL when no specific id is implicated.
|
|
560
548
|
CREATE TABLE IF NOT EXISTS parse_diagnostics (
|
|
561
549
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
562
550
|
code TEXT NOT NULL,
|
|
@@ -591,13 +579,9 @@ FROM requirements r
|
|
|
591
579
|
CROSS JOIN repos
|
|
592
580
|
LEFT JOIN tags t
|
|
593
581
|
ON t.req_id = r.id AND t.repo = repos.name
|
|
594
|
-
--
|
|
595
|
-
--
|
|
596
|
-
--
|
|
597
|
-
-- coverage) in ONE edit, so a migrated term never renders an empty coverage
|
|
598
|
-
-- cell. The Q4 ORPHAN_REQ / Q5 UNVERIFIED_REQ queries (sqlite.ts) carry the
|
|
599
|
-
-- SAME literal exclusion so a term never fires a coverage ERROR. The match is
|
|
600
|
-
-- the exact literal 'TERM' \u2014 never a real domain key.
|
|
582
|
+
-- A glossary TERM is a requirement row but never a code-coverage obligation.
|
|
583
|
+
-- The ORPHAN_REQ / UNVERIFIED_REQ queries in sqlite.ts carry the same literal
|
|
584
|
+
-- exclusion. The match is the exact literal 'TERM', never a real domain key.
|
|
601
585
|
-- @spec SCHM-017
|
|
602
586
|
WHERE r.key != 'TERM'
|
|
603
587
|
GROUP BY r.id, repos.name;
|
|
@@ -616,15 +600,9 @@ JOIN requirements r ON r.id = t.req_id
|
|
|
616
600
|
JOIN repos ON repos.name = t.repo
|
|
617
601
|
WHERE r.changed_at_version > repos.pinned_spec_version;
|
|
618
602
|
|
|
619
|
-
-- term_drift
|
|
620
|
-
--
|
|
621
|
-
--
|
|
622
|
-
-- changed_at_version \u2014 i.e. the term's definition moved on since the citation
|
|
623
|
-
-- was pinned. INNER JOIN (not LEFT): an unresolved term_id (no matching
|
|
624
|
-
-- requirement) is an UNDEFINED_TERM concern, NOT drift \u2014 scoped distinctly by
|
|
625
|
-
-- the Wave-D diagnostics. term.key='TERM' keeps the VIEW scoped to the reserved
|
|
626
|
-
-- glossary domain. Present-and-empty until Wave C populates term_citations; the
|
|
627
|
-
-- diagnostic (TERM_DRIFT) reads it in Wave E.
|
|
603
|
+
-- term_drift: one row per cites reference whose pinned_version is behind the
|
|
604
|
+
-- cited TERM's changed_at_version. INNER JOIN: an unresolved term_id is an
|
|
605
|
+
-- UNDEFINED_TERM concern, not drift.
|
|
628
606
|
-- @spec CHCK-018
|
|
629
607
|
CREATE VIEW IF NOT EXISTS term_drift AS
|
|
630
608
|
SELECT
|
|
@@ -638,38 +616,21 @@ FROM term_citations tc
|
|
|
638
616
|
JOIN requirements term ON term.id = tc.term_id
|
|
639
617
|
WHERE term.key = 'TERM' AND term.changed_at_version > tc.pinned_version;
|
|
640
618
|
|
|
641
|
-
-- provenance_matrix
|
|
642
|
-
--
|
|
643
|
-
--
|
|
644
|
-
--
|
|
645
|
-
--
|
|
646
|
-
--
|
|
647
|
-
--
|
|
648
|
-
--
|
|
649
|
-
--
|
|
650
|
-
-- WR-01 (13-REVIEW review-fix): test_levels is re-aggregated at the
|
|
651
|
-
-- INDIVIDUAL-level granularity via a correlated subquery over tags, NOT by
|
|
652
|
-
-- GROUP_CONCAT(DISTINCT c.test_levels). The coverage VIEW already concatenates
|
|
653
|
-
-- levels per (req,repo) into a single string (api -> "unit,integration",
|
|
654
|
-
-- mobile -> "unit"); GROUP_CONCAT(DISTINCT ...) over those whole strings
|
|
655
|
-
-- deduplicates STRINGS, not levels, so a level present in two repos emitted
|
|
656
|
-
-- twice ("unit,integration,unit"). The subquery selects DISTINCT t2.level for
|
|
657
|
-
-- this req across ALL repos (ORDER BY level for determinism), so each level
|
|
658
|
-
-- appears exactly once. The correlation is ON req_id ONLY (t2.req_id =
|
|
659
|
-
-- p.req_id) \u2014 the issue_id-opacity fence stays green (no issue_id key usage).
|
|
660
|
-
-- CRITICAL FENCE CONSTRAINT
|
|
661
|
-
-- (PROV-02/SC3): the CI issue_id-opacity fence forbids grouping by issue_id,
|
|
662
|
-
-- so we GROUP BY the provenance PK p.id (which uniquely determines the link)
|
|
663
|
-
-- and let issue_id ride as a bare projected column under SQLite's
|
|
664
|
-
-- bare-column rule. issue_id is therefore PURELY projected \u2014 never a
|
|
665
|
-
-- JOIN/GROUP-BY/PK/FK/UNIQUE/INDEX key.
|
|
619
|
+
-- provenance_matrix: provenance \xD7 requirements \xD7 coverage, never
|
|
620
|
+
-- materialized. Every join is on req_id, never on issue_id. coverage is a
|
|
621
|
+
-- per-(req, repo) matrix, so the join fans out per repo and is aggregated to
|
|
622
|
+
-- one row per provenance link, grouped by the provenance PK p.id; issue_id
|
|
623
|
+
-- rides as a bare projected column and is never a JOIN, GROUP BY, PK, FK,
|
|
624
|
+
-- UNIQUE, or INDEX key. test_levels is aggregated by a correlated subquery
|
|
625
|
+
-- over tags (DISTINCT level, ORDER BY level) so a level present in two repos
|
|
626
|
+
-- appears once.
|
|
666
627
|
CREATE VIEW IF NOT EXISTS provenance_matrix AS
|
|
667
628
|
SELECT
|
|
668
629
|
p.req_id AS req_id,
|
|
669
630
|
p.role AS role,
|
|
670
|
-
p.issue_id AS issue_id, --
|
|
631
|
+
p.issue_id AS issue_id, -- opaque payload, projected only
|
|
671
632
|
p.source_file AS source_file,
|
|
672
|
-
p.line AS line,
|
|
633
|
+
p.line AS line,
|
|
673
634
|
r.status AS req_status,
|
|
674
635
|
MAX(c.implemented) AS implemented,
|
|
675
636
|
MAX(c.verified) AS verified,
|
|
@@ -1204,19 +1165,86 @@ function packageColumnName(root, dir) {
|
|
|
1204
1165
|
function byName(a, b) {
|
|
1205
1166
|
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
1206
1167
|
}
|
|
1207
|
-
function column(name, path, pin, ignore) {
|
|
1168
|
+
function column(name, path, pin, node, ignore) {
|
|
1208
1169
|
return {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1170
|
+
repo: {
|
|
1171
|
+
name,
|
|
1172
|
+
path,
|
|
1173
|
+
pinned_spec_version: pin,
|
|
1174
|
+
...ignore && ignore.length > 0 ? { ignore } : {}
|
|
1175
|
+
},
|
|
1176
|
+
node
|
|
1213
1177
|
};
|
|
1214
1178
|
}
|
|
1179
|
+
function packageKey(ecosystem, packageName) {
|
|
1180
|
+
return `${ecosystem ?? ""}\x00${packageName}`;
|
|
1181
|
+
}
|
|
1182
|
+
function providersOf(columns) {
|
|
1183
|
+
const providers = new Map;
|
|
1184
|
+
for (const c of columns) {
|
|
1185
|
+
if (c.node.packageName === undefined)
|
|
1186
|
+
continue;
|
|
1187
|
+
const key = packageKey(c.node.ecosystem, c.node.packageName);
|
|
1188
|
+
if (!providers.has(key))
|
|
1189
|
+
providers.set(key, c.repo.name);
|
|
1190
|
+
}
|
|
1191
|
+
return providers;
|
|
1192
|
+
}
|
|
1193
|
+
function dependenciesOf(c, providers) {
|
|
1194
|
+
const deps = new Set;
|
|
1195
|
+
for (const name of c.node.dependsOn) {
|
|
1196
|
+
const provider = providers.get(packageKey(c.node.ecosystem, name));
|
|
1197
|
+
if (provider !== undefined && provider !== c.repo.name)
|
|
1198
|
+
deps.add(provider);
|
|
1199
|
+
}
|
|
1200
|
+
return deps;
|
|
1201
|
+
}
|
|
1202
|
+
function dependencyGraph(columns) {
|
|
1203
|
+
const providers = providersOf(columns);
|
|
1204
|
+
const dependents = new Map;
|
|
1205
|
+
const waitingOn = new Map;
|
|
1206
|
+
for (const c of columns) {
|
|
1207
|
+
const deps = dependenciesOf(c, providers);
|
|
1208
|
+
waitingOn.set(c.repo.name, deps.size);
|
|
1209
|
+
for (const provider of deps) {
|
|
1210
|
+
dependents.set(provider, [...dependents.get(provider) ?? [], c.repo.name]);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
return { names: columns.map((c) => c.repo.name).sort(), dependents, waitingOn };
|
|
1214
|
+
}
|
|
1215
|
+
function dependencyDepths(columns) {
|
|
1216
|
+
const { names, dependents, waitingOn } = dependencyGraph(columns);
|
|
1217
|
+
const depth = new Map;
|
|
1218
|
+
const ready = names.filter((n) => waitingOn.get(n) === 0);
|
|
1219
|
+
for (const n of ready)
|
|
1220
|
+
depth.set(n, 0);
|
|
1221
|
+
for (let i = 0;i < ready.length; i++) {
|
|
1222
|
+
const provider = ready[i];
|
|
1223
|
+
const providerDepth = depth.get(provider) ?? 0;
|
|
1224
|
+
for (const consumer of dependents.get(provider) ?? []) {
|
|
1225
|
+
depth.set(consumer, Math.max(depth.get(consumer) ?? 0, providerDepth + 1));
|
|
1226
|
+
const left = (waitingOn.get(consumer) ?? 0) - 1;
|
|
1227
|
+
waitingOn.set(consumer, left);
|
|
1228
|
+
if (left === 0)
|
|
1229
|
+
ready.push(consumer);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const cyclic = Math.max(-1, ...depth.values()) + 1;
|
|
1233
|
+
for (const n of names)
|
|
1234
|
+
if (!depth.has(n))
|
|
1235
|
+
depth.set(n, cyclic);
|
|
1236
|
+
return depth;
|
|
1237
|
+
}
|
|
1238
|
+
function toRepos(columns) {
|
|
1239
|
+
const depth = dependencyDepths(columns);
|
|
1240
|
+
return columns.map((c) => ({ ...c.repo, dependency_depth: depth.get(c.repo.name) ?? 0 })).sort(byName);
|
|
1241
|
+
}
|
|
1215
1242
|
function selfMember(absPlatform, platformVersion) {
|
|
1216
1243
|
return {
|
|
1217
1244
|
name: basename(absPlatform),
|
|
1218
1245
|
path: absPlatform,
|
|
1219
1246
|
pinned_spec_version: platformVersion,
|
|
1247
|
+
dependency_depth: 0,
|
|
1220
1248
|
selfMember: true
|
|
1221
1249
|
};
|
|
1222
1250
|
}
|
|
@@ -1238,11 +1266,11 @@ async function packageColumns(repoPath, prefix, repo, parentPin) {
|
|
|
1238
1266
|
const path = join3(repoPath, pkg.path);
|
|
1239
1267
|
const configPath = join3(path, MEMBER_CONFIG_FILENAME);
|
|
1240
1268
|
if (!existsSync(configPath)) {
|
|
1241
|
-
out.push(column(name, path, parentPin));
|
|
1269
|
+
out.push(column(name, path, parentPin, pkg));
|
|
1242
1270
|
continue;
|
|
1243
1271
|
}
|
|
1244
1272
|
const cfg = await readRepoConfig(configPath);
|
|
1245
|
-
out.push(column(name, path, extractPin(cfg.specs), cfg.ignore));
|
|
1273
|
+
out.push(column(name, path, extractPin(cfg.specs), pkg, cfg.ignore));
|
|
1246
1274
|
}
|
|
1247
1275
|
return out;
|
|
1248
1276
|
}
|
|
@@ -1279,14 +1307,14 @@ async function membersOf(reading, absPlatform, platformVersion) {
|
|
|
1279
1307
|
const diagnostics2 = surfaced(mapped);
|
|
1280
1308
|
if (mapped.mode !== "multi-repo") {
|
|
1281
1309
|
const repo = mapped.repos[0];
|
|
1282
|
-
const
|
|
1283
|
-
return { mode: mapped.mode, members
|
|
1310
|
+
const members = repo !== undefined && repo.mode === "monorepo" && repo.packages.length > 0 ? toRepos(await packageColumns(absPlatform, "", repo, platformVersion)) : [selfMember(absPlatform, platformVersion)];
|
|
1311
|
+
return { mode: mapped.mode, members, unpinned: [], undeclared: [], diagnostics: diagnostics2 };
|
|
1284
1312
|
}
|
|
1285
1313
|
if (!mapped.declared) {
|
|
1286
1314
|
const undeclared2 = mapped.repos.map((r) => ({ name: r.name, path: join3(absPlatform, r.name) }));
|
|
1287
1315
|
return { mode: "multi-repo", members: [], unpinned: [], undeclared: undeclared2, diagnostics: diagnostics2 };
|
|
1288
1316
|
}
|
|
1289
|
-
const
|
|
1317
|
+
const columns = [];
|
|
1290
1318
|
const unpinned = [];
|
|
1291
1319
|
for (const repo of mapped.repos) {
|
|
1292
1320
|
const path = located.repos[repo.name];
|
|
@@ -1300,14 +1328,13 @@ async function membersOf(reading, absPlatform, platformVersion) {
|
|
|
1300
1328
|
const cfg = await readRepoConfig(configPath);
|
|
1301
1329
|
const pin = extractPin(cfg.specs);
|
|
1302
1330
|
if (repo.mode === "monorepo" && repo.packages.length > 0) {
|
|
1303
|
-
|
|
1331
|
+
columns.push(...await packageColumns(path, `${repo.name}/`, repo, pin));
|
|
1304
1332
|
} else {
|
|
1305
|
-
|
|
1333
|
+
columns.push(column(repo.name, path, pin, repo, cfg.ignore));
|
|
1306
1334
|
}
|
|
1307
1335
|
}
|
|
1308
|
-
members.sort(byName);
|
|
1309
1336
|
const undeclared = mapped.diagnostics.filter((d) => d.code === "UNLISTED_REPO").map((d) => ({ name: d.subject, path: join3(absPlatform, d.subject) }));
|
|
1310
|
-
return { mode: "multi-repo", members, unpinned, undeclared, diagnostics: diagnostics2 };
|
|
1337
|
+
return { mode: "multi-repo", members: toRepos(columns), unpinned, undeclared, diagnostics: diagnostics2 };
|
|
1311
1338
|
}
|
|
1312
1339
|
async function discoverRepos(platformDir) {
|
|
1313
1340
|
const absPlatform = resolve2(platformDir);
|
|
@@ -1320,7 +1347,8 @@ async function discoverRepos(platformDir) {
|
|
|
1320
1347
|
const canonical = {
|
|
1321
1348
|
name: CANONICAL_SPECS_DIR,
|
|
1322
1349
|
path: canonicalPath,
|
|
1323
|
-
pinned_spec_version: platformVersion
|
|
1350
|
+
pinned_spec_version: platformVersion,
|
|
1351
|
+
dependency_depth: 0
|
|
1324
1352
|
};
|
|
1325
1353
|
const reading = readPlatformMap(absPlatform);
|
|
1326
1354
|
const shape = reading === null ? loneSingleRepo(absPlatform, platformVersion) : await membersOf(reading, absPlatform, platformVersion);
|
|
@@ -1706,11 +1734,11 @@ SELECT
|
|
|
1706
1734
|
) AS via_other
|
|
1707
1735
|
FROM repos
|
|
1708
1736
|
WHERE repos.name != 'spec-engine'
|
|
1709
|
-
ORDER BY repos.name
|
|
1737
|
+
ORDER BY repos.dependency_depth, repos.name
|
|
1710
1738
|
`;
|
|
1711
1739
|
|
|
1712
1740
|
// src/storage/sql/reads.ts
|
|
1713
|
-
var LIST_RELATIONS_SQL, LIST_TERM_ALIASES_SQL = "SELECT term_id, name FROM term_aliases ORDER BY term_id, name", LIST_TERM_CITATIONS_SQL, LIST_PROVENANCE_SQL, LIST_PROVENANCE_MATRIX_SQL, PROVENANCE_BY_ISSUE_SQL, LIST_REPOS_SQL = "SELECT name, path, pinned_spec_version FROM repos ORDER BY name", GET_REPO_SQL = "SELECT name, path, pinned_spec_version FROM repos WHERE name = $name", LIST_DOMAINS_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains ORDER BY key", GET_DOMAIN_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains WHERE key = $key", LIST_TAGS_SQL = `SELECT id, req_id, repo, file, line, kind, level FROM tags
|
|
1741
|
+
var LIST_RELATIONS_SQL, LIST_TERM_ALIASES_SQL = "SELECT term_id, name FROM term_aliases ORDER BY term_id, name", LIST_TERM_CITATIONS_SQL, LIST_PROVENANCE_SQL, LIST_PROVENANCE_MATRIX_SQL, PROVENANCE_BY_ISSUE_SQL, LIST_REPOS_SQL = "SELECT name, path, pinned_spec_version, dependency_depth FROM repos ORDER BY name", GET_REPO_SQL = "SELECT name, path, pinned_spec_version, dependency_depth FROM repos WHERE name = $name", LIST_DOMAINS_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains ORDER BY key", GET_DOMAIN_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains WHERE key = $key", LIST_TAGS_SQL = `SELECT id, req_id, repo, file, line, kind, level FROM tags
|
|
1714
1742
|
WHERE ($repo IS NULL OR repo = $repo)
|
|
1715
1743
|
AND ($req_id IS NULL OR req_id = $req_id)
|
|
1716
1744
|
AND ($file IS NULL OR file = $file)
|
|
@@ -1983,7 +2011,7 @@ class SqliteStorage {
|
|
|
1983
2011
|
}
|
|
1984
2012
|
#makeWriteHandle() {
|
|
1985
2013
|
const db = this.#db;
|
|
1986
|
-
const insRepo = db.prepare("INSERT OR REPLACE INTO repos (name, path, pinned_spec_version) VALUES ($name, $path, $pin)");
|
|
2014
|
+
const insRepo = db.prepare("INSERT OR REPLACE INTO repos (name, path, pinned_spec_version, dependency_depth) VALUES ($name, $path, $pin, $depth)");
|
|
1987
2015
|
const insDomain = db.prepare("INSERT OR REPLACE INTO domains (key, owner, schema, spec_version, source_repo) " + "VALUES ($key, $owner, $schema, $ver, $source)");
|
|
1988
2016
|
const insReq = db.prepare("INSERT OR REPLACE INTO requirements " + "(id, key, seq, status, superseded_by, text, why, source_file, line, spec_version, changed_at_version, superseded_at_version) " + "VALUES ($id, $key, $seq, $status, $sup, $text, $why, $file, $line, $sver, $cver, $saver)");
|
|
1989
2017
|
const insTag = db.prepare("INSERT INTO tags (req_id, repo, file, line, kind, level) " + "VALUES ($req, $repo, $file, $line, $kind, $level)");
|
|
@@ -2013,7 +2041,8 @@ class SqliteStorage {
|
|
|
2013
2041
|
insRepo.run({
|
|
2014
2042
|
name: r.name,
|
|
2015
2043
|
path: r.path,
|
|
2016
|
-
pin: r.pinned_spec_version
|
|
2044
|
+
pin: r.pinned_spec_version,
|
|
2045
|
+
depth: r.dependency_depth
|
|
2017
2046
|
});
|
|
2018
2047
|
},
|
|
2019
2048
|
upsertDomain(d) {
|
|
@@ -2136,11 +2165,7 @@ function poisonSchemaVersion(path, badVersion) {
|
|
|
2136
2165
|
function poisonRepoRow(path, name) {
|
|
2137
2166
|
const db = new Database(path);
|
|
2138
2167
|
try {
|
|
2139
|
-
db.run("INSERT INTO repos (name, path, pinned_spec_version) VALUES (?, ?, ?)", [
|
|
2140
|
-
name,
|
|
2141
|
-
"/dev/null",
|
|
2142
|
-
999
|
|
2143
|
-
]);
|
|
2168
|
+
db.run("INSERT INTO repos (name, path, pinned_spec_version, dependency_depth) VALUES (?, ?, ?, ?)", [name, "/dev/null", 999, 0]);
|
|
2144
2169
|
} finally {
|
|
2145
2170
|
db.close();
|
|
2146
2171
|
}
|
|
@@ -2165,7 +2190,7 @@ function computeBuildId(storage2) {
|
|
|
2165
2190
|
},
|
|
2166
2191
|
{
|
|
2167
2192
|
label: "repos",
|
|
2168
|
-
sql: "SELECT name, path, pinned_spec_version FROM repos ORDER BY name"
|
|
2193
|
+
sql: "SELECT name, path, pinned_spec_version, dependency_depth FROM repos ORDER BY name"
|
|
2169
2194
|
},
|
|
2170
2195
|
{
|
|
2171
2196
|
label: "domains",
|
|
@@ -2511,7 +2536,8 @@ function buildRows(input) {
|
|
|
2511
2536
|
{
|
|
2512
2537
|
name: canonical.name,
|
|
2513
2538
|
path: canonical.path,
|
|
2514
|
-
pinned_spec_version: platformVersion
|
|
2539
|
+
pinned_spec_version: platformVersion,
|
|
2540
|
+
dependency_depth: 0
|
|
2515
2541
|
},
|
|
2516
2542
|
...members
|
|
2517
2543
|
], (r) => r.name);
|
|
@@ -5654,19 +5680,15 @@ var init_list = __esm(() => {
|
|
|
5654
5680
|
});
|
|
5655
5681
|
|
|
5656
5682
|
// src/propagation/format.ts
|
|
5657
|
-
function sortPropagation(rows) {
|
|
5658
|
-
return [...rows].sort((a, b) => a.repo.localeCompare(b.repo));
|
|
5659
|
-
}
|
|
5660
5683
|
function renderPropagation(rows, mode) {
|
|
5661
|
-
const sorted = sortPropagation(rows);
|
|
5662
5684
|
if (mode === "json") {
|
|
5663
|
-
return JSON.stringify(
|
|
5685
|
+
return JSON.stringify(rows);
|
|
5664
5686
|
}
|
|
5665
|
-
if (
|
|
5687
|
+
if (rows.length === 0)
|
|
5666
5688
|
return "";
|
|
5667
5689
|
const header = ["REPO", "STATE", "VIA", "DRIFT?"];
|
|
5668
5690
|
const lines = [header];
|
|
5669
|
-
for (const row of
|
|
5691
|
+
for (const row of rows) {
|
|
5670
5692
|
lines.push([row.repo, row.state, row.via_req_id ?? EMPTY_CELL2, row.drifted ? "yes" : "no"]);
|
|
5671
5693
|
}
|
|
5672
5694
|
const widths = header.map((_, col) => Math.max(...lines.map((row) => row[col]?.length ?? 0)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spec-engine/spec-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Cross-repo spec engine — durable requirements with permanent IDs, bound to code via @spec tags, checked in CI. Ships the spec CLI, the local coverage webapp (spec serve), and offline docs (spec docs).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|