clearotron 0.2.2 → 0.2.4

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 (161) hide show
  1. package/.env.example +64 -37
  2. package/.nvmrc +1 -1
  3. package/CONTRIBUTING.md +8 -4
  4. package/INSTALL.md +36 -14
  5. package/README.md +18 -22
  6. package/THIRD-PARTY-NOTICES.md +1 -1
  7. package/bin/clearotron.mjs +22 -0
  8. package/bin/example.mjs +10 -5
  9. package/bin/onboard.mjs +370 -28
  10. package/bin/start.mjs +220 -12
  11. package/build-info.json +2 -2
  12. package/demo/README.md +1 -1
  13. package/demo/full-country-search/run/_driver/framework.json +1 -1
  14. package/demo/global-preliminary-search/run/_driver/framework.json +1 -1
  15. package/demo/knockout-search/run/_driver/framework.json +1 -1
  16. package/demo/multi-country-focus-search/run/_driver/framework.json +1 -1
  17. package/docs/E2E.md +5 -5
  18. package/docs/GLOSSARY.md +85 -0
  19. package/docs/PORTAL.md +2 -2
  20. package/docs/README.md +6 -4
  21. package/docs/SECURITY.md +17 -0
  22. package/docs/architecture/01-product-overview.md +21 -9
  23. package/docs/architecture/04-configuration-reference.md +2 -0
  24. package/docs/architecture/05-config-governance.md +19 -0
  25. package/docs/architecture/05-customer-profiles.md +2 -2
  26. package/docs/configuration.md +4 -3
  27. package/docs/decisions/0006-what-the-public-repository-carries.md +30 -5
  28. package/driver/CHANGELOG.md +90 -0
  29. package/driver/README.md +25 -6
  30. package/driver/common-law-receipts.mjs +1 -1
  31. package/driver/config-staleness.mjs +1 -1
  32. package/driver/connotation-search.mjs +1 -1
  33. package/driver/contract-audit.mjs +5 -1
  34. package/driver/contract-e3-baseline.json +11 -11
  35. package/driver/dev-portal.mjs +2 -2
  36. package/driver/doubt-selection.mjs +1 -1
  37. package/driver/drainer-identity.mjs +1 -1
  38. package/driver/driver.config.mjs +1 -1
  39. package/driver/effort-model.mjs +2 -2
  40. package/driver/engine/cli-version.mjs +103 -0
  41. package/driver/engine/probe.mjs +45 -5
  42. package/driver/enqueue-schema.mjs +8 -8
  43. package/driver/feedback-store.mjs +1 -1
  44. package/driver/findings-model.mjs +52 -2
  45. package/driver/flag-snapshot.mjs +20 -0
  46. package/driver/framework.mjs +1 -1
  47. package/driver/gateway.mjs +20 -3
  48. package/driver/outbox-backoff.mjs +1 -1
  49. package/driver/package.json +3 -3
  50. package/driver/party-facts.mjs +2 -2
  51. package/driver/phase0.mjs +1 -1
  52. package/driver/pipeline.mjs +211 -106
  53. package/driver/plain-register.mjs +134 -0
  54. package/driver/portal-access.mjs +1 -1
  55. package/driver/portal-config-view.mjs +70 -2
  56. package/driver/portal-families.mjs +1 -1
  57. package/driver/portal-local-auth.mjs +5 -1
  58. package/driver/portal-report.mjs +1 -1
  59. package/driver/portal-service.mjs +79 -9
  60. package/driver/portal-upstream.mjs +2 -2
  61. package/driver/predelivery-lint.mjs +151 -2
  62. package/driver/profile-page.html +10 -2
  63. package/driver/profile-service.mjs +4 -4
  64. package/driver/profiles/README.md +3 -3
  65. package/driver/profiles.mjs +144 -12
  66. package/driver/publish/index.mjs +2 -2
  67. package/driver/publish/knockout.mjs +7 -1
  68. package/driver/publish/render-knockout.mjs +453 -118
  69. package/driver/publish/render.mjs +111 -16
  70. package/driver/reference-strip-signatures.mjs +20 -0
  71. package/driver/search-policy.mjs +3 -3
  72. package/driver/skills/blind-frame/SKILL.md +1 -1
  73. package/driver/skills/knockout-assess/SKILL.md +84 -0
  74. package/driver/skills/matter-frame/SKILL.md +6 -6
  75. package/driver/skills/matter-frame/watchlist-reference.md +2 -2
  76. package/driver/skills/prelim-common-law/SKILL.md +1 -1
  77. package/driver/skills/prelim-search/SKILL.md +1 -1
  78. package/driver/skills/prelim-search/report-prose.md +82 -0
  79. package/driver/skills/prelim-search/risk-framework-demo.manifest.json +1 -1
  80. package/driver/skills/prelim-search/risk-framework-triage.manifest.json +1 -1
  81. package/driver/skills/prelim-search/risk-framework.manifest.json +1 -1
  82. package/driver/skills/prelim-search/synthesis-rules.md +1 -1
  83. package/driver/skills/prelim-variants/SKILL.md +3 -3
  84. package/driver/stage-context.mjs +13 -0
  85. package/driver/stages.mjs +54 -7
  86. package/driver/suite-census.json +220 -58
  87. package/driver/systemd/clearotron-worker.service +3 -3
  88. package/driver/tokens.mjs +1 -1
  89. package/driver/unit-inventory.mjs +214 -27
  90. package/driver/verify.mjs +1 -1
  91. package/driver/whatif-memo-run.mjs +26 -4
  92. package/driver/whatif-memo.mjs +19 -1
  93. package/examples/README.md +2 -2
  94. package/examples/grants.example.json +6 -7
  95. package/mcp-server/CHANGELOG.md +4 -0
  96. package/mcp-server/lib/scrub.mjs +1 -1
  97. package/mcp-server/lib/whatif.mjs +2 -2
  98. package/mcp-server/package.json +1 -1
  99. package/mcp-server/packs/ops/EXAMPLES.md +2 -2
  100. package/mcp-server/server.mjs +1 -1
  101. package/package.json +31 -19
  102. package/portal-ui/dist/assets/{index-Ct3iQtZ2.css → index-1ziUJX1E.css} +43 -2
  103. package/portal-ui/dist/assets/{index-BUGgiYtF.js → index-KFAHMgdT.js} +289 -73
  104. package/portal-ui/dist/index.html +2 -2
  105. package/portal-ui/package.json +1 -1
  106. package/providers/_shared/term-shape.mjs +1 -1
  107. package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
  108. package/providers/oauth-mcp-bridge/package.json +1 -1
  109. package/providers/uspto-local/src/index-store.js +2 -2
  110. package/scripts/added-reference-check.mjs +45 -89
  111. package/scripts/citation-line-check.mjs +121 -2
  112. package/scripts/e2e.mjs +2 -2
  113. package/scripts/env-classify.mjs +67 -3
  114. package/scripts/generated-files-are-current.mjs +125 -0
  115. package/scripts/live-surface-check.mjs +38 -1
  116. package/scripts/mint-names-in-force.mjs +4 -2
  117. package/scripts/mint-public-residue.mjs +74 -0
  118. package/scripts/mint-reference-strip-backlog.mjs +12 -1
  119. package/scripts/mint-suite-census.mjs +5 -2
  120. package/scripts/nightly-notice.mjs +0 -2
  121. package/scripts/preinstall-node-check.mjs +24 -0
  122. package/scripts/render-check.mjs +13 -2
  123. package/scripts/report-frame-check.mjs +8 -1
  124. package/scripts/report-screenshot.mjs +6 -3
  125. package/scripts/revisit-render-check.mjs +6 -3
  126. package/scripts/score.mjs +1 -1
  127. package/scripts/strip-tracker-citations.mjs +38 -7
  128. package/scripts/test-full.mjs +226 -0
  129. package/scripts/test-run.mjs +16 -0
  130. package/shared/README.md +2 -1
  131. package/shared/anon-overlay.mjs +3 -3
  132. package/shared/browser-temp-root.mjs +142 -0
  133. package/shared/checkout-move.mjs +1 -1
  134. package/shared/client-door.mjs +4 -3
  135. package/shared/customer-store.mjs +1 -1
  136. package/shared/identifier-scan.mjs +1 -1
  137. package/shared/install-auth.mjs +35 -0
  138. package/shared/invocation.mjs +33 -1
  139. package/shared/listen.mjs +55 -3
  140. package/shared/names-in-force.mjs +6 -1
  141. package/shared/node-floor.mjs +97 -0
  142. package/shared/os-advice.mjs +91 -0
  143. package/shared/reap-on-exit.mjs +42 -0
  144. package/shared/reference-guard-classes.mjs +351 -0
  145. package/shared/staff-domain.mjs +154 -0
  146. package/shared/store-in-repo.mjs +23 -2
  147. package/shared/suite-census.mjs +31 -3
  148. package/shared/withheld-paths-access.mjs +36 -18
  149. package/skills/clearotron-ops/SKILL.md +1 -1
  150. package/driver/recipes/README.md +0 -49
  151. package/driver/skills/prelim-search/risk-framework-aurora.manifest.json +0 -19
  152. package/driver/skills/prelim-search/risk-framework-aurora.md +0 -77
  153. package/driver/skills/prelim-search/risk-framework-zephyr.manifest.json +0 -14
  154. package/driver/skills/prelim-search/risk-framework-zephyr.md +0 -33
  155. package/driver/skills/prelim-search/worked-examples-aurora.md +0 -10
  156. package/driver/skills/prelim-search/worked-examples-zephyr.md +0 -10
  157. package/scripts/ai-page-render-check.mjs +0 -532
  158. package/scripts/clearances-render-check.mjs +0 -480
  159. package/scripts/composer-render-check.mjs +0 -1027
  160. package/scripts/home-render-check.mjs +0 -526
  161. package/scripts/portal-lifecycle-check.mjs +0 -639
