clearotron 0.3.2-beta.7 → 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 (272) hide show
  1. package/.env.example +58 -26
  2. package/CONTRIBUTING.md +8 -8
  3. package/INSTALL.md +148 -81
  4. package/README.md +3 -3
  5. package/SECURITY.md +3 -3
  6. package/bin/brandowner.mjs +3 -3
  7. package/bin/framework-preflight.mjs +1 -1
  8. package/bin/onboard.mjs +637 -216
  9. package/bin/start.mjs +151 -27
  10. package/bin/update.mjs +58 -11
  11. package/build-info.json +2 -2
  12. package/docs/DELIVERY.md +2 -1
  13. package/docs/INTAKE.md +1 -1
  14. package/docs/ONBOARDING.md +1 -1
  15. package/docs/architecture/03-run-lifecycle.md +6 -6
  16. package/docs/architecture/04-configuration-reference.md +32 -14
  17. package/docs/architecture/05-config-governance.md +23 -8
  18. package/docs/architecture/05-customer-profiles.md +2 -2
  19. package/docs/architecture/06-operations-runbook.md +3 -3
  20. package/docs/architecture/08-development-guide.md +6 -6
  21. package/docs/configuration.md +5 -5
  22. package/docs/decisions/0003-credential-model.md +1 -1
  23. package/docs/writing-standard.md +4 -0
  24. package/driver/CHANGELOG.md +124 -0
  25. package/driver/README.md +3 -3
  26. package/driver/band-size.mjs +59 -0
  27. package/driver/binding-layers.mjs +1 -1
  28. package/driver/citation-census.json +3 -3
  29. package/driver/{prelim-variants-record.mjs → clearance-variants-record.mjs} +24 -24
  30. package/driver/common-law-receipts.mjs +2 -2
  31. package/driver/company-bundle.mjs +3 -3
  32. package/driver/compose-read.mjs +8 -14
  33. package/driver/config-inventory.mjs +112 -9
  34. package/driver/consumption-ledger.mjs +2 -2
  35. package/driver/contract-arm2-baseline.json +2 -5
  36. package/driver/contract-dictation-registry.mjs +19 -19
  37. package/driver/contract-e3-backlog.mjs +43 -43
  38. package/driver/contract-e3-baseline.json +14 -14
  39. package/driver/contract-vocabulary.mjs +68 -27
  40. package/driver/deliver-trigger.sh +16 -16
  41. package/driver/demo-container.mjs +3 -3
  42. package/driver/dev-portal.mjs +3 -3
  43. package/driver/disposition-call.mjs +1 -1
  44. package/driver/door-gates.mjs +41 -7
  45. package/driver/doubt-ledger.mjs +2 -2
  46. package/driver/drainer-identity.mjs +34 -8
  47. package/driver/driver.config.mjs +367 -104
  48. package/driver/engine/CONTRACT.md +10 -3
  49. package/driver/engine/README.md +2 -2
  50. package/driver/engine/anthropic-agent.mjs +77 -21
  51. package/driver/engine/auth.mjs +129 -10
  52. package/driver/engine/jx-turn.mjs +7 -6
  53. package/driver/engine/mcp/README.md +1 -1
  54. package/driver/engine/mcp/dispositions-server.mjs +3 -3
  55. package/driver/engine/mcp/gather-config.mjs +9 -9
  56. package/driver/engine/mcp/perplexity-server.mjs +2 -2
  57. package/driver/engine/mcp/recording-server.mjs +18 -5
  58. package/driver/engine/openai-agent.mjs +4 -2
  59. package/driver/engine/probe.mjs +110 -23
  60. package/driver/enqueue-schema.mjs +6 -2
  61. package/driver/findings-model.mjs +6 -3
  62. package/driver/flag-snapshot.mjs +34 -8
  63. package/driver/form-neighbourhood.mjs +54 -7
  64. package/driver/framework.mjs +4 -4
  65. package/driver/gateway.mjs +36 -24
  66. package/driver/jx-lanes.mjs +23 -4
  67. package/driver/jx-units.mjs +7 -4
  68. package/driver/jx.mjs +34 -4
  69. package/driver/knockout-review-record.mjs +56 -4
  70. package/driver/known-conflicts.mjs +1 -1
  71. package/driver/matter-frame-record.mjs +90 -1
  72. package/driver/named-band.mjs +1 -1
  73. package/driver/ordinary-words.mjs +51 -0
  74. package/driver/outbox-backoff.mjs +31 -16
  75. package/driver/package.json +1 -1
  76. package/driver/partial-payload-baseline.json +2 -2
  77. package/driver/phase0.mjs +3 -3
  78. package/driver/pipeline-knockout.mjs +5 -5
  79. package/driver/pipeline.mjs +396 -81
  80. package/driver/placement-form.mjs +77 -1
  81. package/driver/placement-model.mjs +1 -1
  82. package/driver/portal-config-view.mjs +30 -1
  83. package/driver/portal-report.mjs +107 -6
  84. package/driver/portal-service.mjs +80 -14
  85. package/driver/portal-upstream.mjs +1 -1
  86. package/driver/predelivery-lint.mjs +12 -2
  87. package/driver/preserve-merge.mjs +3 -3
  88. package/driver/product-rows.mjs +2 -2
  89. package/driver/products.mjs +1 -1
  90. package/driver/profiles/README.md +3 -3
  91. package/driver/profiles/demo-brand-owner.json +2 -2
  92. package/driver/profiles.mjs +55 -17
  93. package/driver/progress.mjs +18 -8
  94. package/driver/provider-usage.mjs +8 -8
  95. package/driver/publish/index.mjs +154 -8
  96. package/driver/publish/knockout.mjs +39 -5
  97. package/driver/publish/pool-admin.mjs +1 -1
  98. package/driver/publish/publish-inputs.mjs +18 -2
  99. package/driver/publish/render-knockout.mjs +184 -31
  100. package/driver/publish/render.mjs +323 -93
  101. package/driver/publish/report-data.mjs +4 -1
  102. package/driver/publish/report-topbar.mjs +58 -0
  103. package/driver/publish/search-depth.mjs +133 -4
  104. package/driver/publish/templates/report.css +78 -4
  105. package/driver/publish/xlsx.mjs +20 -1
  106. package/driver/queue-order.mjs +2 -2
  107. package/driver/recording-agreement.mjs +1 -1
  108. package/driver/reference-score.mjs +1 -1
  109. package/driver/register-availability.mjs +2 -2
  110. package/driver/register-count.mjs +50 -5
  111. package/driver/register-coverage.mjs +161 -1
  112. package/driver/register-digest-record.mjs +236 -11
  113. package/driver/register-grant-vocabulary.mjs +1 -1
  114. package/driver/register-plan.mjs +189 -2
  115. package/driver/registry-fidelity.mjs +3 -3
  116. package/driver/repair-composers.mjs +1 -1
  117. package/driver/repair-contract.mjs +1 -1
  118. package/driver/replay-archive.mjs +6 -6
  119. package/driver/report-overview-record.mjs +2 -2
  120. package/driver/result-noun-fields.mjs +2 -2
  121. package/driver/run-economics.mjs +41 -10
  122. package/driver/run-requirements.mjs +173 -9
  123. package/driver/runner.mjs +5 -5
  124. package/driver/scope-facts.mjs +20 -5
  125. package/driver/scope-ledger.mjs +5 -5
  126. package/driver/search-policy.mjs +22 -12
  127. package/driver/skills/README.md +15 -15
  128. package/driver/skills/blind-frame/SKILL.md +2 -2
  129. package/driver/skills/case-law-citation/SKILL.md +4 -4
  130. package/driver/skills/case-law-citation/sources/eurlex.md +1 -1
  131. package/driver/skills/{prelim-common-law → clearance-common-law}/SKILL.md +22 -22
  132. package/driver/skills/{prelim-common-law → clearance-common-law}/perplexity-prompts.md +1 -1
  133. package/driver/skills/{prelim-register → clearance-register}/SKILL.md +10 -10
  134. package/driver/skills/{prelim-register → clearance-register}/digest.md +2 -2
  135. package/driver/skills/{prelim-register → clearance-register}/providers/README.md +1 -1
  136. package/driver/skills/{prelim-register → clearance-register}/providers/clarivate.md +37 -35
  137. package/driver/skills/{prelim-register → clearance-register}/providers/corsearch.md +20 -11
  138. package/driver/skills/{prelim-register → clearance-register}/providers/signa.md +5 -5
  139. package/driver/skills/{prelim-register → clearance-register}/register-recipes.md +3 -3
  140. package/driver/skills/{prelim-register → clearance-register}/status-rules.md +2 -2
  141. package/driver/skills/{prelim-register → clearance-register}/stealth-filer-indicators.md +1 -1
  142. package/driver/skills/{prelim-register → clearance-register}/unit.md +2 -2
  143. package/driver/skills/{prelim-search → clearance-search}/SKILL.md +31 -31
  144. package/driver/skills/{prelim-search → clearance-search}/delivery-contract.md +1 -1
  145. package/driver/skills/{prelim-search → clearance-search}/phase2-execution.md +18 -18
  146. package/driver/skills/{prelim-search → clearance-search}/synthesis-rules.md +7 -7
  147. package/driver/skills/{prelim-variants → clearance-variants}/SKILL.md +18 -18
  148. package/driver/skills/{prelim-variants → clearance-variants}/transliteration-scripts.md +5 -5
  149. package/driver/skills/frame-diff/SKILL.md +1 -1
  150. package/driver/skills/knockout-assess/SKILL.md +10 -7
  151. package/driver/skills/matter-frame/SKILL.md +3 -3
  152. package/driver/skills/narrative-refutation/SKILL.md +9 -9
  153. package/driver/skills/placement-inquiry/SKILL.md +5 -5
  154. package/driver/stage-context.mjs +1 -1
  155. package/driver/stages-knockout.mjs +4 -4
  156. package/driver/stages.mjs +65 -61
  157. package/driver/status-snapshot.mjs +2 -2
  158. package/driver/suite-census.json +340 -136
  159. package/driver/surface-exit-verdict.mjs +58 -0
  160. package/driver/systemd/README.md +9 -6
  161. package/driver/systemd/clearotron-worker.service +1 -1
  162. package/driver/terminal-clamp.mjs +109 -1
  163. package/driver/tokens.mjs +169 -3
  164. package/driver/unit-environment.mjs +42 -15
  165. package/driver/unit-inventory.mjs +19 -2
  166. package/driver/usage-ledger.mjs +1 -1
  167. package/driver/variant-manifest-model.mjs +4 -4
  168. package/driver/verify-knockout.mjs +27 -0
  169. package/driver/verify.mjs +94 -6
  170. package/driver/whatif-queue.mjs +1 -1
  171. package/driver/wordlists/en.txt +63906 -0
  172. package/mcp-server/CHANGELOG.md +8 -0
  173. package/mcp-server/README.md +1 -1
  174. package/mcp-server/lib/README.md +1 -1
  175. package/mcp-server/lib/options.mjs +8 -7
  176. package/mcp-server/lib/plan.mjs +18 -2
  177. package/mcp-server/lib/runs.mjs +1 -1
  178. package/mcp-server/lib/usage.mjs +3 -3
  179. package/mcp-server/lib/whatif.mjs +1 -1
  180. package/mcp-server/package.json +1 -1
  181. package/mcp-server/server.mjs +18 -1
  182. package/package.json +12 -11
  183. package/portal-ui/dist/assets/{index-CVOIvdhc.css → index-CtvwLCti.css} +207 -3
  184. package/portal-ui/dist/assets/{index-5UyqAyNM.js → index-EVaSo5-g.js} +1580 -527
  185. package/portal-ui/dist/index.html +2 -2
  186. package/portal-ui/package.json +1 -1
  187. package/providers/README.md +1 -1
  188. package/providers/_shared/enumerate.mjs +6 -6
  189. package/providers/_shared/execute-plan.mjs +3 -3
  190. package/providers/_shared/ledger.mjs +119 -5
  191. package/providers/_shared/provider-text.mjs +2 -2
  192. package/providers/_shared/screen.mjs +2 -2
  193. package/providers/_shared/script-form.mjs +3 -3
  194. package/providers/_shared/territory-codes.mjs +23 -3
  195. package/providers/clarivate/README.md +1 -1
  196. package/providers/clarivate/src/capabilities.js +12 -12
  197. package/providers/clarivate/src/core.js +37 -43
  198. package/providers/corsearch/README.md +1 -1
  199. package/providers/corsearch/src/capabilities.js +5 -5
  200. package/providers/corsearch/src/core.js +3 -3
  201. package/providers/jx/README.md +2 -1
  202. package/providers/jx/src/turn-envelope.mjs +8 -3
  203. package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
  204. package/providers/oauth-mcp-bridge/package.json +1 -1
  205. package/providers/perplexity/src/core.js +1 -1
  206. package/providers/signa/README.md +1 -1
  207. package/providers/signa/src/capabilities.js +42 -49
  208. package/providers/signa/src/core.js +106 -29
  209. package/providers/uspto-local/README.md +1 -1
  210. package/providers/uspto-local/src/sync.js +1 -1
  211. package/scripts/README.md +1 -0
  212. package/scripts/ask-ai-render-check.mjs +127 -1
  213. package/scripts/authority-boundary-probe.mjs +8 -6
  214. package/scripts/backfill-started-at.mjs +2 -2
  215. package/scripts/census-merge-driver.mjs +33 -2
  216. package/scripts/citation-anchor-report.mjs +181 -0
  217. package/scripts/dead-names.mjs +1 -1
  218. package/scripts/deprecate-below.mjs +66 -8
  219. package/scripts/drain-preflight.mjs +1 -1
  220. package/scripts/e2e.mjs +174 -0
  221. package/scripts/env-audit.mjs +39 -6
  222. package/scripts/env-classify.mjs +20 -2
  223. package/scripts/freeze-example-run.mjs +61 -18
  224. package/scripts/generated-files-are-current.mjs +69 -4
  225. package/scripts/live-surface-check.mjs +124 -41
  226. package/scripts/markdown-link-check.mjs +1 -1
  227. package/scripts/merge-shape-check.mjs +242 -0
  228. package/scripts/mint-names-in-force.mjs +5 -5
  229. package/scripts/mint-offered-territories.mjs +72 -0
  230. package/scripts/mint-public-residue.mjs +2 -2
  231. package/scripts/mint-reference-strip-backlog.mjs +2 -2
  232. package/scripts/mint-suite-census.mjs +75 -2
  233. package/scripts/mint-writing-standard-backlog.mjs +2 -2
  234. package/scripts/purge-runs.mjs +7 -7
  235. package/scripts/reconcile-runs.mjs +2 -2
  236. package/scripts/release-approve-parked.mjs +20 -2
  237. package/scripts/release-await-cut.mjs +120 -1
  238. package/scripts/release-note-required.mjs +76 -8
  239. package/scripts/report-header-render-check.mjs +164 -0
  240. package/scripts/settings-render-check.mjs +75 -2
  241. package/scripts/test-full.mjs +96 -3
  242. package/scripts/test-run.mjs +10 -0
  243. package/shared/brand.mjs +27 -0
  244. package/shared/connect-clients.mjs +39 -11
  245. package/shared/deployment-box.mjs +7 -2
  246. package/shared/driver-dir.mjs +1 -1
  247. package/shared/env-aliases.mjs +1 -1
  248. package/shared/identifier-scan.mjs +65 -9
  249. package/shared/identifier-sentinels.mjs +22 -0
  250. package/shared/names-in-force.mjs +4 -2
  251. package/shared/offered-territories.json +738 -0
  252. package/shared/pre-rename-spellings.mjs +53 -0
  253. package/shared/reference-guard-classes.mjs +40 -2
  254. package/shared/stdio-connect.mjs +39 -4
  255. package/shared/tree-commit.mjs +48 -0
  256. /package/driver/skills/{prelim-register → clearance-register}/providers/euipo.md +0 -0
  257. /package/driver/skills/{prelim-register → clearance-register}/providers/free-tier.md +0 -0
  258. /package/driver/skills/{prelim-register → clearance-register}/providers/uspto-local.md +0 -0
  259. /package/driver/skills/{prelim-search → clearance-search}/field-doctrine-pharma.md +0 -0
  260. /package/driver/skills/{prelim-search → clearance-search}/firm-wide-reasoning.md +0 -0
  261. /package/driver/skills/{prelim-search → clearance-search}/report-prose.md +0 -0
  262. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.manifest.json +0 -0
  263. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.md +0 -0
  264. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.manifest.json +0 -0
  265. /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.md +0 -0
  266. /package/driver/skills/{prelim-search → clearance-search}/risk-framework.manifest.json +0 -0
  267. /package/driver/skills/{prelim-search → clearance-search}/risk-framework.md +0 -0
  268. /package/driver/skills/{prelim-search → clearance-search}/template-formatting.md +0 -0
  269. /package/driver/skills/{prelim-search → clearance-search}/templates/email/generic.md +0 -0
  270. /package/driver/skills/{prelim-search → clearance-search}/templates/search-request-form.html +0 -0
  271. /package/driver/skills/{prelim-search → clearance-search}/worked-examples-demo.md +0 -0
  272. /package/driver/skills/{prelim-search → clearance-search}/worked-examples.md +0 -0
