clearotron 0.3.2-beta.8 → 0.3.2-beta.9

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 (238) hide show
  1. package/.env.example +34 -3
  2. package/CONTRIBUTING.md +8 -8
  3. package/INSTALL.md +6 -6
  4. package/SECURITY.md +3 -3
  5. package/bin/brandowner.mjs +3 -3
  6. package/bin/framework-preflight.mjs +1 -1
  7. package/bin/start.mjs +18 -4
  8. package/build-info.json +2 -2
  9. package/docs/DELIVERY.md +2 -1
  10. package/docs/INTAKE.md +1 -1
  11. package/docs/ONBOARDING.md +1 -1
  12. package/docs/architecture/03-run-lifecycle.md +6 -6
  13. package/docs/architecture/04-configuration-reference.md +6 -3
  14. package/docs/architecture/05-config-governance.md +6 -1
  15. package/docs/architecture/05-customer-profiles.md +2 -2
  16. package/docs/architecture/06-operations-runbook.md +3 -3
  17. package/docs/architecture/08-development-guide.md +6 -6
  18. package/docs/configuration.md +5 -5
  19. package/docs/decisions/0003-credential-model.md +1 -1
  20. package/docs/writing-standard.md +4 -0
  21. package/driver/CHANGELOG.md +48 -0
  22. package/driver/README.md +3 -3
  23. package/driver/binding-layers.mjs +1 -1
  24. package/driver/citation-census.json +3 -3
  25. package/driver/{prelim-variants-record.mjs → clearance-variants-record.mjs} +24 -24
  26. package/driver/common-law-receipts.mjs +2 -2
  27. package/driver/company-bundle.mjs +3 -3
  28. package/driver/compose-read.mjs +8 -14
  29. package/driver/consumption-ledger.mjs +2 -2
  30. package/driver/contract-arm2-baseline.json +2 -3
  31. package/driver/contract-dictation-registry.mjs +19 -19
  32. package/driver/contract-e3-backlog.mjs +19 -19
  33. package/driver/contract-e3-baseline.json +14 -14
  34. package/driver/contract-vocabulary.mjs +24 -17
  35. package/driver/deliver-trigger.sh +16 -16
  36. package/driver/demo-container.mjs +3 -3
  37. package/driver/dev-portal.mjs +3 -3
  38. package/driver/disposition-call.mjs +1 -1
  39. package/driver/doubt-ledger.mjs +2 -2
  40. package/driver/drainer-identity.mjs +34 -8
  41. package/driver/driver.config.mjs +95 -45
  42. package/driver/engine/mcp/README.md +1 -1
  43. package/driver/engine/mcp/dispositions-server.mjs +3 -3
  44. package/driver/engine/mcp/gather-config.mjs +9 -9
  45. package/driver/engine/mcp/perplexity-server.mjs +2 -2
  46. package/driver/engine/mcp/recording-server.mjs +5 -5
  47. package/driver/enqueue-schema.mjs +6 -2
  48. package/driver/findings-model.mjs +5 -2
  49. package/driver/flag-snapshot.mjs +6 -3
  50. package/driver/form-neighbourhood.mjs +54 -7
  51. package/driver/framework.mjs +4 -4
  52. package/driver/gateway.mjs +12 -6
  53. package/driver/jx-lanes.mjs +2 -2
  54. package/driver/jx-units.mjs +1 -1
  55. package/driver/jx.mjs +30 -2
  56. package/driver/knockout-review-record.mjs +56 -4
  57. package/driver/known-conflicts.mjs +1 -1
  58. package/driver/named-band.mjs +1 -33
  59. package/driver/ordinary-words.mjs +51 -0
  60. package/driver/outbox-backoff.mjs +31 -16
  61. package/driver/package.json +1 -1
  62. package/driver/partial-payload-baseline.json +2 -2
  63. package/driver/phase0.mjs +3 -3
  64. package/driver/pipeline-knockout.mjs +5 -5
  65. package/driver/pipeline.mjs +206 -68
  66. package/driver/placement-form.mjs +77 -1
  67. package/driver/placement-model.mjs +1 -1
  68. package/driver/portal-report.mjs +92 -5
  69. package/driver/portal-service.mjs +34 -8
  70. package/driver/portal-upstream.mjs +1 -1
  71. package/driver/preserve-merge.mjs +3 -3
  72. package/driver/product-rows.mjs +2 -2
  73. package/driver/products.mjs +1 -1
  74. package/driver/profiles/README.md +3 -3
  75. package/driver/profiles/demo-brand-owner.json +2 -2
  76. package/driver/profiles.mjs +55 -17
  77. package/driver/progress.mjs +18 -8
  78. package/driver/provider-usage.mjs +8 -8
  79. package/driver/publish/index.mjs +110 -5
  80. package/driver/publish/knockout.mjs +29 -4
  81. package/driver/publish/pool-admin.mjs +1 -1
  82. package/driver/publish/publish-inputs.mjs +18 -2
  83. package/driver/publish/render-knockout.mjs +115 -24
  84. package/driver/publish/render.mjs +153 -34
  85. package/driver/publish/search-depth.mjs +133 -4
  86. package/driver/publish/templates/report.css +60 -3
  87. package/driver/publish/xlsx.mjs +8 -1
  88. package/driver/queue-order.mjs +2 -2
  89. package/driver/recording-agreement.mjs +1 -1
  90. package/driver/reference-score.mjs +1 -1
  91. package/driver/register-count.mjs +50 -5
  92. package/driver/register-coverage.mjs +67 -0
  93. package/driver/register-grant-vocabulary.mjs +1 -1
  94. package/driver/register-plan.mjs +19 -2
  95. package/driver/registry-fidelity.mjs +3 -3
  96. package/driver/repair-composers.mjs +1 -1
  97. package/driver/repair-contract.mjs +1 -1
  98. package/driver/replay-archive.mjs +6 -6
  99. package/driver/report-overview-record.mjs +2 -2
  100. package/driver/run-requirements.mjs +3 -3
  101. package/driver/runner.mjs +2 -2
  102. package/driver/scope-facts.mjs +20 -5
  103. package/driver/scope-ledger.mjs +5 -5
  104. package/driver/search-policy.mjs +22 -12
  105. package/driver/skills/README.md +15 -15
  106. package/driver/skills/blind-frame/SKILL.md +2 -2
  107. package/driver/skills/case-law-citation/SKILL.md +4 -4
  108. package/driver/skills/case-law-citation/sources/eurlex.md +1 -1
  109. package/driver/skills/{prelim-common-law → clearance-common-law}/SKILL.md +22 -22
  110. package/driver/skills/{prelim-common-law → clearance-common-law}/perplexity-prompts.md +1 -1
  111. package/driver/skills/{prelim-register → clearance-register}/SKILL.md +10 -10
  112. package/driver/skills/{prelim-register → clearance-register}/digest.md +2 -2
  113. package/driver/skills/{prelim-register → clearance-register}/providers/README.md +1 -1
  114. package/driver/skills/{prelim-register → clearance-register}/providers/clarivate.md +37 -35
  115. package/driver/skills/{prelim-register → clearance-register}/providers/corsearch.md +20 -11
  116. package/driver/skills/{prelim-register → clearance-register}/providers/signa.md +5 -5
  117. package/driver/skills/{prelim-register → clearance-register}/register-recipes.md +3 -3
  118. package/driver/skills/{prelim-register → clearance-register}/status-rules.md +2 -2
  119. package/driver/skills/{prelim-register → clearance-register}/stealth-filer-indicators.md +1 -1
  120. package/driver/skills/{prelim-register → clearance-register}/unit.md +2 -2
  121. package/driver/skills/{prelim-search → clearance-search}/SKILL.md +31 -31
  122. package/driver/skills/{prelim-search → clearance-search}/delivery-contract.md +1 -1
  123. package/driver/skills/{prelim-search → clearance-search}/phase2-execution.md +18 -18
  124. package/driver/skills/{prelim-search → clearance-search}/synthesis-rules.md +7 -7
  125. package/driver/skills/{prelim-variants → clearance-variants}/SKILL.md +18 -18
  126. package/driver/skills/{prelim-variants → clearance-variants}/transliteration-scripts.md +5 -5
  127. package/driver/skills/frame-diff/SKILL.md +1 -1
  128. package/driver/skills/knockout-assess/SKILL.md +10 -7
  129. package/driver/skills/matter-frame/SKILL.md +3 -3
  130. package/driver/skills/narrative-refutation/SKILL.md +9 -9
  131. package/driver/skills/placement-inquiry/SKILL.md +5 -5
  132. package/driver/stage-context.mjs +1 -1
  133. package/driver/stages-knockout.mjs +4 -4
  134. package/driver/stages.mjs +53 -53
  135. package/driver/status-snapshot.mjs +2 -2
  136. package/driver/suite-census.json +218 -92
  137. package/driver/surface-exit-verdict.mjs +58 -0
  138. package/driver/systemd/README.md +2 -2
  139. package/driver/systemd/clearotron-worker.service +1 -1
  140. package/driver/terminal-clamp.mjs +2 -0
  141. package/driver/usage-ledger.mjs +1 -1
  142. package/driver/variant-manifest-model.mjs +4 -4
  143. package/driver/verify-knockout.mjs +27 -0
  144. package/driver/verify.mjs +67 -6
  145. package/driver/whatif-queue.mjs +1 -1
  146. package/driver/wordlists/en.txt +63906 -0
  147. package/mcp-server/CHANGELOG.md +4 -0
  148. package/mcp-server/README.md +1 -1
  149. package/mcp-server/lib/README.md +1 -1
  150. package/mcp-server/lib/options.mjs +8 -7
  151. package/mcp-server/lib/plan.mjs +18 -2
  152. package/mcp-server/lib/runs.mjs +1 -1
  153. package/mcp-server/lib/usage.mjs +3 -3
  154. package/mcp-server/lib/whatif.mjs +1 -1
  155. package/mcp-server/package.json +1 -1
  156. package/mcp-server/server.mjs +3 -0
  157. package/package.json +12 -11
  158. package/portal-ui/dist/assets/{index-CVOIvdhc.css → index-CtvwLCti.css} +207 -3
  159. package/portal-ui/dist/assets/{index-6jzO9HiX.js → index-EVaSo5-g.js} +1459 -482
  160. package/portal-ui/dist/index.html +2 -2
  161. package/portal-ui/package.json +1 -1
  162. package/providers/README.md +1 -1
  163. package/providers/_shared/enumerate.mjs +6 -6
  164. package/providers/_shared/execute-plan.mjs +3 -3
  165. package/providers/_shared/ledger.mjs +119 -5
  166. package/providers/_shared/provider-text.mjs +2 -2
  167. package/providers/_shared/screen.mjs +2 -2
  168. package/providers/_shared/script-form.mjs +3 -3
  169. package/providers/_shared/territory-codes.mjs +23 -3
  170. package/providers/clarivate/README.md +1 -1
  171. package/providers/clarivate/src/capabilities.js +12 -12
  172. package/providers/clarivate/src/core.js +37 -43
  173. package/providers/corsearch/README.md +1 -1
  174. package/providers/corsearch/src/capabilities.js +5 -5
  175. package/providers/corsearch/src/core.js +3 -3
  176. package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
  177. package/providers/oauth-mcp-bridge/package.json +1 -1
  178. package/providers/perplexity/src/core.js +1 -1
  179. package/providers/signa/README.md +1 -1
  180. package/providers/signa/src/capabilities.js +42 -49
  181. package/providers/signa/src/core.js +106 -29
  182. package/providers/uspto-local/src/sync.js +1 -1
  183. package/scripts/README.md +1 -0
  184. package/scripts/ask-ai-render-check.mjs +127 -1
  185. package/scripts/authority-boundary-probe.mjs +4 -4
  186. package/scripts/backfill-started-at.mjs +2 -2
  187. package/scripts/census-merge-driver.mjs +33 -2
  188. package/scripts/citation-anchor-report.mjs +181 -0
  189. package/scripts/dead-names.mjs +1 -1
  190. package/scripts/deprecate-below.mjs +66 -8
  191. package/scripts/drain-preflight.mjs +1 -1
  192. package/scripts/e2e.mjs +174 -0
  193. package/scripts/env-audit.mjs +27 -0
  194. package/scripts/env-classify.mjs +20 -2
  195. package/scripts/freeze-example-run.mjs +20 -10
  196. package/scripts/live-surface-check.mjs +124 -41
  197. package/scripts/markdown-link-check.mjs +1 -1
  198. package/scripts/merge-shape-check.mjs +242 -0
  199. package/scripts/mint-names-in-force.mjs +5 -5
  200. package/scripts/mint-offered-territories.mjs +72 -0
  201. package/scripts/mint-public-residue.mjs +2 -2
  202. package/scripts/mint-reference-strip-backlog.mjs +2 -2
  203. package/scripts/mint-suite-census.mjs +75 -2
  204. package/scripts/mint-writing-standard-backlog.mjs +2 -2
  205. package/scripts/purge-runs.mjs +7 -7
  206. package/scripts/reconcile-runs.mjs +2 -2
  207. package/scripts/release-approve-parked.mjs +20 -2
  208. package/scripts/release-await-cut.mjs +120 -1
  209. package/scripts/release-note-required.mjs +76 -8
  210. package/scripts/report-header-render-check.mjs +164 -0
  211. package/shared/brand.mjs +27 -0
  212. package/shared/connect-clients.mjs +39 -11
  213. package/shared/env-aliases.mjs +1 -1
  214. package/shared/identifier-scan.mjs +65 -9
  215. package/shared/identifier-sentinels.mjs +22 -0
  216. package/shared/names-in-force.mjs +3 -1
  217. package/shared/offered-territories.json +738 -0
  218. package/shared/pre-rename-spellings.mjs +53 -0
  219. package/shared/reference-guard-classes.mjs +40 -2
  220. package/shared/stdio-connect.mjs +39 -4
  221. package/shared/tree-commit.mjs +48 -0
  222. /package/driver/skills/{prelim-register → clearance-register}/providers/euipo.md +0 -0
  223. /package/driver/skills/{prelim-register → clearance-register}/providers/free-tier.md +0 -0
  224. /package/driver/skills/{prelim-register → clearance-register}/providers/uspto-local.md +0 -0
  225. /package/driver/skills/{prelim-search → clearance-search}/field-doctrine-pharma.md +0 -0
  226. /package/driver/skills/{prelim-search → clearance-search}/firm-wide-reasoning.md +0 -0
  227. /package/driver/skills/{prelim-search → clearance-search}/report-prose.md +0 -0
  228. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.manifest.json +0 -0
  229. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.md +0 -0
  230. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.manifest.json +0 -0
  231. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.md +0 -0
  232. /package/driver/skills/{prelim-search → clearance-search}/risk-framework.manifest.json +0 -0
  233. /package/driver/skills/{prelim-search → clearance-search}/risk-framework.md +0 -0
  234. /package/driver/skills/{prelim-search → clearance-search}/template-formatting.md +0 -0
  235. /package/driver/skills/{prelim-search → clearance-search}/templates/email/generic.md +0 -0
  236. /package/driver/skills/{prelim-search → clearance-search}/templates/search-request-form.html +0 -0
  237. /package/driver/skills/{prelim-search → clearance-search}/worked-examples-demo.md +0 -0
  238. /package/driver/skills/{prelim-search → clearance-search}/worked-examples.md +0 -0
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-only
2
2
  // Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