@@ -125,8 +125,8 @@ LUMENGARDE is the distinctive anchor — risk concentrates on coined-word axes a
125
125
 
126
126
  **A variant VALUE is a mark term, never a note about one.** A parenthetical, sentence punctuation
127
127
  (`—`, `;`) or a space-flanked slash makes it a label, and a label dispatched verbatim returns a
128
- confident zero over marks that exist — a nil search that reads as a clean. Write `ZEPHYR`, not
129
- `ZEPHYR (root)`; the rationale column is where the note belongs. The compiler refuses annotated values
128
+ confident zero over marks that exist — a nil search that reads as a clean. Write `COASTLINE`, not
129
+ `COASTLINE (root)`; the rationale column is where the note belongs. The compiler refuses annotated values
130
130
  whatever their length, so a two-word one is caught the same way a long one is.
131
131
 
132
132
  **If a value genuinely IS a mark carrying that punctuation** — a device mark recorded with its Vienna
@@ -178,7 +178,7 @@ silently narrows what was searched, and the deferred row is the honest outcome.
178
178
 
179
179
  ### Watchlists
180
180
 
181
- - **Aggressive enforcers:** Nordwave, Aurora Interactive
181
+ - **Aggressive enforcers:** Nordwave, Foxglade Interactive
182
182
  - **Major brand owners:** Sony, Aureon, Nintendo, HP, Activision, Take-Two, EA