@@ -14,7 +14,7 @@
14
14
  //
15
15
  // AND IT PRESENTED AS SEARCHED, which is the part that made it a compliance defect rather than a
16
16
  // coverage one. The disclosure machinery only describes territories the compiler recorded as
17
- // UNREACHABLE. These were recorded as reached, so there was no row to render, and `prelim-search`'s
17
+ // UNREACHABLE. These were recorded as reached, so there was no row to render, and `clearance-search`'s
18
18
  // rule against rendering a deferred jurisdiction as a clean negative had nothing to fire on.
19
19
  //
20
20
  // ── TWO FACTS, KEPT APART ON PURPOSE ────────────────────────────────────────────────────────────────
@@ -1,5 +1,5 @@
1
1
  {
2
- "citations": 545,
3
- "files": 3878,
4
- "checkable": 105
2
+ "citations": 489,
3
+ "files": 3945,
4
+ "checkable": 126
5
5
  }
@@ -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
- // prelim-variants-record.mjs — the recording transport for the variant manifest.
3
+ // clearance-variants-record.mjs — the recording transport for the variant manifest.
4
4
  //
5
5
  // Conversion 3, after blind-frame, skeptic, frame-diff and matter-frame. It is
6
6
  // the first conversion whose ruling includes CLASS 3, and the first that can DELETE a derivation rather