3
- // progress.mjs — live run status for the prelim-search driver.
3
+ // progress.mjs — live run status for the clearance-search driver.
4
4
  //
5
5
  // Two artifacts, both written by the driver into the FORWARDING agent's own workspace
6
6
  // (so the agent's sandboxed read tool can see them — agents can't exec, so on-demand status is a
@@ -14,7 +14,7 @@
14
14
  // blindly incremented — so the resumable pipeline can re-drive a run without corrupting either file.
15
15
 
16
16
  import { readFileSync, writeFileSync, renameSync, unlinkSync, readdirSync, statSync, existsSync } from "node:fs";
17
- import { join, dirname } from "node:path";
17
+ import { join, dirname } from "node:path"; import { STUDIO_SEGMENT_RE } from "../shared/pre-rename-spellings.mjs";
18
18
  import { DRIVER_DIR } from "../shared/driver-dir.mjs"; //
19
19
  import { config } from "./driver.config.mjs";
20
20
  import { batchMarkName } from "./mark-name.mjs";
@@ -29,7 +29,7 @@ import { engineCommit, engineCommitSource } from "./engine-build.mjs"; // —
29
29
  // driver's execution units (fan-out register axes, skeptic-escalation re-runs, corrective re-synthesis,
30
30
  // two refutation passes) onto a clean forward-only sequence, so the displayed step never jumps backward.