183
183
  - **Competitors:** Epic Games, Valve, Unity, Riot Games
184
184
 
@@ -369,6 +369,19 @@ export const DISPATCH_EXTRAS = [
369
369
  { path: join(P.runDir, "_records"), dir: true, why: "assembleRunRecords — the fetched official records each claimed field is checked against; without them the check finds zero mismatches and the block silently vanishes" },
370
370
  ],
371
371
  },
372
+ {
373
+ // Rule 1 of the two-register rule, measured over the record's default-visible lines and handed to
374
+ // the pass that already rewrites. Advisory by construction: it reaches the writer, never the reader.
375
+ //
376
+ // It reads the RECORD rather than the narrative, and that is the point — the lines a client meets
377
+ // first are typed fields (each conflict's one sentence, the coverage notes, the actions, the mark
378
+ // assessment's two reads), not prose to be re-parsed out of a document. A sandbox without this file
379
+ // composes no block, which is correct and visible: the composer stamps what it built.
380
+ id: "refute-plain-register", stage: "narrative-refutation",
381
+ reads: (P) => [
382
+ { path: P.findings, why: "plainRegisterExtra reads the typed default-visible fields — net, coverage notes, actions, mark assessment — and the owners it must blank before reading" },
383
+ ],
384
+ },
372
385
  // ── — synthesis's two, and the asymmetry they close ─────────────────────────────────────────