@@ -17,7 +17,7 @@
17
17
  // O3c measured the cost of that hand-formatting directly: 9 Bash calls with 4 WRITES across 15 attempts,
18
18
  // and the shape is `python3 -c` over `variant-manifest.json` — the seat pre-checking its own JSON before
19
19
  // saving it. That is CLASS 3, and the design's ruling is that such a check does not get a compute tool:
20
- // it moves to the ACCEPTANCE BOUNDARY. `acceptPrelimVariants` measures on accept and refuses with the
20
+ // it moves to the ACCEPTANCE BOUNDARY. `acceptClearanceVariants` measures on accept and refuses with the
21
21
  // measured value, so the check runs on every call instead of when a seat remembers to run it.
22
22
  //
23
23
  // ── AND IT DELETES A DERIVATION, WHICH IS THE REAL PRIZE ────────────────────────────────────────────
@@ -45,8 +45,8 @@ export const PROSE_FILE = "variant-manifest.md";
45
45
  const SCHEMA_VERSION = 1;
46
46
 
47
47
  /** Where the call's evidence lives — the driver's own record of what the seat handed it. */
48
- export function prelimVariantsCallPaths(runDir) {
49
- const dir = driverDir(runDir, "prelim-variants-calls");
48
+ export function clearanceVariantsCallPaths(runDir) {
49
+ const dir = driverDir(runDir, "clearance-variants-calls");
50
50
  return { dir, payload: join(dir, "call-001.json"), accepted: join(dir, "accepted.json") };
51
51
  }
52
52
 
@@ -107,7 +107,7 @@ export function renderScopeLedgerTable(rows) {
107
107
  * Rendering from the parsed model is what makes the two artifacts one statement: they cannot disagree
108
108
  * about a variant, because both are projections of one validated object. PURE.
109
109
  */
110
- export function renderPrelimVariants(model, scopeRows) {
110
+ export function renderClearanceVariants(model, scopeRows) {
111
111
  // ── HEADING DEPTH IS LOAD-BEARING, AND THIS IS THE ONE THING IN THIS FILE NOT TO "TIDY" ────────────
112
112
  //
113
113
  // `variantsManifestAudit` (common-law-receipts.mjs) arms a term collector on any heading matching
@@ -151,7 +151,7 @@ export function renderPrelimVariants(model, scopeRows) {
151
151
  ...model.search_floor.map((a) => `- ${a}`), "");
152
152
 
153
153
  out.push(...renderScopeLedgerTable(scopeRows));
154
- out.push("---", "", "Rendered by the driver from the stage's `record_prelim_variants` call. "
154
+ out.push("---", "", "Rendered by the driver from the stage's `record_clearance_variants` call. "
155
155
  + `The structured model in ${MODEL_FILE} is the authority for the terms, and scope-ledger.json for the `
156
156
  + "ledger; this file is their human-readable projection.", "");
157
157
  return out.join("\n");
@@ -178,8 +178,8 @@ const DECLARED = Object.freeze({
178
178
  export const refuseUndeclared = (params) => refuseUndeclaredShared(params, DECLARED, "variantmodel");
179
179
 
180
180
  /** The last ACCEPTED call for this run, or null. */
181
- export function lastAcceptedPrelimVariants(runDir) {
182
- return lastAccepted(prelimVariantsCallPaths(String(runDir ?? "")).accepted, readFileSync);
181
+ export function lastAcceptedClearanceVariants(runDir) {
182
+ return lastAccepted(clearanceVariantsCallPaths(String(runDir ?? "")).accepted, readFileSync);
183
183
  }
184
184
 
185
185
  /**
@@ -190,7 +190,7 @@ export function lastAcceptedPrelimVariants(runDir) {
190
190
  * Preserving rather than requiring, because nothing here can tell a first call from a repair and a
191
191
  * product refusal is never a pass. See preserve-merge.mjs for the class.
192
192
  */
193
- export function mergePrelimVariantsCall(stored, received) {
193
+ export function mergeClearanceVariantsCall(stored, received) {
194
194
  const base = stored ?? {};
195
195
  return {
196
196
  mark: received?.mark,
@@ -207,7 +207,7 @@ export function mergePrelimVariantsCall(stored, received) {
207
207
  };
208
208
  }
209
209
 
210
- export function acceptPrelimVariants(params) {
210
+ export function acceptClearanceVariants(params) {
211
211
  const model = {
212
212
  schema_version: SCHEMA_VERSION,
213
213
  mark: params?.mark,
@@ -250,7 +250,7 @@ export function acceptPrelimVariants(params) {
250
250
  scopeRows.push({ layer, item, status, reason, reopen_trigger });
251
251
  }
252
252
 
253
- return { ok: true, model: parsed, scopeRows, content: renderPrelimVariants(parsed, scopeRows) };
253
+ return { ok: true, model: parsed, scopeRows, content: renderClearanceVariants(parsed, scopeRows) };
254
254
  }
255
255
 
256
256
  /**
@@ -263,8 +263,8 @@ export function acceptPrelimVariants(params) {
263
263
  * the first two, so a crash between writes leaves the run with its load-bearing files and a missing
264
264
  * restatement rather than a restatement of something never stored.
265
265
  */
266
- export function recordPrelimVariants(runDir, received, { now = () => new Date().toISOString() } = {}) {
267
- const { dir, payload } = prelimVariantsCallPaths(String(runDir ?? ""));
266
+ export function recordClearanceVariants(runDir, received, { now = () => new Date().toISOString() } = {}) {
267
+ const { dir, payload } = clearanceVariantsCallPaths(String(runDir ?? ""));
268
268
  // — ONE FILE PER CALL, refusals included. This wrote a single fixed `call-001.json`,
269
269
  // so a turn refused and then re-sent kept only the survivor: the file whose header promises "including
270
270
  // calls that were refused" held the one call that was not. Sequence 1 still resolves to `call-001.json`,
@@ -288,8 +288,8 @@ export function recordPrelimVariants(runDir, received, { now = () => new Date().
288
288
  if (undeclared) {
289
289
  return { written: null, refused: undeclared, captured: closeCapture({ ok: false, refused: undeclared }), capture_failed: captureFailed };
290
290
  }
291
- const call = mergePrelimVariantsCall(lastAcceptedPrelimVariants(runDir), received);
292
- const verdict = acceptPrelimVariants(call);
291
+ const call = mergeClearanceVariantsCall(lastAcceptedClearanceVariants(runDir), received);
292
+ const verdict = acceptClearanceVariants(call);
293
293
  if (!verdict.ok) {
294
294
  return { written: null, refused: verdict.reason, captured: closeCapture({ ok: false, refused: verdict.reason }), capture_failed: captureFailed };
295
295
  }
@@ -299,7 +299,7 @@ export function recordPrelimVariants(runDir, received, { now = () => new Date().
299
299
  try {
300
300
  writeFileSync(at, JSON.stringify(verdict.model, null, 2) + "\n");
301
301
  // Step 5. BEST-EFFORT, in its own try — see report-overview-record.mjs for why.
302
- try { writeFileSync(prelimVariantsCallPaths(String(runDir ?? "")).accepted, acceptedEnvelope(call, now())); }
302
+ try { writeFileSync(clearanceVariantsCallPaths(String(runDir ?? "")).accepted, acceptedEnvelope(call, now())); }
303
303
  catch { /* a lost base is never a lost artifact */ }
304
304
  writeFileSync(proseAt, verdict.content);
305
305
  } catch (e) {
@@ -320,7 +320,7 @@ export function recordPrelimVariants(runDir, received, { now = () => new Date().
320
320
 
321
321
  /** The typed scope-ledger rows, for the driver's own serialisation. PURE — no re-parse of the table. */
322
322
  export function recordedScopeRows(received) {
323
- const v = acceptPrelimVariants(received);
323
+ const v = acceptClearanceVariants(received);
324
324
  return v.ok ? v.scopeRows : [];
325
325
  }
326
326
 
@@ -332,7 +332,7 @@ export function recordedScopeRows(received) {
332
332
  * and it carried no ledger rows" — which is a manifest with an empty ledger, not a manifest to re-parse.
333
333
  * Collapsing the two would send the driver back to the table on a run that had already answered.
334
334
  *
335
- * The rows are re-validated through `acceptPrelimVariants` rather than trusted raw out of the capture:
335
+ * The rows are re-validated through `acceptClearanceVariants` rather than trusted raw out of the capture:
336
336
  * the capture records what ARRIVED, untidied, including a refused call's params. IMPURE (reads the run).
337
337
  */
338
338
  export function recordedScopeLedgerRows(runDir) {
@@ -344,22 +344,22 @@ export function recordedScopeLedgerRows(runDir) {
344
344
  // partial does not validate on its own, so this would have returned null — "no typed call, go and
345
345
  // parse the prose" — on a run whose merged call had validated and written a manifest. The accepted
346
346
  // base IS the merged, validated call, which is exactly what this wants.
347
- const merged = lastAcceptedPrelimVariants(dir0);
347
+ const merged = lastAcceptedClearanceVariants(dir0);
348
348
  if (merged) {
349
- const v = acceptPrelimVariants(merged);
349
+ const v = acceptClearanceVariants(merged);
350
350
  if (v.ok) return v.scopeRows;
351
351
  }
352
352
  // FALLBACK: runs that predate the base, which have a capture and no accepted.json. Unchanged
353
353
  // behaviour for them, and it is why this is a fallback rather than a replacement.
354
354
  let captured;
355
- try { captured = JSON.parse(readFileSync(prelimVariantsCallPaths(dir0).payload, "utf8")); }
355
+ try { captured = JSON.parse(readFileSync(clearanceVariantsCallPaths(dir0).payload, "utf8")); }
356
356
  catch { return null; } // no call — the prose path is the answer
357
- const v = acceptPrelimVariants(captured?.params);
357
+ const v = acceptClearanceVariants(captured?.params);
358
358
  return v.ok ? v.scopeRows : null; // a refused call never wrote a manifest either
359
359
  }
360
360
 
361
361
  /** Was this run's manifest written through the typed transport? The ruled discriminator. */
362
- export function prelimVariantsWasRecorded(runDir) {
363
- try { readFileSync(prelimVariantsCallPaths(String(runDir ?? "")).payload, "utf8"); return true; }
362
+ export function clearanceVariantsWasRecorded(runDir) {
363
+ try { readFileSync(clearanceVariantsCallPaths(String(runDir ?? "")).payload, "utf8"); return true; }
364
364
  catch { return false; }
365
365
  }
@@ -46,7 +46,7 @@ function rowCells(ln) {
46
46
  * The variant terms the manifest commits the grid to search: data rows of sections whose heading
47
47
  * contains "variants" (the Variants tables). "Variant applicability" sections (category
48
48
  * dispositions, not search terms) are excluded. The term column is resolved from each table's
49
- * HEADER row — the live prelim-variants format is `| Category | Value | Rationale | Verify? |`
49
+ * HEADER row — the live clearance-variants format is `| Category | Value | Rationale | Verify? |`
50
50
  * (term in "Value"), while older/test manifests use `| Variant | ... |` (term first); default 0
51
51
  * when no recognizable header exists.
52
52
  */
@@ -842,7 +842,7 @@ export function mergeCommonLawFindings(parts) {
842
842
  // FALSE against the real ledger, and the arm failed the very run it was derived from:
843
843
  //
844
844
  // • `candidates[]` is RAW, UNJUDGED web-search output — the grid program records up to 8 candidates
845
- // per cell "without judging similarity" (skills/prelim-common-law/perplexity-prompts.md), and the
845
+ // per cell "without judging similarity" (skills/clearance-common-law/perplexity-prompts.md), and the
846
846
  // filtering is explicitly the model's. The real cell `CORAL FREEZE × vitaminshoppe.com` holds three:
847
847
  // a BCAA how-to video, a Hawaiian shaved-ice supplement and a Dallas store-locator page. The
848
848
  // model's "No results" means "no relevant listing", and no deterministic test can tell that page
@@ -44,7 +44,7 @@ import { Refusal } from "../shared/onboarding-store.mjs";
44
44
  * Broken means someone TRIED to select one and it does not resolve, and falling back there would rate a
45
45
  * client's matters under a framework nobody chose while the output said everything was fine.
46
46
  *
47
- * The profile validator checks the SHAPE of this string only (`skills/prelim-search/<file>.md`, no
47
+ * The profile validator checks the SHAPE of this string only (`skills/clearance-search/<file>.md`, no
48
48
  * escape) and never whether the file is there — so a shape-valid path to a document that does not exist
49
49
  * validates cleanly and fails at rating time, which is the wrong place to find out.
50
50
  */
@@ -58,9 +58,9 @@ export function resolveFramework(requested, {
58
58
  const path = String(requested).trim();
59
59
  // Shape first, and by the profile validator's own rule rather than a second copy of it: a path that
60
60
  // would be refused at load must be refused here, in the same words, before anything is written.
61
- if (!/^skills\/prelim-search\/[^/]+\.md$/.test(path) || path.includes(".."))
61
+ if (!/^skills\/(?:clearance|prelim)-search\/[^/]+\.md$/.test(path) || path.includes("..")) // either spelling of the folder, as the profile loader
62
62
  throw new Refusal(
63
- `--framework must name a document of the form "skills/prelim-search/<file>.md" (got ${JSON.stringify(path)}). `
63
+ `--framework must name a document of the form "skills/clearance-search/<file>.md" (got ${JSON.stringify(path)}). `
64
64
  + `A profile selects a SHIPPED framework, never an arbitrary path.`);
65
65
 
66
66
  // RESOLVED THE WAY THE RATING STAGE RESOLVES IT, never by joining the repo root. `skills/...` paths
@@ -55,6 +55,7 @@ const MAX_CLASSES = 15;
55
55
  const MAX_TERRITORIES = 20;
56
56
  const MAX_NOTES = 8;
57
57
  import { PRODUCTS, PRODUCT_IDS } from "./products.mjs";
58
+ import OFFERED from "../shared/offered-territories.json" with { type: "json" }; // the form's own list, minted by rule
58
59
 
59
60
  const MAX_STR = 600;
60
61
  const MAX_NOTE = 240;
@@ -62,22 +63,15 @@ const MAX_NOTE = 240;
62
63
  /**
63
64
  * The territory vocabulary, as offered by the composer's Where field.
64
65
  *
65
- * MIRRORS `REGIONS` + `COUNTRIES` in portal-ui/src/contract/composerProduct.ts. Kept here only so the
66
- * prompt can name it; the authority on what is selectable is the UI's own list.
66
+ * READ FROM THE SAME FILE THE FORM READS: shared/offered-territories.json, minted by rule from what the
67
+ * registers can search (scripts/mint-offered-territories.mjs). It used to be a hand-kept copy of the form's
68
+ * own list, "mirrored" by a comment, and a comment keeps nothing equal. One file read twice cannot differ.
67
69
  *
68
- * "Worldwide" LEFT THIS LIST. It is not a territory the composer offers and never was — it is a MODE,
69
- * and the read now carries it as its own boolean. Leaving it here taught the model to write a token the
70
- * form had to strip back out, which is how "everywhere" and "nowhere in particular" became the same
71
- * bytes on the wire in the first place.
70
+ * "Worldwide" is not in it. It is not a territory the composer offers and never was — it is a MODE,
71
+ * and the read carries it as its own boolean. Listing it taught the model to write a token the form had to
72
+ * strip back out, which is how "everywhere" and "nowhere in particular" became the same bytes on the wire.
72
73
  */
73
- export const PROMPT_TERRITORIES = Object.freeze([
74
- "European Union", "Benelux", "African Regional (ARIPO)",
75
- "United States", "United Kingdom", "Ireland", "France", "Germany", "Spain", "Italy", "Netherlands",
76
- "Switzerland", "Austria", "Sweden", "Norway", "Poland", "Bulgaria", "Greece", "Turkey", "Canada",
77
- "Mexico", "Brazil", "Argentina", "China", "Hong Kong", "Taiwan", "Macau", "Japan", "South Korea",
78
- "Singapore", "India", "Thailand", "Australia", "New Zealand", "United Arab Emirates", "Saudi Arabia",
79
- "South Africa",
80
- ]);
74
+ export const PROMPT_TERRITORIES = Object.freeze([...OFFERED.regions, ...OFFERED.countries].map((t) => t.name));
81
75
 
82
76
  /**
83
77
  * The shape the model must answer in.
@@ -42,7 +42,10 @@ import {
42
42
  DEFAULT_ENGINE_ID, ENGINE_BINARIES, PROVIDERS, RESEARCH_PROVIDERS, SERP_PROVIDERS,
43
43
  missingCredentials, preflightEngineBinary, providerIdFrom,
44
44
  } from "./driver.config.mjs";
45
- import { resolveAuthMode } from "./engine/auth.mjs";
45
+ import {
46
+ resolveAuthMode, billingMode, cloudsSwitchedOn, BILLING_MODES, CLOUD_SWITCH, CLOUD_CREDENTIAL_CHECK,
47
+ } from "./engine/auth.mjs";
48
+ import { billingRefusalWords } from "./run-requirements.mjs"; // — a refusal that quotes the billing word says it by name
46
49
  import { CASELAW_BRIDGES } from "./engine/mcp/gather-config.mjs"; // — the list that decides what is spawned
47
50
  import { existsSync, readFileSync, statSync } from "node:fs";
48
51
  import { join } from "node:path";
@@ -60,7 +63,9 @@ const shown = (names) => names.map((n) => n);
60
63
  * absent, because the alternative is silently billing a subscription the operator thought they had
61
64
  * stopped using (engine/auth.mjs's opening argument). A writer that caught that and recorded "unknown"
62
65
  * would erase precisely the misconfiguration a staff config page exists to surface, so it is caught and
63
- * recorded AS ITSELF: mode `api-key`, `apiBilled: false`, and a fault naming the variable to set.
66
+ * recorded AS ITSELF: mode `api-key`, `apiBilled: false`, and a fault naming the variable to set. The
67
+ * cloud mode's refusals, and a word that is not a mode at all, are recorded as themselves too, by the
68
+ * refusal's own sentence.
64
69
  *
65
70
  * `apiBilled` is what the page should believe over `mode` — the two come apart in exactly this case,
66
71
  * and only one of them describes who gets the invoice.
@@ -77,14 +82,38 @@ export function engineInventory(env = process.env) {
77
82
  const billing = (() => {
78
83
  try {
79
84
  const a = resolveAuthMode({ engineName: id, env });
80
- return { mode: a.mode, apiBilled: a.apiBilled === true, missing: [] };
81
- } catch {
82
- // The one state resolveAuthMode throws for. The message carries no value, but it is not copied
83
- // either — the two variable names are reconstructed from the table so this cannot drift from it.
85
+ // `cloud` is the resolver's own answer, never re-derived here: the account a run bills is whatever
86
+ // the run door resolves, and a page that worked it out a second way could name another.
87
+ const cloud = a.cloud ?? null;
88
+ return { mode: a.mode, apiBilled: a.apiBilled === true, missing: [], cloud, cloudName: cloudName(cloud) };
89
+ } catch (e) {
90
+ // Recorded AS ITSELF, whichever refusal it was. An API-key mode with no key names the variable to
91
+ // set, reconstructed from the table so it cannot drift from it. Every other refusal (a cloud mode with
92
+ // no cloud switched on or with two, a cloud switch beside a mode it contradicts, a word that is not a
93
+ // mode) leaves `missing` empty, because the page reads `missing` as "set this key" and a key that is
94
+ // set is not missing, and carries a `reason` the page words itself (billingRefusalReason, below).
95
+ const mode = billingMode(env);
96
+ const keyAbsent = Boolean(spec?.apiKeyEnv) && String(env[spec.apiKeyEnv] ?? "") === "";
97
+ if (mode === "api-key" && keyAbsent) {
98
+ return { mode, apiBilled: false, missing: shown([spec.apiKeyEnv]), cloud: null, cloudName: null };
99
+ }
100
+ const reason = billingRefusalReason(id, env);
101
+ const notAMode = reason.kind === "not-a-mode";
84
102
  return {
85
- mode: "api-key",
86
- apiBilled: false,
87
- missing: spec?.apiKeyEnv ? shown([spec.apiKeyEnv]) : [],
103
+ // A WORD THAT IS NOT A MODE IS NOT RECORDED. It is whatever was typed into the setting that decides
104
+ // who pays, and a key pasted there by mistake is that word; this inventory is written to a file and
105
+ // served to a browser. So the mode reads `unknown`, and the refusal says the setting by name.
106
+ mode: notAMode ? "unknown" : mode,
107
+ apiBilled: false, missing: [], cloud: null, cloudName: null,
108
+ // THAT REFUSAL IS WRITTEN HERE, NOT CLEANED FROM THE RESOLVER'S. The resolver's sentence quotes the
109
+ // typed word, and removing it by pattern depends on how the word is spelled: a word that itself
110
+ // contains " is not a billing mode" left its tail behind. Built from names alone, nothing typed can
111
+ // reach it. Every other refusal quotes only setting names and the three mode words.
112
+ refusal: notAMode
113
+ ? `${reason.setting} is set to a word that is not a billing mode — refusing to guess, because the guess `
114
+ + `would bill the subscription. One of: ${reason.modes.join(", ")}.`
115
+ : billingRefusalWords(String(e?.message ?? e)),
116
+ reason,
88
117
  };
89
118
  }
90
119
  })();
@@ -104,6 +133,80 @@ export function engineInventory(env = process.env) {
104
133
  return { id, vendor: spec?.vendor ?? null, known: Boolean(spec), billing, binaryPresent };
105
134
  }
106
135
 
136
+ /**
137
+ * What a reader calls the account a cloud mode bills: "Google Cloud", "Microsoft Azure", "Amazon Bedrock"
138
+ * or "Gateway". Null for no cloud.
139
+ *
140
+ * ONE TABLE OF CLOUD NAMES, NOT TWO. `CLOUD_CREDENTIAL_CHECK` in engine/auth.mjs already names each cloud
141
+ * the way a reader knows it, for the advice a refused sign-in prints, and this reads it rather than keeping
142
+ * a second list that could name a cloud differently on the page. It writes the gateway "the gateway", for
143
+ * use inside a sentence; a label on a page drops the article and starts with a capital, and that one
144
+ * adjustment is the whole of what this adds.
145
+ */
146
+ export function cloudName(cloud) {
147
+ const who = CLOUD_CREDENTIAL_CHECK[cloud]?.who;
148
+ if (!who) return null;
149
+ const bare = who.replace(/^the /, "");
150
+ return bare.charAt(0).toUpperCase() + bare.slice(1);
151
+ }
152
+
153
+ /**
154
+ * WHY A BILLING SETTING REFUSES EVERY SEARCH, as data: which refusal it is and the names involved, so the
155
+ * config page words it once, in plain English, without reading the resolver's sentence apart.
156
+ *
157
+ * `kind` is one of:
158
+ * switch-beside-mode subscription or api-key, and a cloud's switch on beside it (`clouds`)
159
+ * two-clouds cloud, and more than one cloud switched on (`clouds`)
160
+ * no-cloud cloud, and no cloud switched on and no gateway address (`clouds` lists every
161
+ * switch, and `gateway` the gateway's setting, as the choices to make)
162
+ * not-a-mode a word that is not a billing mode (`modes` are the ones this engine takes)
163
+ * cloud-on-codex cloud, on the Codex engine, which a cloud account cannot pay for (`engineSetting`
164
+ * and `engineChoice` name the Claude engine only where it would pay: one cloud
165
+ * switched on, or none and a gateway address; otherwise that move is one more refusal)
166
+ * unclassified a refusal this list does not name; the page says only that searches are refused.
167
+ * Not "other": that is an assistant's id in connect-clients.mjs, and a surface may
168
+ * not name one in code
169
+ *
170
+ * `defaulted` says the billing setting is blank, so `subscription` is the default and not a word anybody
171
+ * wrote: a page that said "payment is set to subscription" would send a reader looking for a line their
172
+ * settings file does not have. It is the state measured on Foundry, a switch on and the word unset.
173
+ *
174
+ * THE SAME PREDICATES AS THE RESOLVER, IN ITS ORDER. `resolveAuthMode` refuses by throwing a sentence and
175
+ * nothing else, so the kind is read back from the same environment through the same functions it uses
176
+ * (`billingMode`, `cloudsSwitchedOn`), in the order it checks them. Called only once it has thrown.
177
+ *
178
+ * NAMES, NEVER A VALUE. `mode` is sent only when it is one of the three modes; the word in a `not-a-mode`
179
+ * refusal is whatever was typed, and it is not sent at all.
180
+ */
181
+ export function billingRefusalReason(id, env = process.env) {
182
+ const mode = billingMode(env);
183
+ const setting = "CLEAROTRON_AI_BILLING";
184
+ const clouds = (ids) => ids.map((c) => ({ name: cloudName(c), setting: CLOUD_SWITCH[c] }));
185
+ const defaulted = String(env[setting] ?? "").trim() === "";
186
+ const base = { setting, mode: null, defaulted, clouds: [], modes: [], gateway: null, engineSetting: null, engineChoice: null };
187
+ if (id === "openai-agent") {
188
+ if (mode === "cloud") {
189
+ // The resolver's own test for a cloud the Claude engine would pay through, in its words: one switch,
190
+ // or no switch and a gateway address.
191
+ const on = cloudsSwitchedOn(env);
192
+ const claudeWouldPay = on.length === 1 || (!on.length && Boolean(env.ANTHROPIC_BASE_URL));
193
+ return { ...base, kind: "cloud-on-codex", mode, modes: ["subscription", "api-key"],
194
+ ...(claudeWouldPay ? { engineSetting: "CLEAROTRON_AI", engineChoice: "anthropic-agent" } : {}) };
195
+ }
196
+ if (mode !== "subscription" && mode !== "api-key") return { ...base, kind: "not-a-mode", modes: ["subscription", "api-key"] };
197
+ }
198
+ if (id === "anthropic-agent") {
199
+ const on = cloudsSwitchedOn(env);
200
+ if ((mode === "subscription" || mode === "api-key") && on.length) return { ...base, kind: "switch-beside-mode", mode, clouds: clouds(on) };
201
+ if (mode === "cloud" && on.length > 1) return { ...base, kind: "two-clouds", mode, clouds: clouds(on) };
202
+ if (mode === "cloud" && !on.length) {
203
+ return { ...base, kind: "no-cloud", mode, clouds: clouds(Object.keys(CLOUD_SWITCH)), gateway: "ANTHROPIC_BASE_URL" };
204
+ }
205
+ if (!BILLING_MODES.includes(mode)) return { ...base, kind: "not-a-mode", modes: [...BILLING_MODES] };
206
+ }
207
+ return { ...base, kind: "unclassified", mode: BILLING_MODES.includes(mode) ? mode : null };
208
+ }
209
+
107
210
  /**
108
211
  * The three states an install can be in, as far as running a search goes..
109
212
  *
@@ -25,7 +25,7 @@
25
25
  // follows, so an MCP tool call can read consumption without dragging the pipeline in.
26
26
 
27
27
  import { appendFileSync, readFileSync, readdirSync } from "node:fs";
28
- import { join } from "node:path";
28
+ import { join } from "node:path"; import { studioDirFor } from "../shared/pre-rename-spellings.mjs";
29
29
 
30
30
  /** Where the consumption ledger sits: beside the matter ledger, in the studio dir. */
31
31
  export function consumptionLedgerPath(studioRoot) {
@@ -158,7 +158,7 @@ export function accountConsumption({ workspaceRoot, account, now = Date.now() })
158
158
 
159
159
  const rows = [];
160
160
  for (const ws of workspaces) {
161
- const studio = join(workspaceRoot, ws, "studio", "prelim-search");
161
+ const studio = studioDirFor(join(workspaceRoot, ws));
162
162
  try {
163
163
  for (const line of readFileSync(consumptionLedgerPath(studio), "utf8").split("\n")) {
164
164
  if (!line.trim()) continue;
@@ -11,7 +11,7 @@
11
11
  "the baseline tracks named elements, and a rename is a new name. Regenerate rather than work around it.",
12
12
  "Measured 139 of 287 declared elements."
13
13
  ],
14
- "total": 107,
14
+ "total": 104,
15
15
  "byStage": {
16
16
  "matter-frame": [
17
17
  "### Intake asks line shape — rendered by the driver from intake_asks[]",
@@ -24,7 +24,7 @@
24
24
  "Scope reasoning — search-wide/cite-narrow, the in-scope-by-reach routes, and the reopen trigger behind each exclusion",
25
25
  "search_channels — the domains where real use of THIS matter's goods would show"
26
26
  ],
27
- "prelim-variants": [
27
+ "clearance-variants": [
28
28
  "### Scope ledger rows — {Layer, Item, Status: applied|dropped, Reason, Reopen trigger} across variant / field / source / jurisdiction",
29
29
  "Archetype — primary (one of six) + modifiers + one-line reasoning",
30
30
  "Cross-mark themes — shared elements, shared conceptual themes, shared industry-incumbent alerts (multi-mark requests)",
@@ -84,7 +84,6 @@
84
84
  "the dictated entry list — qid, predicate, terms, owner, nice_classes, regions, when-guard, expected_kind, covered_by"
85
85
  ],
86
86
  "placement-inquiry": [
87
- "mark / owner / records / territories / classes on a SELECTED row",
88
87
  "retract: <row_id> — withdrawing a seat row already on the form",
89
88
  "return payload — the absolute output path plus a 2-3 line summary",
90
89
  "select — one record URI of the register candidate being placed",
@@ -94,8 +93,6 @@
94
93
  "register-digest": [
95
94
  "## Summary counts — total queries executed (search + detail-fetch), enumerated records across N axes, crowd-descriptor count, candidates past the gate, surfaced count, open-verification-flag count",
96
95
  "Audit trail — per-unit search/detail-fetch counts, per-jurisdiction `_query` attribution",
97
- "INSTRUCTED CHECKS — the answer to each requester ask the register owns",
98
- "adopt-or-override each placement by engaging its reason, and the `### Disagreement resolutions` rows (one per surfaced disagreement and per borderline:true, each ADOPTED/OVERRODE in writing)",
99
96
  "return payload — the absolute output path plus a 2-3 line summary (counts + the path of the file written)",
100
97
  "rolled-up coverage judgment — `sufficient: <true|false>`",
101
98
  "source attribution — the register name tagged on each record, plus the EUIPO `environment` word",
@@ -111,10 +111,10 @@ export function grantedToolsFor(stage) {
111
111
  //
112
112
  // SIX OF THE TEN ARE ONE SUB-CLASS NOBODY HAD NAMED: SHARED DOCTRINE, DIVERGENT GRANTS. A skill file is
113
113
  // served whole to several stages whose grants differ, so the same sentence is a correct instruction to
114
- // one seat and an order for a tool the next seat does not hold. `prelim-register/SKILL.md` goes to
115
- // register-unit and register-digest; `prelim-search/phase2-execution.md` goes to skeptic;
116
- // `prelim-search/synthesis-rules.md` goes to synthesis AND report-overview. report-overview and
117
- // prelim-variants are tool-free BY DESIGN, each with its reason written out in TOOL_FREE_STAGES;
114
+ // one seat and an order for a tool the next seat does not hold. `clearance-register/SKILL.md` goes to
115
+ // register-unit and register-digest; `clearance-search/phase2-execution.md` goes to skeptic;
116
+ // `clearance-search/synthesis-rules.md` goes to synthesis AND report-overview. report-overview and
117
+ // clearance-variants are tool-free BY DESIGN, each with its reason written out in TOOL_FREE_STAGES;
118
118
  // skeptic has since CONVERTED to the RECORDING category (its reason moved to RECORDING_STAGES) and
119
119
  // holds exactly its own record tool — still no retrieval tool, so its entries below reproduce
120
120
  // unchanged. Each of the three is currently served a document telling it to run a lookup.
@@ -124,31 +124,31 @@ export function grantedToolsFor(stage) {
124
124
  // only shrink. The test asserts every entry still reproduces, so a pair fixed in fails CI here
125
125
  // until its line is deleted.
126
126
  export const TOOL_ORDER_BACKLOG = [
127
- { stage: "register-digest", tool: "register_enumerate", site: "driver/skills/prelim-register/SKILL.md",
127
+ { stage: "register-digest", tool: "register_enumerate", site: "driver/skills/clearance-register/SKILL.md",
128
128
  closes: "#865 — shared doctrine: SKILL.md is served to register-unit (holds register) and register-digest "
129
129
  + "(holds band only, since the plan freeze retired live search from the judgment seat)." },
130
- { stage: "register-digest", tool: "register_execute_plan", site: "driver/skills/prelim-register/SKILL.md",
130
+ { stage: "register-digest", tool: "register_execute_plan", site: "driver/skills/clearance-register/SKILL.md",
131
131
  closes: "#865 — same shared file, same split grant." },
132
- { stage: "register-digest", tool: "register_batch_screen", site: "driver/skills/prelim-register/digest.md",
132
+ { stage: "register-digest", tool: "register_batch_screen", site: "driver/skills/clearance-register/digest.md",
133
133
  closes: "#865 — the digest's OWN doctrine, not a shared file: this one is the stage being told to "
134
134
  + "batch-screen with a tool its grant does not carry. gather-config.mjs:196 already records that "
135
135
  + "this stage's prompt once ordered live register checks; the order outlived the note." },
136
- { stage: "register-unit", tool: "band_record", site: "driver/skills/prelim-register/SKILL.md",
136
+ { stage: "register-unit", tool: "band_record", site: "driver/skills/clearance-register/SKILL.md",
137
137
  closes: "#865 — same shared file, same split grant." },
138
- { stage: "register-unit", tool: "band_shape", site: "driver/skills/prelim-register/SKILL.md",
138
+ { stage: "register-unit", tool: "band_shape", site: "driver/skills/clearance-register/SKILL.md",
139
139
  closes: "#865 — same shared file, same split grant." },
140
- { stage: "report-overview", tool: "band_record", site: "driver/skills/prelim-search/synthesis-rules.md",
140
+ { stage: "report-overview", tool: "band_record", site: "driver/skills/clearance-search/synthesis-rules.md",
141
141
  closes: "#865 — same shared file, same split grant." },
142
- { stage: "register-unit", tool: "band_lookup", site: "driver/skills/prelim-register/SKILL.md",
142
+ { stage: "register-unit", tool: "band_lookup", site: "driver/skills/clearance-register/SKILL.md",
143
143
  closes: "#865 — the mirror image: the band tools are the judgment stages', and the unit lane holds "
144
144
  + "register instead." },
145
- { stage: "register-unit", tool: "record_coverage", site: "driver/skills/prelim-register/SKILL.md",
145
+ { stage: "register-unit", tool: "record_coverage", site: "driver/skills/clearance-register/SKILL.md",
146
146
  closes: "#865 — same shared-spine split as the band tools above, minted by the typed coverage "
147
147
  + "transport: the spine's Coverage-ledger section (mode-scoped to DIGEST in its own words) orders "
148
148
  + "`record_coverage`, which only register-digest's grant carries — the unit lane must never hold a "
149
149
  + "writer into judgment's coverage record. Resolved the day the spine's coverage doctrine moves "
150
150
  + "wholly into digest.md, or #865 scopes shared-spine text per seat." },
151
- { stage: "register-unit", tool: "record_register_digest", site: "driver/skills/prelim-register/SKILL.md",
151
+ { stage: "register-unit", tool: "record_register_digest", site: "driver/skills/clearance-register/SKILL.md",
152
152
  closes: "#865 — the SAME shared-spine split as record_coverage above, minted by conversion 11's typed "
153
153
  + "findings transport. The spine's mode list names the tool inside its **Digest mode (judgment — "
154
154
  + "Layer B)** bullet, which is the sentence that already names record_coverage and the band tools; "
@@ -157,20 +157,20 @@ export const TOOL_ORDER_BACKLOG = [
157
157
  + "spine's digest doctrine moves wholly into digest.md, or #865 scopes shared-spine text per seat — "
158
158
  + "and NOT by granting it: the funnel must never hold a writer into judgment's findings document, "
159
159
  + "which is the same rule that keeps record_coverage off its grant." },
160
- { stage: "skeptic", tool: "perplexity_research", site: "driver/skills/prelim-search/phase2-execution.md",
160
+ { stage: "skeptic", tool: "perplexity_research", site: "driver/skills/clearance-search/phase2-execution.md",
161
161
  closes: "#865 — skeptic holds NO retrieval tool ('new search work enters via the escalation lane, never "
162
162
  + "from this seat' — it converted to the RECORDING category, gaining record_skeptic plus the "
163
163
  + "read-only, run-dir-scoped search_run_artifacts, and no retrieval tool) "
164
164
  + "and is served a document ordering a live query." },
165
- { stage: "skeptic", tool: "register_propose_supplemental", site: "driver/skills/prelim-search/phase2-execution.md",
165
+ { stage: "skeptic", tool: "register_propose_supplemental", site: "driver/skills/clearance-search/phase2-execution.md",
166
166
  closes: "#865 — same seat, same file, the supplemental mint it cannot call." },
167
- { stage: "report-overview", tool: "perplexity_research", site: "driver/skills/prelim-search/synthesis-rules.md",
167
+ { stage: "report-overview", tool: "perplexity_research", site: "driver/skills/clearance-search/synthesis-rules.md",
168
168
  closes: "#865 — synthesis-rules.md is served to synthesis (holds perplexity + band) and to "
169
169
  + "report-overview (TOOL-FREE: 'renders the shell from the settled narrative + findings')." },
170
- { stage: "report-overview", tool: "band_lookup", site: "driver/skills/prelim-search/synthesis-rules.md",
170
+ { stage: "report-overview", tool: "band_lookup", site: "driver/skills/clearance-search/synthesis-rules.md",
171
171
  closes: "#865 — same shared file, same split grant." },
172
- { stage: "prelim-variants", tool: "perplexity_research", site: "driver/skills/prelim-variants/SKILL.md",
173
- closes: "#865 — prelim-variants is TOOL-FREE by design ('pure reasoning over material already on "
172
+ { stage: "clearance-variants", tool: "perplexity_research", site: "driver/skills/clearance-variants/SKILL.md",
173
+ closes: "#865 — clearance-variants is TOOL-FREE by design ('pure reasoning over material already on "
174
174
  + "disk') and its own doctrine names a research call." },
175
175
  ];
176
176