31
31
  export const DISPLAY_STEPS = [
32
- "Framing the matter", // 1 matter-frame, prelim-variants
32
+ "Framing the matter", // 1 matter-frame, clearance-variants
33
33
  "Register sweeps", // 2 common-law + register-unit:* (fan-out + escalation re-runs collapse here)
34
34
  "Placement & digest", // 3 placement-inquiry, register-digest (+ re-digest)
35
35
  "Skeptic review", // 4 skeptic
@@ -48,7 +48,7 @@ export const DISPLAY_STEPS = [
48
48
  // UNLABELLED GAP on the stepper the client watches — the run looks stalled while it is working. Three
49
49
  // stages were sitting in that state (blind-frame, frame-diff, doubt-closure); each now says so by name.
50
50
  export const STAGE_TO_STEP = {
51
- "matter-frame": 0, "prelim-variants": 0,
51
+ "matter-frame": 0, "clearance-variants": 0,
52
52
  "common-law": 1, "common-law-half": 1, "register-unit": 1,
53
53
  "placement-inquiry": 2, "register-digest": 2,
54
54
  skeptic: 3,
@@ -398,11 +398,21 @@ export function seedRunStatus(ctx, { resume = false } = {}) {
398
398
  }, null, { critical: true }); // door B — the clearance lane's identity seed, same rule
399
399
  }
400
400
 
401
- // Convenience: advance the run to the step for `rawStageKey` (no-op for unmapped keys) then refresh STATUS.md.
401
+ // Record that the run is at `rawStageKey`: advance the displayed step where the stage has one, and say
402
+ // what the run is doing either way, then refresh STATUS.md.
403
+ //
404
+ // AN UNMAPPED STAGE MOVES NO STEP AND IS STILL SOMETHING THE RUN IS DOING. This returned early for one,
405
+ // so it wrote no `lastStage` at all — and `lastStage` is what status-snapshot publishes as "what the run
406
+ // is actually doing". Three stages have no display step ON PURPOSE (STAGE_NO_STEP names each and why),
407
+ // so for the whole of any of them every surface went on naming the PREVIOUS stage, which is the same
408
+ // defect as a stale step wearing a different field. The step fields are still withheld — that part of
409
+ // the early return was right, and an unmapped stage must never touch the displayed step.
402
410
  export function recordTransition(ctx, rawStageKey) {
403
411
  const step = stepForStage(rawStageKey);
404
- if (!step) return;
405
- writeRunStatus(ctx, { stepIndex: step.index, stepLabel: step.label, stepN: step.n, stepTotal: step.total, lastStage: rawStageKey });
412
+ writeRunStatus(ctx, {
413
+ ...(step ? { stepIndex: step.index, stepLabel: step.label, stepN: step.n, stepTotal: step.total } : {}),
414
+ lastStage: rawStageKey,
415
+ });
406
416
  rollupStatus(ctx?.run?.studioRoot);
407
417
  }
408
418
 
@@ -424,7 +434,7 @@ function findStatusFiles(root, depth, acc) {
424
434
  }
425
435
 
426
436
  function agentFromStudioRoot(studioRoot) {
427
- const m = new RegExp(`${config.workspacePrefixRe}([^/]+)/studio/prelim-search/?$`).exec(studioRoot ?? "");
437
+ const m = new RegExp(`${config.workspacePrefixRe}([^/]+)/studio/${STUDIO_SEGMENT_RE}/?$`).exec(studioRoot ?? "");
428
438
  return m ? m[1] : "";
429
439
  }
430
440
 
@@ -14,9 +14,9 @@
14
14
  //
15
15
  // This module is read-only and defensive: a missing/unreadable ledger or a torn last line (two gateway
16
16
  // turns appending concurrently) returns zeros / skips that line — it never throws. The driver calls it at
17
- // publish time to attribute THIS run's calls by the session-key prefix `prelim-<slug>-<codename>-`.
17
+ // publish time to attribute THIS run's calls by the session-key prefix `clearance-<slug>-<codename>-`.
18
18
 
19
- import { readFileSync, existsSync } from "node:fs";
19
+ import { readFileSync, existsSync } from "node:fs"; import { runPrefixSpellings } from "../shared/pre-rename-spellings.mjs";
20
20
  // Aliased: three functions below take a parameter literally named `ledgerPath`, and an unaliased
21
21
  // import would be shadowed by it inside its own default-value expression (a TDZ ReferenceError
22
22
  // at the first call, not at load).
@@ -46,7 +46,7 @@ export const DEFAULT_LEDGER_PATH = resolveLedgerPath("call");
46
46
  export const KINDS = ["search", "record_fetch", "image", "phoneme", "batch_screen", "enumerate", "execute_plan", "propose_supplemental"];
47
47
 
48
48
  // The gateway namespaces the driver's --session-key as `agent:<agentId>:<key>` before it reaches the
49
- // plugin (confirmed on the first live run: sessionKey = `agent:clawdi:prelim-<slug>-<codename>-…`). Strip
49
+ // plugin (confirmed on the first live run: sessionKey = `agent:clawdi:clearance-<slug>-<codename>-…`). Strip
50
50
  // that leading `agent:<id>:` namespace so the run prefix anchors at the real start of the caller's key —
51
51
  // otherwise a bare startsWith("clearotron-…") matches nothing.
52
52
  function stripGatewayNs(s) {
@@ -54,11 +54,11 @@ function stripGatewayNs(s) {
54
54
  }
55
55
 
56
56
  // Does this ledger row belong to the run identified by `runPrefix`? The driver's --session-key is
57
- // `prelim-<slug>-<codename>-<stage><axis>` (+ optional `-rerunN`), so a prefix match catches every stage +
57
+ // `clearance-<slug>-<codename>-<stage><axis>` (+ optional `-rerunN`), so a prefix match catches every stage +
58
58
  // axis + retry of the run. We check sessionKey (carries the key) and, defensively, sessionId.
59
59
  function rowMatchesRun(row, runPrefix) {
60
- return stripGatewayNs(row.sessionKey).startsWith(runPrefix)
61
- || stripGatewayNs(row.sessionId).startsWith(runPrefix);
60
+ return runPrefixSpellings(runPrefix).some((rp) => stripGatewayNs(row.sessionKey).startsWith(rp)
61
+ || stripGatewayNs(row.sessionId).startsWith(rp)); // either spelling: a run resumed across the rename
62
62
  }
63
63
 
64
64
  // ── band-truth gate (2026-07-14, teal-foundry): count the ledger rows attributed to ONE unit lane ──────
@@ -146,7 +146,7 @@ function emptyTally() {
146
146
  /**
147
147
  * Tally every ledger line whose gateway id starts with `runPrefix`.
148
148
  * @param {string} ledgerPath path to the JSONL ledger
149
- * @param {string} runPrefix e.g. `prelim-acme-bluejay-` (note the trailing hyphen)
149
+ * @param {string} runPrefix e.g. `clearance-acme-bluejay-` (note the trailing hyphen)
150
150
  * @returns {object} the tally (see emptyTally) — never throws
151
151
  */
152
152
  export function tallyRegisterCalls(ledgerPath = DEFAULT_LEDGER_PATH, runPrefix) {
@@ -208,7 +208,7 @@ export function tallyRegisterCalls(ledgerPath = DEFAULT_LEDGER_PATH, runPrefix)
208
208
  * (missing/unreadable ledger or a torn last line ⇒ skips that line, never throws). A cache_hit record_fetch
209
209
  * still counts — the URI WAS fetched this run, which is exactly what the gate asks.
210
210
  * @param {string} ledgerPath path to the JSONL ledger
211
- * @param {string} runPrefix e.g. `prelim-acme-bluejay-` (note the trailing hyphen)
211
+ * @param {string} runPrefix e.g. `clearance-acme-bluejay-` (note the trailing hyphen)
212
212
  * @returns {Set<string>} the set of fetched record URIs
213
213
  */
214
214
  export function fetchedRecordUris(ledgerPath = DEFAULT_LEDGER_PATH, runPrefix) {
@@ -16,7 +16,7 @@ import { buildAudit } from './xlsx.mjs';
16
16
  import { parseFindingsJson, parseFindingsJsonLenient, deriveDisplayVerdict, joinFindingToBlock, CLIENT_TIER_BY_COMPOSITE, projectCoverageJudgment } from '../findings-model.mjs';
17
17
  import { readStore, requiredAbsent, nonClosingAbsences } from './publish-inputs.mjs'; // — and why an absence did not close
18
18
  import { clearanceReportData } from './report-data.mjs';
19
- import { searchDepthRecord } from './search-depth.mjs'; // how much was read to reach the answer, as counts and tokens
19
+ import { searchDepthRecord, planTerritoriesOf } from './search-depth.mjs'; import { bandRecords } from '../named-band.mjs'; // how much was read to reach the answer, as counts and tokens
20
20
  import { parseFrameworkManifest } from '../framework.mjs';
21
21
  import { rollupTokens, servedModels } from '../tokens.mjs';
22
22
  import { reportIdentityFor, productCoverageNote, isRegisterOnly } from '../search-policy.mjs';
@@ -817,6 +817,61 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
817
817
  reasons: droppedConditions.map((c) => c.note) });
818
818
  } catch { /* the workbook row is the record that matters; this line is the second copy */ }
819
819
  }
820
+ // ── THE CHECKS THE RUN DECIDED ON AND DID NOT MAKE ─────────────────────────────────────────────
821
+ //
822
+ // The recall net mints a probe per remembered conflict and a probe per owner behind one, then
823
+ // dispatches at most five owner probes. The excess is recorded in the run's own receipt and nothing
824
+ // downstream carried it to a reader, so a search that decided on nineteen ownership checks, made five
825
+ // and said nothing about the other fourteen read as a search that made the checks it wanted.
826
+ //
827
+ // The cap is not the defect: it is deliberate and the rows it drops are ranked material-first, so the
828
+ // five that run are the five that matter most. What was missing is the disclosure, and it lands where
829
+ // the 2026-09-17 ruling put the same class of fact — an ordinary row on the workbook's gaps sheet and
830
+ // one line in the run record. Nothing reaches the report page: the approved boards draw a forward
831
+ // decision and an open question back to the client, and a check nobody made is neither.
832
+ //
833
+ // THE WORDS ARE THE RECEIPT'S OWN. The party and the probe id are read from it verbatim; "over the
834
+ // cap" and "never dispatched" are the vocabulary the ask ledger already ships for these same rows.
835
+ // Nothing here composes a sentence, and the note carries no seam, so the gaps sheet's own splitter
836
+ // leaves "What was done" empty — which is the fact: nothing was done.
837
+ // READ THROUGH THE DECLARED HELPER, three states and not two. An absent receipt is a run whose recall
838
+ // net minted nothing — env-gated off, or a matter with no remembered conflict — and there is nothing
839
+ // to disclose. A DAMAGED one is a different fact: the probes may have overflowed and this publish
840
+ // cannot tell, so it says so in the run record instead of shipping the same empty sheet an
841
+ // everything-dispatched run ships. Collapsing those two is the defect publish-inputs.mjs exists for.
842
+ //
843
+ // NOT PUSHED ONTO `inputsAbsent`. That list rides meta.json's clientGate record, whose own note two
844
+ // hundred lines down is that it stays undefined when every store was found so a complete run's meta
845
+ // is byte-identical to one written before the list existed. Most runs have no recall receipt, so
846
+ // adding this store to it would change the meta of every archived run on re-render for a store that
847
+ // feeds a workbook row and not the gate -- the same reason the other presentation-only stores are
848
+ // not on it either.
849
+ // `runDir ?? dirname(reportMd)`, the same base its neighbours take and not a defensive flourish:
850
+ // publishReport's `runDir` is OPTIONAL, and a bare `readStore(runDir, …)` throws TypeError on an
851
+ // undefined base rather than reporting an absent store. Caught in CI by the graceful-stop runner arm,
852
+ // which publishes without one: the throw left publishReport at `published → null` and the runner
853
+ // exited 1. Reading from the report's own directory is also the right answer for a republish.
854
+ const recallStore = readStore(runDir ?? dirname(reportMd), '_driver/register-recall.json');
855
+ const undispatchedProbes = (recallStore.value?.overflow ?? [])
856
+ .filter((o) => o && (o.term || o.qid))
857
+ .map((o) => ({
858
+ area: String(o.term ?? o.qid),
859
+ state: 'not-searched',
860
+ note: `over the cap, never dispatched (${String(o.qid ?? 'no probe id recorded')})`,
861
+ }));
862
+ if (recallStore.state === 'damaged' && runDir) {
863
+ try {
864
+ runLog(runDir, { event: 'probe-over-cap-unreadable', store: recallStore.name, error: recallStore.error });
865
+ } catch { /* best-effort, as below */ }
866
+ }
867
+ // Best-effort, as above: a publish never fails for want of a log line.
868
+ if (undispatchedProbes.length && runDir) {
869
+ try {
870
+ runLog(runDir, { event: 'probe-over-cap-undispatched', n: undispatchedProbes.length,
871
+ parties: undispatchedProbes.map((p) => p.area) });
872
+ } catch { /* the workbook row is the record that matters; this line is the second copy */ }
873
+ }
874
+
820
875
  // doc 50 — the run's FROZEN framework manifest (band vocabulary). Present on band-doctrine runs;
821
876
  // absent on every archived run (they render byte-identically on the legacy paths).
822
877
  let framework = null;
@@ -845,11 +900,25 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
845
900
  // two must not render the same way. Null on archived runs ⇒ render keeps the prose fallback.
846
901
  let scopeBasis = null;
847
902
  let searchedJurisdictions = [];
903
+ // WHAT WAS SEARCHED IS THE PLAN'S ANSWER, NOT THE ARCHIVE'S, and it is carried separately from the
904
+ // header's list below because the two are used for different things and one of them is deliberately
905
+ // emptied. The coverage presentation asks which territories the run searched and which it could not
906
+ // reach; the plan compiles both — entries carry the regions it will query, `deferred_coverage` carries
907
+ // the ones this provider does not cover with the reason each. A register that archives no records
908
+ // still has both, which is the whole point: keyed off the record store, a provider that keeps nothing
909
+ // looked like a provider nobody asked.
910
+ //
911
+ // THREE-VALUED like the record listing above it: null means there is no plan to read, so the run
912
+ // cannot say — an archived or legacy run — and it must not be confused with a plan that named nothing.
913
+ let planTerritories = null;
848
914
  try {
849
915
  const plan = JSON.parse(readFileSync(driverDir(runDir ?? dirname(reportMd), 'register-plan.json'), 'utf8'));
850
916
  if (plan?.scope_basis === 'worldwide') scopeBasis = 'worldwide';
851
917
  searchedJurisdictions = [...new Set((plan?.entries ?? []).flatMap((e) => Array.isArray(e.regions) ? e.regions : []))];
852
918
  if (!searchedJurisdictions.length && Array.isArray(plan?.regions)) searchedJurisdictions = [...new Set(plan.regions)];
919
+ // Derived here, BEFORE the worldwide clearing below — that clearing exists for the header and would
920
+ // otherwise empty this on exactly the runs that cover the most ground.
921
+ planTerritories = planTerritoriesOf(plan);
853
922
  } catch { /* no plan sidecar — try the instructed scope */ }
854
923
  if (!searchedJurisdictions.length) {
855
924
  try {
@@ -1065,7 +1134,7 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
1065
1134
  // the same rule (the workbook's own BANNED gate had already started firing on the raw detail —
1066
1135
  // advisory, so CI stayed green). reviewReceipts.lint keeps its raw detail for the internal
1067
1136
  // readers above (fetchState reads registry-record-coverage's URIs out of it).
1068
- counts = await buildAudit({ droppedConditions, findings, coverage, contextNotes, coverageJudgment, markAssessment, corrections: correctionsDoc, fetchState, verdict: verdictInfo, jurisdiction, commonLawJoinedTerms, registerOnly, clientGate, lintFailures: deliveryFlagLines(reviewReceipts.lint), productName, registerPublishesRecordPages: runOrigins == null ? null : runOrigins.length > 0, recordLinks: officeLinks?.byUri ?? null }, auditParsed, join(poolRunDir, auditFile), fm.title, fm);
1137
+ counts = await buildAudit({ droppedConditions, undispatchedProbes, findings, coverage, contextNotes, coverageJudgment, markAssessment, corrections: correctionsDoc, fetchState, verdict: verdictInfo, jurisdiction, commonLawJoinedTerms, registerOnly, clientGate, lintFailures: deliveryFlagLines(reviewReceipts.lint), productName, registerPublishesRecordPages: runOrigins == null ? null : runOrigins.length > 0, recordLinks: officeLinks?.byUri ?? null }, auditParsed, join(poolRunDir, auditFile), fm.title, fm);
1069
1138
  grpRead(join(poolRunDir, auditFile), 0o640);
1070
1139
  if (counts?.gateViolations?.length) console.warn(`[audit-workbook] advisory: ${counts.gateViolations.join(' | ')}`);
1071
1140
  } catch (e) {
@@ -1098,6 +1167,35 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
1098
1167
  // pattern — a run with no grid or no case-law layer still gets its register counts, and the absent
1099
1168
  // ones report themselves as zero or `not-in-scope` rather than as a gap nobody can see.
1100
1169
  let searchDepth = null;
1170
+ // HOW DEEP THE LOCAL-LANGUAGE INVESTIGATION WENT, and it is derived by the one author of that
1171
+ // question rather than re-read here. The import is LAZY and gated on the run's own sidecar existing,
1172
+ // which keeps the property the pipeline's own fold keeps: a plain clearance never loads the jx
1173
+ // machinery at all. No sidecar means the component never ran, which the fold reports as not-in-scope.
1174
+ let laneDepthVerdicts = null;
1175
+ try {
1176
+ const jxSidecar = driverDir(runDir ?? dirname(reportMd), 'jx-lanes.json');
1177
+ if (existsSync(jxSidecar)) {
1178
+ const sidecar = JSON.parse(readFileSync(jxSidecar, 'utf8'));
1179
+ const { laneDepthOfRun } = await import('../jx.mjs');
1180
+ let units = null;
1181
+ try { units = JSON.parse(readFileSync(driverDir(runDir ?? dirname(reportMd), 'jx/units.json'), 'utf8')); } catch { /* the statement handles an absent units file */ }
1182
+ // THE RUN ALREADY ANSWERED THIS, AND THE STAMPED ANSWER IS THE ONE THAT COUNTS.
1183
+ //
1184
+ // `deriveJxSliceStatement` writes `fold.depth` at delivery, from the run's OWN environment, for the
1185
+ // reason the seam exists at all: the arms are environment, so a verdict derived later can only speak
1186
+ // for the box it is derived on. Publishing re-derived it here — a second author for a fact the run
1187
+ // had already stated — and the two disagreed on a delivered client report.
1188
+ //
1189
+ // The disagreement was not subtle. This call passed `deriveJxSliceStatement`'s whole return,
1190
+ // `{executes, slices}`, where the function reads `slices.candidates` — so the lookup found nothing,
1191
+ // every lane's `ran` came back null whatever it had really done, and a full-country JP run that
1192
+ // searched in Japanese printed "Local-language investigation · Not run this run". The stamped
1193
+ // verdict on the same run's sidecar said `ran: "candidates"`, correctly.
1194
+ //
1195
+ // So: read what the run stated, and derive only for a run delivered before the stamp existed.
1196
+ laneDepthVerdicts = laneDepthOfRun({ sidecar, units });
1197
+ }
1198
+ } catch { /* an unreadable sidecar reports as not-in-scope rather than failing a publish */ }
1101
1199
  try {
1102
1200
  const runBase = runDir ?? dirname(reportMd);
1103
1201
  const recDir = join(runBase, '_records');
@@ -1106,15 +1204,22 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
1106
1204
  searchDepth = searchDepthRecord({
1107
1205
  auditMd: (auditMd && existsSync(auditMd)) ? rdText(auditMd) : '',
1108
1206
  recordIndex: recordsByUri ?? {},
1109
- recordFileNames: existsSync(recDir) ? readdirSync(recDir) : [],
1207
+ // null, NOT []: a run whose provider archives no records has no `_records/` at all, and an empty
1208
+ // array is a register that was searched and returned nothing. They are different facts and the
1209
+ // page says different things about them, so the distinction this line already computes is kept
1210
+ // rather than thrown away one character later.
1211
+ recordFileNames: existsSync(recDir) ? readdirSync(recDir) : null,
1212
+ // …and where there is no archive, what the register returned: the band's own record ids.
1213
+ bandRecordIds: existsSync(recDir) ? null : (() => { try { const b = rdJson(join(runBase, 'register-named-band.json')); return b ? bandRecords(b).map((r) => r?.record_id).filter(Boolean) : null; } catch { return null; } })(),
1110
1214
  commonLawGrid: rdJson(join(runBase, 'common-law-grid.json')),
1111
1215
  caseLawText: rdText(join(dirname(reportMd), 'case-law-findings.md')),
1112
1216
  registerPlan: rdJson(driverDir(runBase, 'register-plan.json')),
1217
+ laneDepthVerdicts,
1113
1218
  });
1114
1219
  writeRO('search-depth.json', JSON.stringify(searchDepth, null, 2));
1115
1220
  } catch { /* the depth record is additive — a publish never fails for want of it */ }
1116
1221
 
1117
- writeRO('report.html', renderHtml(parsed, findings, coverage, { demoData, servedModels: served, productName, depthNote, scopeBasis, auditFile: auditFile || undefined, runId, delivery: deliv, recordsByUri, contextNotes, coverageJudgment: coverageJudgmentDisplay, markAssessment, fourAnswers, homeHref: '../index.html', nav: reportNav, chromeHref: '../assets/chrome.css', issued, asOf, verdictInfo, framework, searchedJurisdictions, caseLawByOrdinal, caseLawNotice, enforcerSignals, recordOrigin, recordOrigins: runOrigins, recordCitation: runProviderConf?.recordCitation ?? null, recordLinks: officeLinks?.byUri ?? null, providerLabel, seniorRights, findingsSchemaVersion, searchDepth }));
1222
+ writeRO('report.html', renderHtml(parsed, findings, coverage, { demoData, servedModels: served, productName, depthNote, scopeBasis, auditFile: auditFile || undefined, runId, delivery: deliv, recordsByUri, contextNotes, coverageJudgment: coverageJudgmentDisplay, markAssessment, fourAnswers, homeHref: '../index.html', nav: reportNav, chromeHref: '../assets/chrome.css', issued, asOf, verdictInfo, framework, searchedJurisdictions, planTerritories, caseLawByOrdinal, caseLawNotice, enforcerSignals, recordOrigin, recordOrigins: runOrigins, recordCitation: runProviderConf?.recordCitation ?? null, recordLinks: officeLinks?.byUri ?? null, providerLabel, seniorRights, findingsSchemaVersion, searchDepth }));
1118
1223
  // ONE report (spec 2026-07-30 §5): report.client.html is no longer written. The knockout lane's own
1119
1224
  // collapse note is the precedent: "two renderings of one run is how the wrong link gets sent". The
1120
1225
  // client host serves the same report.html through the portal's readReport() (cleaning built in) — its
@@ -1486,7 +1591,7 @@ export function composeEmailBody(reportMdPath, url, auditFile, productName = nul
1486
1591
  const FONT = "font-family:Calibri,'Segoe UI',Arial,sans-serif";
1487
1592
 
1488
1593
  // 5-tier reporting-template risk scheme — the whole left "NAME / RISK RATING" cell takes the bg colour.
1489
- // Matches skills/prelim-search/templates/search-request-form.html + knockout-searches/template-formatting.md.
1594
+ // Matches skills/clearance-search/templates/search-request-form.html + knockout-searches/template-formatting.md.
1490
1595
  // `txt` = a readable text-colour version of the tier (for the EXECUTIVE SUMMARY risk phrase, where a bright
1491
1596
  // fill like yellow/red is unreadable as text). Order matters: VERY HIGH before HIGH.
1492
1597
  export const RISK_TIERS = [
@@ -7,14 +7,14 @@
7
7
  // self-reported trail), meta.json batch stamps, and the email composer. Deliberately separate from publishReport (single-clearance-shaped); shares the exported
8
8
  // palette (TONE_TIER, resolved against the run's FROZEN framework) so driver and interactive output
9
9
  // can never drift and a knockout never reads the clearance composer's module state.
10
- import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'node:fs';
10
+ import { readFileSync, writeFileSync, copyFileSync, mkdirSync, chmodSync, existsSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
12
  import { driverDir, ensureDriverDir } from '../../shared/driver-dir.mjs'; //
13
13
  import { riskTier, TONE_TIER, regenIndex, regenSurfaces, auditRouteFor, markReportRouteFor, reportRouteFor } from './index.mjs';
14
14
  import { runKnockoutLint, deliveryFlagLines } from '../predelivery-lint.mjs';
15
15
  import { note } from '../log.mjs';
16
16
  import { addSheet } from './xlsx.mjs';
17
- import { COUNT_PREDICATES, COUNT_BASIS, countsForMark, countLine, countedMarks, variantFormsLine } from '../register-count.mjs';
17
+ import { COUNT_PREDICATES, COUNT_BASIS, countsForMark, countLine, countedMarks, variantFormsLine, disclosedFloor, moreThan } from '../register-count.mjs';
18
18
  import { RECORD_BASIS, recordsForMark, recordsLine, listedMarks, normalizeRegisterRecordLinks } from '../register-records.mjs';
19
19
  import { reportIdentityFor, productCoverageNote, kebab } from '../search-policy.mjs';
20
20
  import { batchMarkName } from '../mark-name.mjs';
@@ -143,7 +143,8 @@ export async function buildKnockoutWorkbook(findings, receipts, outPath, registe
143
143
  if (noteRows.length) addSheet(wb, 'Working Notes', ['Mark', 'Type', 'Note'], noteRows);
144
144
 
145
145
  if (registerCounts?.marks?.length) {
146
- const cell = (c) => (Number.isFinite(c?.total) ? c.total : 'not available');
146
+ // A floor the register disclosed is its own figure, printed as the report prints it.
147
+ const cell = (c) => (Number.isFinite(c?.total) ? c.total : disclosedFloor(c) !== null ? moreThan(disclosedFloor(c)) : 'not available');
147
148
  const countRows = (findings.marks ?? []).map((m) => {
148
149
  const e = countsForMark(registerCounts, m.name);
149
150
  const row = {
@@ -161,7 +162,7 @@ export async function buildKnockoutWorkbook(findings, receipts, outPath, registe
161
162
  row['Basis'] = COUNT_BASIS;
162
163
  // The verbatim provider reason for anything missing — the auditable half of "not available".
163
164
  row['Notes'] = COUNT_PREDICATES
164
- .filter((p) => !Number.isFinite(e?.counts?.[p.key]?.total))
165
+ .filter((p) => !Number.isFinite(e?.counts?.[p.key]?.total) && disclosedFloor(e?.counts?.[p.key]) === null)
165
166
  .map((p) => `${p.label}: ${e?.counts?.[p.key]?.unavailable ?? 'no count recorded'}`)
166
167
  .join(' · ');
167
168
  return row;
@@ -335,6 +336,30 @@ export async function publishKnockout({ runId, codename, runDir, findings, plan,
335
336
  const poolRunDir = join(poolRoot, runId);
336
337
  mkdirSync(poolRunDir, { recursive: true });
337
338
  const writeRO = (name, data) => { const p = join(poolRunDir, name); writeFileSync(p, data); grpRead(p, 0o640); };
339
+
340
+ // ── THE RUN'S WORKING RECORD TRAVELS WITH THE REPORT ──────────────────────────────────────────────
341
+ //
342
+ // The delivered directory held the report, its data and the workbook, and nothing the engine itself
343
+ // wrote. That is what the acceptance read needs to tell a line the ENGINE wrote from a line the
344
+ // RENDERER invented: without it the check can still place a line as report text or as a sanctioned
345
+ // label, so it passes — and cannot fail in the way it exists to fail. A clearance run's record is
346
+ // `audit.md` and `findings.json`; this lane's own names are these two, and they are copied rather
347
+ // than rebuilt so the pool carries the bytes the stages wrote.
348
+ //
349
+ // AN ABSENT ONE IS SAID, NOT SKIPPED. A run whose stage never wrote its assessment is a different
350
+ // thing from a run that wrote one, and a publisher that copies what it finds and says nothing about
351
+ // what it does not makes the two look identical to the next reader — which is the whole defect this
352
+ // closes, one directory along.
353
+ for (const name of ['knockout-findings.json', 'knockout-assessment.md']) {
354
+ const src = join(runDir, name);
355
+ if (existsSync(src)) {
356
+ const dest = join(poolRunDir, name);
357
+ copyFileSync(src, dest);
358
+ grpRead(dest, 0o640);
359
+ } else {
360
+ note(`knockout working record: ${name} is not in the run directory, so the delivered report has no engine text to be checked against`);
361
+ }
362
+ }
338
363
  const issued = (() => {
339
364
  try {
340
365
  const d = new Date();
@@ -116,7 +116,7 @@ function linkHome(poolDir) {
116
116
 
117
117
  // Locate an archived run's WORKSPACE dir (the publishReport input set: raw report.md, findings.json,
118
118
  // _driver/ sidecars) across every agent workspace. Layout (pipeline archive step):
119
- // <workspaceRoot>/<workspacePrefix><agent>/studio/prelim-search/archive/<month|legacy-*>/<matter-slug>/<date-codename>/
119
+ // <workspaceRoot>/<workspacePrefix><agent>/studio/clearance-search/archive/<month|legacy-*>/<matter-slug>/<date-codename>/
120
120
  // where `<matter-slug>-<date-codename>` === runId. Returns every match (ambiguity is the caller's error).
121
121
  // The roots route through driver.config.mjs's workspace helpers (never inline the `workspace-` literal —
122
122
  // a deployment picks its own prefix via CLEAROTRON_WORKSPACE_PREFIX / CLEAROTRON_WORK_DIR).
@@ -17,7 +17,7 @@
17
17
  // contract the publication gate held before it was retired into tests at — pass, fail, and COULD
18
18
  // NOT RUN, never folded into two — carried to the seam where a run becomes a client artifact.
19
19
  //
20
- // Modelled on mcp-server/lib/coverage.mjs:65-75 (assertValidatorCoverage): a closed partition asserted
20
+ // Modelled on assertValidatorCoverage() in mcp-server/lib/coverage.mjs (lines 65-75): a closed partition asserted
21
21
  // at LOAD, so a new store cannot be added without consciously choosing its gating. The failure that
22
22
  // mechanism exists to prevent is the — a hand-copied mirror that silently stopped covering an
23
23
  // artifact and still answered "complete: yes" about a file it never checked.
@@ -57,10 +57,26 @@ export const PUBLISH_INPUTS = {
57
57
  "_driver/verdict.json": "optional",
58
58
  // doc 50 — the frozen band vocabulary. Present on band-doctrine runs only, by design.
59
59
  "_driver/framework.json": "optional",
60
+ // The recall net's receipt. Its overflow[] carries the probes the run minted and did not dispatch,
61
+ // one row each on the workbook's gaps sheet. `optional`, and the reason is the recall net's own
62
+ // switches: the lane is env-gated for rollback and mints nothing when a matter has no remembered
63
+ // conflict, so an absent receipt is a run that had nothing to record rather than one that lost it.
64
+ // `required` would close the gate on every knockout and every matter with a clean history.
65
+ "_driver/register-recall.json": "optional",
60
66
  // T6 (D4) — the frozen register plan; the render falls back to the coverage prose without it.
61
67
  "_driver/register-plan.json": "optional",
68
+ // The band the register returned, read ONLY where the run has no `_records/` archive, to count what was
69
+ // read and where. Optional: a knockout, a legacy run and a register-less run have none, and an absent
70
+ // band leaves the count as "cannot say" exactly as before.
71
+ "register-named-band.json": "optional",
62
72
  // The instructed scope, read only as the register plan's fallback for the searched-jurisdiction set.
63
73
  "_driver/instructed-scope.json": "optional",
74
+ // The frozen local-language lane decision, and the units the fold wrote. Read to derive how deep that
75
+ // investigation went against what the matter configured — through the engine's own asked-versus-ran
76
+ // reader, not by re-deciding it here. Optional and genuinely so: a plain clearance never runs the
77
+ // component and carries neither file, which is the state that folds to not-in-scope.
78
+ "_driver/jx-lanes.json": "optional",
79
+ "_driver/jx/units.json": "optional",
64
80
  // T7 (E5) — the grounded case-law profiles. A run with no case-law layer legitimately has none.
65
81
  "case-law-findings.md": "optional",
66
82
  // T7 (E6) — Corsearch enforcement telemetry; presentation-only, absent ⇒ no lines.
@@ -104,7 +120,7 @@ export const CALLER_SUPPLIED = {
104
120
  };
105
121
 
106
122
  /**
107
- * Load-time gate, on the assertValidatorCoverage model (mcp-server/lib/coverage.mjs:64-73).
123
+ * Load-time gate, modelled on assertValidatorCoverage() in mcp-server/lib/coverage.mjs (lines 64-73).
108
124
  *
109
125
  * Asserts the table is a well-formed closed partition: every gating is one of the two words, no store
110
126
  * is declared in more than one table, and no declaration is blank. It deliberately does NOT scan