373
386
  //
374
387
  // The reviewer received the plan-execution receipt as a code-derived table; the stage it reviews
package/driver/stages.mjs CHANGED
@@ -66,9 +66,9 @@ export { REGISTER_AXES, decideAxes };
66
66
  //
67
67
  // (stages.mjs cannot import pipeline.mjs — pipeline imports stages — and stage-context.mjs, where the
68
68
  // extras are declared, imports stages.mjs too. stages.mjs is the one home both sides can reach.)
69
- export function stampDispatchBlocks(ctx, stage, { built = [], failed = [] } = {}) {
69
+ export function stampDispatchBlocks(ctx, stage, { built = [], failed = [], empty = [] } = {}) {
70
70
  if (!ctx || typeof ctx !== "object") return;
71
- ctx.dispatchBlocks = { ...(ctx.dispatchBlocks ?? {}), [stage]: { built: [...built], failed: [...failed] } };
71
+ ctx.dispatchBlocks = { ...(ctx.dispatchBlocks ?? {}), [stage]: { built: [...built], failed: [...failed], empty: empty.map((e) => ({ ...e })) } };
72
72
  }
73
73
 
74
74
  // Three states, and the third is the point: ABSENT IS NOT CORRUPT. A run with no register plan is a
@@ -85,7 +85,7 @@ export function dispatchBlockState(ctx, stage, id) {
85
85
  if (!rec) return "absent";
86
86
  if (rec.built?.includes(id)) return "present";
87
87
  if (rec.failed?.some((f) => f.id === id)) return "failed";
88
- return "absent";
88
+ return rec.empty?.find((x) => x.id === id)?.kind ?? "absent"; // ran and produced no text; WHICH nothing is the builder's own word — see `nothingFound` at the foot of this file
89
89
  }
90
90
 
91
91
  // ── The two synthesis lines that make a claim about the blocks, branched in ONE place ──────────────
@@ -1301,7 +1301,7 @@ export const STAGES = {
1301
1301
  job.jurisdictions ? `Instructed territories (AUTHORITATIVE scope — do NOT widen to "major markets"): ${Array.isArray(job.jurisdictions) ? job.jurisdictions.join(", ") : job.jurisdictions}` : "",
1302
1302
  job.customer ? `Customer/applicant (from the intake brief — drives the self-exclusion set): ${job.customer}` : "",
1303
1303
  // WS-B profile defaults — falsy-omitted, so an empty profile renders this message byte-identical
1304
- // to the pre-profile shape (the aurora/generic regression anchor).
1304
+ // to the pre-profile shape (the regression anchor).
1305
1305
  // Industry is CONTEXT not a rule (Design Law #1): it sharpens which sectors/adjacencies matter for
1306
1306
  // the vertical (e.g. food/ingestible adjacency for a beverage brand), it never dictates a conclusion.
1307
1307
  profile?.industry ? `Customer industry (context for sector framing — let it sharpen the relevant sectors and adjacencies for this vertical; it is context, never a rule that decides a finding): ${profile.industry}.` : "",
@@ -1813,7 +1813,7 @@ export const STAGES = {
1813
1813
  // DETERMINISTIC GRID (robust fix, 2026-06-14): when the driver wrote a grid-spec, the model is OUT
1814
1814
  // of the grid data path entirely — it passes grid_spec_path, the plugin runs the dictated cells and
1815
1815
  // WRITES common-law-grid.json from the API response (no truncation, no dropped/mis-keyed cells), and
1816
- // the model only judges the returned candidates. This is the structural cure for both the Zephyr
1816
+ // the model only judges the returned candidates. This is the structural cure for both the dense-marketplace
1817
1817
  // truncation and the NOVA PULSE dropped-cell failures (no tier bump would fix the output ceiling).
1818
1818
  if (gridSpecPath) {
1819
1819
  return lines(
@@ -2353,7 +2353,7 @@ export const STAGES = {
2353
2353
  },
2354
2354
  "placements.json — the structured mirror, keys EXACTLY {mark, owner, jurisdiction, records, tier, reason} + optional borderline": {
2355
2355
  class: "mechanical:code-rendered", tokens: ["placementmodel_missing", "placements_unparseable", "placements_key_unknown", "placement_invalid", "placement_key_unknown"],
2356
- why: "The driver renders it: renderPlacementsJson() in placement-form.mjs over the union, landed by gateway.mjs:706 (the #562 union-then-render block; re-verified 2026-08-29 the old :507 predated this branch and pointed at the engine-resolution doc comment). The skill file was not updated with #562, so the stage's two sources contradict each other — the contract that escapes if E1 is authored against stages.mjs alone. [citation unverified]",
2356
+ why: "The driver renders it: renderPlacementsJson() in placement-form.mjs over the union, landed by `syncPlacementForm` in gateway.mjs (the union-then-render block). Cited by SYMBOL because the number has now moved twice: an earlier :507 pointed at an engine-resolution comment, and :706 went blank when an unrelated block was inserted above it. The skill file was not updated with #562, so the stage's two sources contradict each other — the contract that escapes if E1 is authored against stages.mjs alone.",
2357
2357
  },
2358
2358
  "mark / owner / records / territories / classes on a SELECTED row": {
2359
2359
  class: "mechanical:code-extracted", tokens: [],
@@ -3222,7 +3222,7 @@ export const STAGES = {
3222
3222
  // lane's own prose contract. Two of the four longest sentences in the delivered report were
3223
3223
  // coverage/gap prose, and NOTHING governed it: the code-stamped `coverage_line:` front-matter
3224
3224
  // (scope-facts.mjs,) is EXCLUDED from predelivery-lint's prose scan by design
3225
- // (stripFrontMatterBlock, predelivery-lint.mjs:374), so a coverage number re-typed into prose
3225
+ // (stripFrontMatterBlock, predelivery-lint.mjs:376), so a coverage number re-typed into prose
3226
3226
  // beside it is caught only if the prose form itself trips SCOPE_NUMBER_RE — which the narrative's
3227
3227
  // own phrasings routinely dodge. Prompt-only per §7; the fix is to stop authoring the duplicate.
3228
3228
  `COVERAGE PROSE (the lane that runs longest — hold it to the house budgets): the register coverage line a reader sees is COMPUTED from this run's own record and STAMPED BY CODE as front-matter (the proportion, the class states, the searched registers). Do NOT re-type its numbers anywhere in prose — not in a coverage[] note, not in the coverage_judgment reason, not in the narrative. Nothing catches the duplicate for you (the code-stamped line sits outside the prose checks precisely because ITS numbers are the authoritative ones), so a re-typed count does not disagree with the record — it silently drifts from it a redelivery later. Carry the SUBSTANCE and drop the number: "the remaining forms are non-Latin script" says the useful half; the code says how many. State each coverage fact ONCE, in ONE place — an area's state belongs in its coverage[] row, the sufficiency read belongs in coverage_judgment.reason, and neither is re-narrated in the other or in the findings. And say WHICH KIND of negative you hold every time: a source this run actually queried and got nothing from reads "searched — none found"; a source it did not reach reads "not searched this run" or "could not be searched — <the reason>". The same source must never wear both readings in one report.`,
@@ -4617,3 +4617,50 @@ export function whatsappRouting(job, agentId) {
4617
4617
  whatsappCcOperator: OPERATOR_WHATSAPP_COPY ? (AGENT_WHATSAPP[agentId] ?? null) : null,
4618
4618
  };
4619
4619
  }
4620
+
4621
+ // ── A BUILDER THAT RAN AND SAID NOTHING SAYS SO, AND SAYS WHICH NOTHING ────────────────────────────
4622
+ //
4623
+ // AT THE FOOT OF THE FILE ON PURPOSE, and not because it belongs here. It belongs beside
4624
+ // `dispatchBlockState`, which is where a reader will look for it. Sixty-one line citations elsewhere in
4625
+ // the tree point INTO this file by number, and inserting twenty-three lines near the top moved every one
4626
+ // of them — three onto blank lines, where a guard caught them, and the rest onto real code, where one
4627
+ // guard could not see them and a second one could. Repointing them then rewrote their lines, which put
4628
+ // fifty-four pre-existing bare citations on the ADDED side of the diff and failed the check that refuses
4629
+ // newly-added ones. Appending costs a reader one jump; shifting costs the tree ninety-four citations and
4630
+ // two guards' worth of churn. The same reasoning is written up on the export tagging in bin/onboard.mjs.
4631
+ //
4632
+ // The receipt used to record a builder only when it produced text. An empty return landed in neither
4633
+ // list, so "this run had nothing to flag" and "this builder never ran" were the same reading — and the
4634
+ // second is a defect while the first is an ordinary run. Found in review on the narrative refutation
4635
+ // block: it reached its stage with flags and the stage rewrote none, and the receipt could not be used
4636
+ // to tell that apart from a block that never built.
4637
+ //
4638
+ // TWO EMPTIES, NOT ONE, because collapsing them repeats the fault one size down. A builder that read the
4639
+ // record and found nothing to flag has made a NEGATIVE FINDING; a builder whose source file was not
4640
+ // there has not looked at all. A single `empty` class would make "no flags over forty visible lines"
4641
+ // indistinguishable from "there was no record to open". So a builder returns one of these rather than a
4642
+ // bare "", and a bare "" from a builder nobody has taught to discriminate is recorded as exactly that —
4643
+ // unstated — rather than being counted as either.
4644
+ export const nothingFound = (why) => ({ dispatchEmpty: "nothing-found", why: String(why) });
4645
+ export const nothingToRead = (why) => ({ dispatchEmpty: "nothing-to-read", why: String(why) });
4646
+
4647
+ /** The sentinel a builder returned, or null when it returned something else. PURE. */
4648
+ export const emptyReturn = (v) =>
4649
+ (v && typeof v === "object" && typeof v.dispatchEmpty === "string" && typeof v.why === "string" ? v : null);
4650
+
4651
+ /**
4652
+ * Sort one builder's return into the three lists the receipt carries. Mutates the arrays it is given,
4653
+ * which is what keeps its caller two lines long — and its caller is inside a file whose line numbers
4654
+ * thirty-three citations depend on.
4655
+ */
4656
+ export function recordEmptyReturn(built, id, { parts, ids, empty }) {
4657
+ const said = emptyReturn(built);
4658
+ if (said) empty.push({ id, kind: said.dispatchEmpty, why: said.why });
4659
+ else if (typeof built === "string" && built) { parts.push(built); ids.push({ id, chars: built.length }); }
4660
+ else empty.push({ id, kind: "empty-unstated", why: "the builder returned nothing and named no reason" });
4661
+ }
4662
+
4663
+ /** Why a builder that ran produced nothing — its own sentence, or null when it is not in that state. PURE. */
4664
+ export function dispatchBlockWhy(ctx, stage, id) {
4665
+ return ctx?.dispatchBlocks?.[stage]?.empty?.find((x) => x.id === id)?.why ?? null;
4666
+ }