clearotron 0.4.0-beta.0 → 0.4.0-beta.2

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 (293) hide show
  1. package/.env.example +9 -0
  2. package/INSTALL.md +4 -31
  3. package/README.md +5 -5
  4. package/bin/brandowner.mjs +1 -1
  5. package/bin/onboard.mjs +18 -74
  6. package/bin/start.mjs +58 -14
  7. package/bin/update.mjs +10 -2
  8. package/build-info.json +2 -2
  9. package/demo/full-country-search/run/_driver/search-policy.json +1 -1
  10. package/demo/full-country-search/run/status.json +1 -1
  11. package/demo/global-preliminary-search/run/_driver/search-policy.json +1 -1
  12. package/demo/global-preliminary-search/run/status.json +1 -1
  13. package/demo/knockout-search/run/_driver/search-policy.json +1 -1
  14. package/demo/knockout-search/run/status.json +1 -1
  15. package/demo/multi-country-focus-search/run/_driver/search-policy.json +1 -1
  16. package/demo/multi-country-focus-search/run/status.json +1 -1
  17. package/docs/README.md +1 -0
  18. package/docs/SECURITY-OWASP.md +298 -0
  19. package/docs/SECURITY.md +18 -13
  20. package/docs/architecture/02-architecture.md +5 -5
  21. package/docs/architecture/03-run-lifecycle.md +28 -29
  22. package/docs/architecture/04-configuration-reference.md +5 -10
  23. package/docs/architecture/05-config-governance.md +5 -7
  24. package/docs/architecture/06-operations-runbook.md +0 -12
  25. package/docs/architecture/07-quality-and-audit.md +12 -13
  26. package/docs/architecture/09-security-and-data.md +9 -4
  27. package/driver/CHANGELOG.md +70 -0
  28. package/driver/ask-ledger.mjs +4 -2
  29. package/driver/authority-trees.mjs +90 -8
  30. package/driver/band-shape.mjs +13 -13
  31. package/driver/blind-frame-model.mjs +1 -1
  32. package/driver/case-law-ledger.mjs +19 -3
  33. package/driver/claim-liveness.mjs +30 -1
  34. package/driver/clearance-variants-record.mjs +10 -3
  35. package/driver/common-law-receipts.mjs +64 -14
  36. package/driver/commonlaw-carry.mjs +2 -2
  37. package/driver/compare.mjs +2 -2
  38. package/driver/connotation-search.mjs +33 -104
  39. package/driver/contract-arm2-baseline.json +6 -5
  40. package/driver/contract-e3-backlog.mjs +62 -62
  41. package/driver/contract-vocabulary.mjs +38 -31
  42. package/driver/coverage-form-io.mjs +13 -1
  43. package/driver/coverage-form.mjs +42 -4
  44. package/driver/coverage-ledger.mjs +3 -5
  45. package/driver/cross-check-wait.mjs +43 -0
  46. package/driver/declination-call.mjs +34 -10
  47. package/driver/declination-tool.mjs +27 -15
  48. package/driver/deferral-row.mjs +139 -0
  49. package/driver/degraded-parts.mjs +377 -0
  50. package/driver/deliver-trigger.sh +1 -1
  51. package/driver/demo-container.mjs +4 -1
  52. package/driver/dev-portal.mjs +5 -5
  53. package/driver/dispatch-record.mjs +2 -2
  54. package/driver/driver.config.mjs +193 -96
  55. package/driver/e2e/README.md +1 -1
  56. package/driver/effective-scope.mjs +58 -2
  57. package/driver/engine/anthropic-agent.mjs +103 -27
  58. package/driver/engine/auth.mjs +2 -2
  59. package/driver/engine/child-record.mjs +11 -0
  60. package/driver/engine/cli-version.mjs +4 -3
  61. package/driver/engine/common.mjs +17 -2
  62. package/driver/engine/deny-authority-write.mjs +3 -1
  63. package/driver/engine/engine-env.mjs +174 -0
  64. package/driver/engine/engine-spawn.mjs +179 -0
  65. package/driver/engine/mcp/band-server.mjs +1 -1
  66. package/driver/engine/mcp/clarivate-server.mjs +2 -1
  67. package/driver/engine/mcp/codex-config.mjs +112 -3
  68. package/driver/engine/mcp/corsearch-server.mjs +1 -0
  69. package/driver/engine/mcp/declination-server.mjs +9 -8
  70. package/driver/engine/mcp/dispositions-server.mjs +2 -1
  71. package/driver/engine/mcp/euipo-server.mjs +1 -0
  72. package/driver/engine/mcp/fetch-server.mjs +14 -11
  73. package/driver/engine/mcp/free-tier-server.mjs +1 -1
  74. package/driver/engine/mcp/gather-config.mjs +23 -11
  75. package/driver/engine/mcp/perplexity-server.mjs +97 -17
  76. package/driver/engine/mcp/probe-server.mjs +43 -21
  77. package/driver/engine/mcp/public-fetch.mjs +195 -0
  78. package/driver/engine/mcp/recording-server.mjs +79 -14
  79. package/driver/engine/mcp/signa-server.mjs +1 -0
  80. package/driver/engine/mcp/supplemental.mjs +14 -14
  81. package/driver/engine/mcp/unit-note-server.mjs +50 -1
  82. package/driver/engine/mcp/uspto-local-server.mjs +1 -0
  83. package/driver/engine/openai-agent.mjs +109 -28
  84. package/driver/engine/probe.mjs +152 -19
  85. package/driver/enqueue-schema.mjs +5 -5
  86. package/driver/feedback-issues.mjs +1 -1
  87. package/driver/feedback-store.mjs +1 -1
  88. package/driver/findings-model.mjs +67 -16
  89. package/driver/flag-snapshot.mjs +1 -1
  90. package/driver/floor-duty.mjs +23 -7
  91. package/driver/form-neighbourhood.mjs +52 -17
  92. package/driver/frame-diff-model.mjs +3 -3
  93. package/driver/framework-method.mjs +304 -0
  94. package/driver/gateway.mjs +49 -21
  95. package/driver/hand-off-exits.mjs +129 -0
  96. package/driver/jx-lanes.mjs +1 -1
  97. package/driver/jx.mjs +8 -5
  98. package/driver/knockout-assess-record.mjs +7 -1
  99. package/driver/knockout-frame-record.mjs +96 -34
  100. package/driver/knockout-review-record.mjs +7 -2
  101. package/driver/log.mjs +2 -2
  102. package/driver/matter-frame-record.mjs +31 -2
  103. package/driver/methodology-witness.mjs +6 -3
  104. package/driver/named-band.mjs +1 -1
  105. package/driver/owner-use-check.mjs +45 -10
  106. package/driver/package.json +1 -1
  107. package/driver/partial-payload-baseline.json +4 -0
  108. package/driver/phase0.mjs +2 -2
  109. package/driver/pipeline-knockout.mjs +256 -129
  110. package/driver/pipeline.mjs +417 -614
  111. package/driver/placement-carry.mjs +22 -3
  112. package/driver/placement-form-io.mjs +18 -4
  113. package/driver/placement-form.mjs +53 -4
  114. package/driver/placement-union.mjs +32 -5
  115. package/driver/portal-report.mjs +3 -3
  116. package/driver/portal-service.mjs +6 -5
  117. package/driver/portal-static.mjs +11 -18
  118. package/driver/predelivery-lint.mjs +10 -10
  119. package/driver/profile-page.html +5 -5
  120. package/driver/profiles.mjs +5 -5
  121. package/driver/progress.mjs +6 -4
  122. package/driver/provider-usage.mjs +25 -2
  123. package/driver/publish/index.mjs +97 -25
  124. package/driver/publish/knockout.mjs +136 -14
  125. package/driver/publish/profiles-page.mjs +2 -0
  126. package/driver/publish/publish-inputs.mjs +10 -4
  127. package/driver/publish/render-knockout.mjs +48 -21
  128. package/driver/publish/render.mjs +13 -8
  129. package/driver/publish/report-data.mjs +2 -0
  130. package/driver/publish/search-depth.mjs +77 -4
  131. package/driver/publish/templates/report.css +1 -1
  132. package/driver/publish/xlsx.mjs +34 -15
  133. package/driver/reasoning-tripwires.mjs +2 -160
  134. package/driver/recall-receipt.mjs +27 -0
  135. package/driver/recall-reconciliation.mjs +1 -1
  136. package/driver/record-carry.mjs +14 -14
  137. package/driver/recording-agreement.mjs +2 -2
  138. package/driver/reference-score.mjs +169 -47
  139. package/driver/register-count.mjs +61 -4
  140. package/driver/register-digest-record.mjs +14 -1
  141. package/driver/register-plan.mjs +161 -80
  142. package/driver/register-records.mjs +37 -4
  143. package/driver/registration-scripts.mjs +22 -0
  144. package/driver/registry-fidelity.mjs +4 -4
  145. package/driver/repair-composers.mjs +23 -7
  146. package/driver/repair-contract.mjs +1 -1
  147. package/driver/repairs.mjs +5 -2
  148. package/driver/reviewer-open-points.mjs +33 -0
  149. package/driver/rule-shape.mjs +8 -8
  150. package/driver/run-economics.mjs +3 -3
  151. package/driver/run-integrity.mjs +2 -2
  152. package/driver/runner.mjs +17 -5
  153. package/driver/scope-facts.mjs +89 -12
  154. package/driver/score-redaction.mjs +247 -0
  155. package/driver/screen-gate.mjs +1 -1
  156. package/driver/search-policy.mjs +17 -0
  157. package/driver/skills/README.md +2 -2
  158. package/driver/skills/blind-frame/SKILL.md +2 -2
  159. package/driver/skills/clearance-common-law/SKILL.md +29 -36
  160. package/driver/skills/clearance-common-law/perplexity-prompts.md +8 -30
  161. package/driver/skills/clearance-register/SKILL.md +15 -16
  162. package/driver/skills/clearance-register/digest.md +15 -31
  163. package/driver/skills/clearance-register/register-recipes.md +14 -56
  164. package/driver/skills/clearance-register/unit.md +37 -48
  165. package/driver/skills/clearance-search/SKILL.md +6 -8
  166. package/driver/skills/clearance-search/delivery-contract.md +0 -7
  167. package/driver/skills/clearance-search/firm-wide-reasoning.md +5 -4
  168. package/driver/skills/clearance-search/phase2-execution.md +5 -6
  169. package/driver/skills/clearance-search/report-prose.md +5 -5
  170. package/driver/skills/clearance-search/risk-framework-triage.md +2 -2
  171. package/driver/skills/clearance-search/synthesis-rules.md +9 -9
  172. package/driver/skills/clearance-search/template-formatting.md +2 -2
  173. package/driver/skills/clearance-variants/SKILL.md +8 -8
  174. package/driver/skills/clearance-variants/transliteration-scripts.md +2 -2
  175. package/driver/skills/frame-diff/SKILL.md +2 -2
  176. package/driver/skills/knockout-assess/SKILL.md +40 -10
  177. package/driver/skills/knockout-frame/SKILL.md +42 -10
  178. package/driver/skills/matter-frame/SKILL.md +12 -5
  179. package/driver/skills/matter-frame/watchlist-reference.md +1 -1
  180. package/driver/skills/narrative-refutation/SKILL.md +5 -3
  181. package/driver/skills/placement-inquiry/SKILL.md +5 -1
  182. package/driver/stage-context.mjs +27 -6
  183. package/driver/stages-knockout.mjs +92 -79
  184. package/driver/stages.mjs +111 -68
  185. package/driver/stray-artifacts.mjs +9 -5
  186. package/driver/suite-census.json +696 -150
  187. package/driver/synthesis-record.mjs +26 -4
  188. package/driver/systemd/README.md +5 -6
  189. package/driver/systemd/clearotron-client-mcp.service +24 -0
  190. package/driver/systemd/clearotron-mcp-face.service +24 -0
  191. package/driver/systemd/clearotron-portal.service +24 -0
  192. package/driver/systemd/clearotron-worker.service +29 -0
  193. package/driver/tokens.mjs +10 -9
  194. package/driver/turnaround-bands.mjs +1 -1
  195. package/driver/unit-inventory.mjs +24 -43
  196. package/driver/variant-manifest-model.mjs +22 -3
  197. package/driver/verify-knockout.mjs +163 -4
  198. package/driver/verify.mjs +42 -16
  199. package/driver/web-grid.mjs +150 -0
  200. package/driver/whatif-memo-run.mjs +1 -1
  201. package/driver/withheld-families.mjs +113 -4
  202. package/driver/worker-heartbeat.mjs +13 -2
  203. package/mcp-server/CHANGELOG.md +8 -0
  204. package/mcp-server/README.md +1 -1
  205. package/mcp-server/lib/knockout.mjs +1 -1
  206. package/mcp-server/lib/ops.mjs +10 -10
  207. package/mcp-server/lib/scrub.mjs +1 -1
  208. package/mcp-server/lib/trace.mjs +3 -2
  209. package/mcp-server/package.json +1 -1
  210. package/mcp-server/server.mjs +3 -3
  211. package/package.json +1 -1
  212. package/portal-ui/dist/assets/{index-DVtz44vH.js → index-BPAUjcI0.js} +3 -2
  213. package/portal-ui/dist/assets/{index-5CCwiJG7.css → index-D2wrw9cH.css} +18 -2
  214. package/portal-ui/dist/assets/plus-jakarta-sans-BUCHxqJ-.woff2 +0 -0
  215. package/portal-ui/dist/index.html +2 -15
  216. package/portal-ui/package.json +1 -1
  217. package/providers/_shared/README.md +1 -1
  218. package/providers/_shared/answer-memory.mjs +199 -0
  219. package/providers/_shared/enumerate.mjs +155 -50
  220. package/providers/_shared/execute-plan.mjs +62 -16
  221. package/providers/_shared/ledger-path.mjs +1 -1
  222. package/providers/_shared/ledger.mjs +48 -6
  223. package/providers/_shared/plan-guards.mjs +7 -0
  224. package/providers/_shared/script-form.mjs +24 -5
  225. package/providers/_shared/term-shape.mjs +5 -5
  226. package/providers/clarivate/src/capabilities.js +11 -0
  227. package/providers/clarivate/src/core.js +193 -18
  228. package/providers/corsearch/README.md +1 -2
  229. package/providers/corsearch/src/core.js +2 -2
  230. package/providers/jx-subclass/lookup.mjs +1 -1
  231. package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
  232. package/providers/oauth-mcp-bridge/README.md +8 -7
  233. package/providers/oauth-mcp-bridge/bridge.mjs +21 -5
  234. package/providers/oauth-mcp-bridge/package.json +1 -1
  235. package/providers/oauth-mcp-bridge/systemd/courtlistener-mcp.service +1 -1
  236. package/providers/oauth-mcp-bridge/warm-server.mjs +16 -5
  237. package/providers/perplexity/src/core.js +246 -21
  238. package/providers/signa/src/capabilities.js +24 -0
  239. package/providers/signa/src/core.js +93 -25
  240. package/scripts/README.md +2 -1
  241. package/scripts/added-reference-check.mjs +2 -1
  242. package/scripts/backup-recall-stores.mjs +5 -5
  243. package/scripts/demo-evidence.mjs +2 -1
  244. package/scripts/deprecate-below.mjs +114 -2
  245. package/scripts/drive-env-check.mjs +2 -1
  246. package/scripts/e2e-first-time.mjs +469 -0
  247. package/scripts/e2e-scenario-ops.mjs +441 -0
  248. package/scripts/e2e.mjs +104 -16
  249. package/scripts/env-audit.mjs +8 -0
  250. package/scripts/freeze-example-run.mjs +7 -7
  251. package/scripts/generated-files-are-current.mjs +2 -1
  252. package/scripts/hand-off-exits-probe.mjs +75 -0
  253. package/scripts/import-cycle-check.mjs +2 -1
  254. package/scripts/merge-shape-check.mjs +2 -1
  255. package/scripts/package-size-budget.mjs +2 -1
  256. package/scripts/record-carry-probe.mjs +1 -1
  257. package/scripts/release-entry-catch-up.mjs +77 -8
  258. package/scripts/release-install-check.mjs +4 -1
  259. package/scripts/release-note-required.mjs +139 -9
  260. package/scripts/release-rehearsal-version.mjs +60 -0
  261. package/scripts/release-sbom.mjs +104 -0
  262. package/scripts/release-visible-check.mjs +7 -5
  263. package/scripts/render-check.mjs +5 -2
  264. package/scripts/report-offline-render-check.mjs +169 -0
  265. package/scripts/score.mjs +79 -8
  266. package/scripts/strip-titles-and-attributions.mjs +2 -1
  267. package/scripts/strip-tracker-citations.mjs +2 -1
  268. package/scripts/test-full.mjs +2 -1
  269. package/scripts/test-run.mjs +10 -3
  270. package/scripts/third-party-notices.mjs +3 -1
  271. package/scripts/travelling-predicates.mjs +1 -1
  272. package/scripts/writing-standard-check.mjs +2 -1
  273. package/shared/brand-fonts.mjs +59 -0
  274. package/shared/brand.mjs +6 -6
  275. package/shared/browser-temp-root.mjs +3 -2
  276. package/shared/doctrine-overlay.mjs +1 -1
  277. package/shared/driver-dir.mjs +47 -11
  278. package/shared/fonts/OFL-fira-code.txt +93 -0
  279. package/shared/fonts/OFL-plus-jakarta-sans.txt +93 -0
  280. package/shared/fonts/README.md +37 -0
  281. package/shared/fonts/fira-code.woff2 +0 -0
  282. package/shared/fonts/plus-jakarta-sans.woff2 +0 -0
  283. package/shared/names-in-force.mjs +2 -0
  284. package/shared/npm-cli.mjs +23 -0
  285. package/shared/os-advice.mjs +14 -2
  286. package/shared/path-seps.mjs +42 -0
  287. package/shared/process-table.mjs +71 -3
  288. package/shared/reference-guard-classes.mjs +30 -6
  289. package/shared/root-doc-commands.mjs +6 -2
  290. package/shared/running-start.mjs +24 -7
  291. package/shared/scope.mjs +2 -2
  292. package/shared/wsl.mjs +1 -1
  293. package/driver/known-conflicts.mjs +0 -327
@@ -30,6 +30,7 @@ export const MIN_CELLS_PER_VARIANT = 7;
30
30
  // It composes `queryKey`, which is the one author of what "the same text" means across a provider
31
31
  // boundary, and keeps its own wrapping-quote strip because a term arrives quoted here and does not there.
32
32
  import { queryKey } from "./connotation-search.mjs";
33
+ import { dictatedCells } from "../providers/perplexity/src/core.js"; // what the spec asks, product or blocks
33
34
 
34
35
  const norm = (s) => queryKey(String(s || "").trim().replace(/^["'`\u2018\u2019\u201c\u201d]+|["'`\u2018\u2019\u201c\u201d]+$/g, ""));
35
36
 
@@ -285,6 +286,30 @@ export function findGridLedgerViolations(manifestOrTerms, ledgerRaw, { minCellsP
285
286
  return shortVariants(variants, (term) => platforms.get(norm(term))?.size || 0, minCellsPerVariant);
286
287
  }
287
288
 
289
+ /**
290
+ * — THE RECEIPTS JOIN FOR A GRID IN BLOCKS: every cell the spec dictated ran — a cell or a recorded gap in
291
+ * the ledger — judged cell by cell. The count and identity joins below assume every term owes every
292
+ * platform, which a grid the matter frame decided does not ask. Same {variant, cells, expected} shape as
293
+ * findGridLedgerViolations, plus the platforms still owed, so the `grid_join_missing` token is unchanged.
294
+ * Throws on an unparseable ledger.
295
+ *
296
+ * @returns {Array<{variant:string, cells:number, expected:number, missing:string[]}>}
297
+ */
298
+ export function findUnranDictatedCells(spec, ledgerRaw) {
299
+ const map = parseGridLedger(ledgerRaw);
300
+ const owed = new Map(); // term → [platform, ran?]
301
+ for (const [term, platform] of dictatedCells(spec)) {
302
+ if (!owed.has(term)) owed.set(term, []);
303
+ owed.get(term).push([platform, map.get(norm(term))?.has(norm(platform)) === true]);
304
+ }
305
+ const out = [];
306
+ for (const [variant, rows] of owed) {
307
+ const missing = rows.filter(([, ran]) => !ran).map(([p]) => p);
308
+ if (missing.length) out.push({ variant, cells: rows.length - missing.length, expected: rows.length, missing });
309
+ }
310
+ return out;
311
+ }
312
+
288
313
  // ── WS-B: platform-identity join (per-customer profiles) ────────────────────────────────────────────────
289
314
  // The count floor proves HOW MANY distinct platforms each variant accounted for — never WHICH. With a
290
315
  // dictated per-customer platform list, a worker on gaming habit could sweep the wrong marketplaces and
@@ -674,6 +699,10 @@ export function splitGridSpec(spec, { outputPaths = {}, dispositionsPaths = {} }
674
699
  // The meaning seat sweeps no cells. `terms: []` is what makes its dispatch the meaning work and
675
700
  // nothing else, and it is why the grid halves keep an untouched 50/50 parity split.
676
701
  terms: isMeaning ? [] : terms[h],
702
+ // A grid in blocks keeps its blocks, each cut to this half's terms (a term owns all its cells in one half).
703
+ ...(Array.isArray(spec.grids) ? { grids: spec.grids.map((g) => ({ terms: g.terms.filter((t) => !isMeaning && terms[h].includes(t)), platforms: g.platforms })).filter((g) => g.terms.length) } : {}),
704
+ // The famous-mark record names only the cells this seat runs.
705
+ ...(Array.isArray(spec.famous) ? { famous: spec.famous.filter((t) => !isMeaning && terms[h].includes(t)) } : {}),
677
706
  output_path: outputPaths[h] ?? spec.output_path,
678
707
  // The disposition_required stamp still rides BOTH halves: it is the receipt-presence arm, and a
679
708
  // stray pr_risk block in the non-owning half must still be judged rather than waved through.
@@ -714,7 +743,8 @@ export function splitGridSpec(spec, { outputPaths = {}, dispositionsPaths = {} }
714
743
  * batch has since closed is dropped). This is the never-thinner guarantee: every dictated cell is
715
744
  * accounted as a real cell or an HONEST gap, so a half failure can never yield a ledger that is
716
745
  * thinner but still valid. A half's honestly-recorded gap for a cell OUTSIDE the spec (a re-keyed
717
- * variant / an extra swept platform) is UNIONED in too — the spec-only recompute would erase it.
746
+ * variant / an extra swept platform) is UNIONED in too — the spec-only recompute would erase it,
747
+ * except a meaning query's gap row when the merged receipts answer that query.
718
748
  * - extras.pr_risk[]: concatenated (a then b), deduped by trimmed query — restores the full dictated
719
749
  * meaning-receipt set from the per-half partitions. Other extras keys: arrays concatenate,
720
750
  * anything else first-defined wins.
@@ -773,23 +803,32 @@ export function mergeGrids(a, b, { spec, halfErrors = {} } = {}) {
773
803
  // gaps: recomputed against the FULL spec
774
804
  const halfTerms = splitGridTerms(spec?.terms ?? []);
775
805
  const gaps = [];
776
- for (const term of spec?.terms ?? []) {
777
- for (const platform of spec?.platforms ?? []) {
778
- if (seen.has(key(term, platform))) continue;
779
- const owner = halfOfTerm(halfTerms, term);
780
- const error = gapErr.get(key(term, platform))?.error
781
- || (owner && halfErrors[owner]) || "cell not accounted by either half-grid";
782
- gaps.push({ term, platform, error });
783
- }
806
+ // Over the cells the spec DICTATES, never terms × platforms: a grid the frame decided is blocks, and the
807
+ // product of its unions would make a gap of every cell nobody asked for, which the closure pass would run.
808
+ for (const [term, platform] of spec ? dictatedCells(spec) : []) {
809
+ if (seen.has(key(term, platform))) continue;
810
+ const owner = halfOfTerm(halfTerms, term);
811
+ const error = gapErr.get(key(term, platform))?.error
812
+ || (owner && halfErrors[owner]) || "cell not accounted by either half-grid";
813
+ gaps.push({ term, platform, error });
784
814
  }
785
815
  // UNION honestly-recorded NON-SPEC gap rows: a half may record a gap for a cell OUTSIDE the canonical
786
816
  // spec (a legitimately re-keyed variant / an extra platform a half swept). The spec-only recompute above
787
817
  // never emits those, silently erasing an honestly-recorded coverage gap — union them so an honest gap
788
818
  // survives the merge. A cell present in cells[] still wins (a stale gap a supplementary batch has since
789
819
  // closed is dropped exactly as for spec cells), and a gap already emitted by the spec loop is not doubled.
820
+ //
821
+ // A MEANING QUERY THE MERGED RECEIPTS ANSWER KEEPS NO GAP ROW. The grid tool records one for each dictated
822
+ // query its program did not return, and the engine's re-issue can answer that query later in the same
823
+ // ledger. Kept beside the answer, the row says a search did not complete when it did, and the closure pass
824
+ // takes every gap row for a store cell, so it searches the query again on a site called "connotation".
825
+ // Queries are matched as the receipts are (norm).
826
+ const answered = new Set((Array.isArray(extras.pr_risk) ? extras.pr_risk : [])
827
+ .map((e) => (typeof e?.query === "string" ? norm(e.query) : "")).filter(Boolean));
790
828
  const gappedKeys = new Set(gaps.map((g) => key(g.term, g.platform)));
791
829
  for (const [k, g] of gapErr) {
792
830
  if (seen.has(k) || gappedKeys.has(k)) continue;
831
+ if (String(g.platform).toLowerCase() === "connotation" && answered.has(norm(g.term))) continue;
793
832
  gaps.push({ term: g.term, platform: g.platform, error: g.error || "cell not accounted by either half-grid" });
794
833
  }
795
834
  return { cells, extras, gaps };
@@ -861,9 +900,9 @@ export function mergeCommonLawFindings(parts) {
861
900
  // a present row chose.
862
901
  //
863
902
  // The join bug is fixed independently, because a census nobody can trust is worse than none:
864
- // • TERM match is EXACT after stripping a trailing annotation ("提基冰沙 (ZH: …)"). It used to be
903
+ // • TERM match is EXACT after stripping a trailing annotation ("瓦波冰沙 (ZH: …)"). It used to be
865
904
  // `rowTerm.includes(cellTerm)`, so the row for "CORAL FREEZE" was credited to the cells for "SLUSH"
866
- // and even "TIK", and the row for "提基冰沙" to the cell for "冰沙" — 71 of the 221.
905
+ // and even "WAV", and the row for "瓦波冰沙" to the cell for "冰沙" — 71 of the 221.
867
906
  // • a platform ROLL-UP row ("Multiple platforms") counts only when the term has no platform-specific
868
907
  // row of its own; combined with the substring bug it used to match every platform at once.
869
908
  // `undisposed` (title containment over raw web-search titles) is GONE: it returned 1,986 rows on the
@@ -926,7 +965,16 @@ export function findSimilarListingSignals(findingsContent) {
926
965
  const signals = [];
927
966
  const OWNER_RE = /(?:developer|publisher|seller|owner)(?:\s+|_)of(?:\s+|_)record\W{0,5}([^\n|]+)/i;
928
967
  const OWNER_SKIP_RE = /not extracted|unknown|n\/a|none|^[\s—–-]*$/i;
968
+ // A table whose header lists the of-record fields matches OWNER_RE on its first label and captures the
969
+ // next one, so a label reached the register as an owner's name ("publisherofrecord", once its
970
+ // underscores were stripped). A label is never a name.
971
+ const OWNER_LABEL_RE = /^(?:developer|publisher|seller|owner)[\s_]*of[\s_]*record\b/i;
929
972
  const URL_RE = /https?:\/\/[^\s)|\]">]+/;
973
+ // A TABLE'S HEADER ROW NAMES COLUMNS, NEVER AN OWNER, whatever column follows an of-record label ("URL",
974
+ // "Store"). It is the row a separator row follows, and both are table rows: a bare "---" under an owner's
975
+ // line is a rule, not a separator, and the owner stands.
976
+ const SEPARATOR_RE = /^\s*\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)*\|?\s*$/;
977
+ const headerRow = (ln, next) => ln.includes("|") && String(next ?? "").includes("|") && SEPARATOR_RE.test(next);
930
978
  let inMatrix = false, inFindings = false;
931
979
  let block = null; // { markText, owner, url }
932
980
  const flush = () => {
@@ -935,7 +983,9 @@ export function findSimilarListingSignals(findingsContent) {
935
983
  }
936
984
  block = null;
937
985
  };
938
- for (const ln of (findingsContent || "").split("\n")) {
986
+ const lines = (findingsContent || "").split("\n");
987
+ for (let i = 0; i < lines.length; i++) {
988
+ const ln = lines[i];
939
989
  const h = ln.match(/^(#{1,6})\s+(.*)/);
940
990
  if (h) {
941
991
  const title = h[2];
@@ -958,10 +1008,10 @@ export function findSimilarListingSignals(findingsContent) {
958
1008
  continue;
959
1009
  }
960
1010
  if (inFindings && block) {
961
- const om = ln.match(OWNER_RE);
1011
+ const om = headerRow(ln, lines[i + 1]) ? null : ln.match(OWNER_RE);
962
1012
  if (om && !block.owner) {
963
1013
  const owner = om[1].replace(/[*_`]/g, "").trim();
964
- if (owner && !OWNER_SKIP_RE.test(owner)) block.owner = owner.slice(0, 120);
1014
+ if (owner && !OWNER_SKIP_RE.test(owner) && !OWNER_LABEL_RE.test(om[1].trim())) block.owner = owner.slice(0, 120);
965
1015
  }
966
1016
  const um = ln.match(URL_RE);
967
1017
  if (um && !block.url) block.url = um[0];
@@ -9,7 +9,7 @@
9
9
  //
10
10
  // ── the loss this exists to catch ────────────────────────────────────────────────────────────────────
11
11
  //
12
- // TIKI TWIST and TIKI TROPICS. Both sat in R3's own common-law records and reached no findings list, and
12
+ // WAVO TWIST and WAVO TROPICS. Both sat in R3's own common-law records and reached no findings list, and
13
13
  // nothing anywhere said why. `common-law-receipts.mjs`'s `findGridCandidateOmissions` already reports one
14
14
  // class of this — a hit cell denied by the Negative-results matrix — but it is CELL-granular, and a cell
15
15
  // carries up to eight candidates. This is the per-candidate half.
@@ -39,7 +39,7 @@
39
39
  // ── THE NON-LATIN TRAP IS ANSWERED, NOT AVOIDED ──────────────────────────────────────────────────────
40
40
  //
41
41
  // `normalizeJoinText` folds every non-[A-Za-z0-9] run to a space, so it returns the EMPTY STRING for
42
- // 色度, 冰沙, 提基冰沙, ティキスラッシュ and 티키 슬러시. A mark-keyed trace is structurally blind to
42
+ // 澜珀, 冰沙, 瓦波冰沙, ワボスラッシュ and 와보 슬러시. A mark-keyed trace is structurally blind to
43
43
  // every one of them.
44
44
  //
45
45
  // NOTHING HERE TOUCHES IT. A Han-script candidate joins on its ASCII/percent-encoded URL. A Han-script
@@ -11,7 +11,7 @@
11
11
 
12
12
  import { readFileSync, existsSync, readdirSync } from "node:fs";
13
13
  import { join, basename, isAbsolute } from "node:path";
14
- import { driverDir } from "../shared/driver-dir.mjs"; //
14
+ import { driverDir, driverFileName } from "../shared/driver-dir.mjs"; //
15
15
  import { fileURLToPath } from "node:url";
16
16
  import { STAGES, paths } from "./stages.mjs";
17
17
  import { isEntrypoint } from "../shared/is-entrypoint.mjs"; // — one entry-point test, all spellings
@@ -141,7 +141,7 @@ function outPath(dir, stage, axis) {
141
141
  // last JSONL line of a stage's telemetry, tried at <dir>/<label>.jsonl (snapshot) and <dir>/_driver/<label>.jsonl.
142
142
  function readTel(dir, stage, axis) {
143
143
  const label = labelFor(stage, axis);
144
- for (const p of [join(dir, `${label}.jsonl`), driverDir(dir, `${label}.jsonl`)]) {
144
+ for (const p of [join(dir, driverFileName(`${label}.jsonl`)), driverDir(dir, `${label}.jsonl`)]) {
145
145
  try {
146
146
  const lines = readFileSync(p, "utf8").trim().split("\n").filter(Boolean);
147
147
  if (lines.length) return JSON.parse(lines[lines.length - 1]);
@@ -4,16 +4,16 @@
4
4
  // searched-not-asserted enforcement (validator side). Sister of use-check.mjs (spec 11): a "clean" claim
5
5
  // is sayable ONLY when a search produced it.
6
6
  //
7
- // The bug this closes (live incident: a mark reading as a benign given name one letter off the Sureño
7
+ // The bug this closes (live incident: a mark reading as a benign given name one letter off the Mara
8
8
  // street-gang label): the deterministic grid runs term×platform marketplace cells only — it asks
9
9
  // "who SELLS this name?", never "what does this name MEAN?". A marketplace grid that finds no listing for
10
10
  // the mark is SILENT on the street-gang association; only a MEANING search surfaces it. With no machine slot
11
11
  // for the meaning search, the model fabricated "(None identified — affirmative sweep) … no gang associations".
12
12
  //
13
13
  // Two halves, both PURE (no node imports → tests offline):
14
- // 1. buildConnotationQueries — the driver dictates the meaning queries (mark + near-forms × shapes) VERBATIM
15
- // into grid-spec.json; the perplexity plugin runs them on the general web and records each into the grid
16
- // ledger's extras.pr_risk[] (a recorded query — even with empty results — is the receipt the search ran).
14
+ // 1. meaningAnglesFromMatterContext — the driver dictates the meaning queries, the matter frame's own list,
15
+ // VERBATIM into grid-spec.json; the perplexity plugin runs them on the general web and records each into the
16
+ // grid ledger's extras.pr_risk[] (a recorded query — even with empty results — is the receipt the search ran).
17
17
  // 2. findConnotationViolations — the validator: recorded meaning receipts must be disposed of, per query,
18
18
  // citing the receipt. A PR/reputational section that ASSERTS a clean result must additionally cite a
19
19
  // Connotation-search source line. A searched-clean (recorded queries, empty results) passes.
@@ -28,98 +28,21 @@
28
28
  // section at all matched no block, so DELETING the section passed the gate. Receipts present now means
29
29
  // receipts validated, whatever the document says.
30
30
 
31
- // The meaning query shapes. NOT a banned-word list — these are SEARCH directions ("what does this name mean,
32
- // and to whom?"). Perplexity surfaces foreign-language / gang / slang meanings from English query shapes
33
- // (a benign given name one letter off "Sureño" → the gang label surfaces from "<mark> gang" / "<mark> urban dictionary").
34
-
31
+ // THE SHAPES THE FIXED MEANING SWEEP ASKED, until 2026-09-25: every run searched the first 6 spellings on
32
+ // the five core shapes and the first 8 translated forms on the three below, about 54 web questions whatever
33
+ // the matter. The matter frame decides the sweep now, as it decides the stores, and no query is built from
34
+ // these. They stay so connotationReasonKey still reads the queries of the runs archived before.
35
35
  export const CONNOTATION_SHAPES = ["meaning slang", "gang", "offensive", "urban dictionary", "wikipedia"];
36
-
37
- /**
38
- * Build the dictated connotation queries from the mark + its near-forms (the grid variants). De-dups
39
- * case-insensitively and caps the term count (the meaning search is the mark + a handful of near-forms, not
40
- * the full variant explosion). Returns ["<term> <shape>", …] — run VERBATIM by the plugin's grid program. PURE.
41
- */
42
- export function pickConnotationTerms(terms, maxTerms = 6) {
43
- const seen = new Set();
44
- const picked = [];
45
- for (const t of (terms ?? [])) {
46
- const v = String(t ?? "").trim();
47
- if (!v) continue;
48
- // KEYED THE SAME WAY THE GATES COMPARE. A producer that folds less than its consumer dictates two
49
- // terms the gate can only see as one, and a seat recording either satisfies it for both — the gate
50
- // then passes a query that never ran, which is the fault it exists to catch, inverted.
51
- const k = queryKey(v);
52
- if (seen.has(k)) continue;
53
- seen.add(k);
54
- picked.push(v);
55
- if (picked.length >= maxTerms) break;
56
- }
57
- return picked;
58
- }
59
-
60
- export function buildConnotationQueries(terms, { shapes = CONNOTATION_SHAPES, maxTerms = 6 } = {}) {
61
- const picked = pickConnotationTerms(terms, maxTerms);
62
- const queries = [];
63
- for (const t of picked) for (const s of shapes) queries.push(`${t} ${s}`);
64
- return queries;
65
- }
66
-
67
- // WP-56 B1 — the NON-ENGLISH half of the meaning sweep. The core bucket runs the mark + its first
68
- // near-forms; the manifest's translated/transliterated forms (non-Latin script rows, "丝绸与铁 / 席尔克"
69
- // packed alternates) sat beyond its cap, so a mark could ship with no meaning read in the very scripts the
70
- // manifest committed to search. These forms get their OWN capped bucket with meaning-appropriate shapes —
71
- // the slang / urban-dictionary shapes are Latin-web search directions; a non-Latin or translated form
72
- // wants its meaning and English reading. Which forms exist stays the variants stage's judgment (the
73
- // structured manifest); this stays mechanical.
74
36
  export const CONNOTATION_SHAPES_TRANSLIT = ["meaning", "meaning in english", "offensive meaning"];
75
37
 
76
- // A letter outside the Latin script (property escape, not a codepoint range — punctuation/dashes in a
77
- // Latin term must not read as "non-Latin").
78
- const NON_LATIN_LETTER_RE = /(?![\p{Script=Latin}])\p{L}/u;
79
-
80
- /**
81
- * Build the translit/translation connotation queries from the STRUCTURED variant-manifest model's
82
- * variants[] ({value, category, rationale}): rows typed category "transliteration" (a Latin-script
83
- * translation typed there rides along) plus any variant value carrying a non-Latin letter, whatever its
84
- * category. " / "-packed alternates split into individual forms; forms the core bucket already queries
85
- * (coreTerms, case-insensitive) are dropped; own cap (default 8, beside the core bucket's 6).
86
- * Returns ["<form> <shape>", …] like buildConnotationQueries. PURE.
87
- */
88
- export function buildTranslitConnotationQueries(modelVariants, { shapes = CONNOTATION_SHAPES_TRANSLIT, maxTerms = 8, coreTerms = [] } = {}) {
89
- const seen = new Set((coreTerms ?? []).map((t) => queryKey(t)).filter(Boolean));
90
- const picked = [];
91
- for (const v of (modelVariants ?? [])) {
92
- const value = String(v?.value ?? "").trim();
93
- if (!value) continue;
94
- const isTranslit = String(v?.category ?? "").trim().toLowerCase() === "transliteration" || NON_LATIN_LETTER_RE.test(value);
95
- if (!isTranslit) continue;
96
- for (const form of value.split(" / ")) {
97
- const f = form.trim();
98
- if (!f) continue;
99
- const k = queryKey(f);
100
- if (seen.has(k)) continue;
101
- seen.add(k);
102
- picked.push(f);
103
- if (picked.length >= maxTerms) break;
104
- }
105
- if (picked.length >= maxTerms) break;
106
- }
107
- const queries = [];
108
- for (const t of picked) for (const s of shapes) queries.push(`${t} ${s}`);
109
- return queries;
110
- }
111
-
112
- // ── P2-C (Round-2 §8b) — the DERIVED half of the sweep's scope ─────────────────────────────────────────
113
- // The fixed CONNOTATION_SHAPES floor asks generic search DIRECTIONS ("<mark> offensive") and stays — it is
114
- // not a sensitivities checklist, and on the evidence run it did retrieve the cultural-criticism material.
115
- // What no fixed list can ask is the PER-MATTER angle: the cultural origin a word evokes, the charged history
116
- // of its imagery, the controversy specific to these goods in this market. Deriving those is judgment, so the
117
- // MATTER FRAME authors them — its dictated `Meaning angles:` line (semantic field × market/industry; the
118
- // stage message dictates it and the matterContext validator requires it on fresh runs, `none` allowed as an
119
- // explicit reasoned emptiness). This parser is the mechanical half: extract, sanitize, cap. The queries are
120
- // appended VERBATIM to grid-spec connotation.queries BESIDE the floor, so the per-query identity join
121
- // (findDroppedConnotationQueries) polices their execution exactly like the floor's. NEVER a hardcoded
122
- // sensitivities checklist — the angles are authored per matter; code only carries them.
38
+ // ── THE MATTER FRAME'S LIST IS THE WHOLE MEANING SWEEP (ruled 2026-09-25) ─────────────────────────────────
39
+ // Deriving what a name could mean, and to whom, is judgment, so the MATTER FRAME authors the questions: its
40
+ // `Meaning angles:` line — the mark's semantic field × this matter's market and goods, the languages whose
41
+ // buyers matter, and any slang, gang or offensive reading (the stage message dictates it and the
42
+ // matterContext validator requires it on fresh runs, `none` allowed as an asserted zero). A fixed floor rode
43
+ // beside it and is gone. This parser is the mechanical half: extract and sanitize; the frame decides how
44
+ // many. The queries go VERBATIM into grid-spec connotation.queries, and the per-query identity join
45
+ // (findDroppedConnotationQueries) polices their execution. NEVER a hardcoded sensitivities checklist.
123
46
 
124
47
  // The frame's machine line (mirrors channelsFromMatterContext's "Search channels:" shape), ANCHORED to a
125
48
  // line start: prose that merely MENTIONS `"Meaning angles:" line …` must never read as the line (the
@@ -167,15 +90,15 @@ function sanitizeMeaningAngle(raw) {
167
90
  }
168
91
 
169
92
  /**
170
- * Parse the matter frame's `Meaning angles: <q>; <q>; …` line into sanitized derived queries.
171
- * Semicolon-separated (an angle phrase may contain commas); `none` (the explicit reasoned-emptiness form)
172
- * and a missing line both yield [] — the floor always rides regardless. Sanitization is mechanical only:
173
- * strip WRAPPING quotes/backticks (see sanitizeMeaningAngle — a phrase quote inside the angle survives),
174
- * collapse whitespace, drop empties and over-length entries, dedupe
175
- * case-insensitively (against itself and `alreadyQueried`, so a derived angle never double-dictates a floor
176
- * query), cap the count. Content judgment stays the frame's. PURE.
93
+ * Parse the matter frame's `Meaning angles: <q>; <q>; …` line into the sanitized meaning queries.
94
+ * Semicolon-separated (an angle phrase may contain commas); `none` (the asserted-zero form) and a missing
95
+ * line both yield [], and meaningAnglesAssertedNone tells the two apart: only the first is the frame's
96
+ * decision. Sanitization is mechanical only: strip WRAPPING quotes/backticks (see sanitizeMeaningAngle — a
97
+ * phrase quote inside the angle survives), collapse whitespace, drop empties, dedupe case-insensitively
98
+ * (against itself and `alreadyQueried`). No cap on length or count: the frame's tool refuses an over-long
99
+ * question at the source, every question is run as written, and how many is the frame's call. PURE.
177
100
  */
178
- export function meaningAnglesFromMatterContext(md, { alreadyQueried = [], maxAngles = 8, maxLen = 90 } = {}) {
101
+ export function meaningAnglesFromMatterContext(md, { alreadyQueried = [], maxAngles = Infinity } = {}) {
179
102
  const m = String(md || "").match(MEANING_ANGLES_RE);
180
103
  if (!m) return [];
181
104
  const value = m[1].trim();
@@ -188,7 +111,7 @@ export function meaningAnglesFromMatterContext(md, { alreadyQueried = [], maxAng
188
111
  // quote runs at both ends unconditionally, so `"` sanitized to "" and fell out here; the wrapper rule
189
112
  // keeps it, and a lone `"` dictated as a query is a search nobody asked for that no receipt can ever
190
113
  // match — the failure this change exists to stop. Dropped explicitly rather than as a side effect.
191
- if (!q || !/[\p{L}\p{N}]/u.test(q) || q.length > maxLen) continue;
114
+ if (!q || !/[\p{L}\p{N}]/u.test(q)) continue;
192
115
  const k = queryKey(q);
193
116
  if (seen.has(k)) continue;
194
117
  seen.add(k);
@@ -198,6 +121,12 @@ export function meaningAnglesFromMatterContext(md, { alreadyQueried = [], maxAng
198
121
  return picked;
199
122
  }
200
123
 
124
+ /** Whether the matter frame asserted that no meaning question applies: its `Meaning angles:` line reads `none`. PURE. */
125
+ export function meaningAnglesAssertedNone(md) {
126
+ const m = String(md || "").match(MEANING_ANGLES_RE);
127
+ return Boolean(m) && /^none\b/i.test(m[1].trim());
128
+ }
129
+
201
130
  // The PR / reputational / connotation section heading (the only place the gate polices — it must not flag a
202
131
  // clean note that appears in some other section's prose, mirroring use-check's section-scoping).
203
132
  const SECTION_RE = /reputational|connotation/i;
@@ -1125,7 +1054,7 @@ const RECURRENT_MIN = 4;
1125
1054
  // ── — THE REASON SCALE IS MEASURED HERE AND DELIBERATELY DOES NOT DECIDE ────────────────────────
1126
1055
  // was ruled "recurrence counts independent reasons, not query strings": a dictated query is
1127
1056
  // "<term> <shape>", the sweep asks every shape of every mark variant, so the seven queries reading
1128
- // BIODELFIN/BIODELFINO/BIODOLPHIN/ΒΙΟΔΕΛΦΙΣ/ΔΕΛΦΙΣ/ΜΠΙΟΔΕΛΦΙΣ/Дельфис + "offensive meaning" that all
1057
+ // BIOVELTRYN/BIOVELTRINO/BIOVELTRINE/ΒΙΟΒΕΛΤΡΙΝ/ΒΕΛΤΡΙΝ/ΜΠΙΟΒΕΛΤΡΙΝ/Вельтрин + "offensive meaning" that all
1129
1058
  // return the dictionary definition of the word "offensive" are one reason asked seven ways. That
1130
1059
  // reasoning is sound and the shape is the right way to express it. It is implemented below as
1131
1060
  // connotationReasonKey, and it is RECORDED on every run — but it is NOT what clears the floor.
@@ -1133,7 +1062,7 @@ const RECURRENT_MIN = 4;
1133
1062
  // IT WAS BUILT, MEASURED AGAINST THE THREE PRESERVED RUNS, AND REFUTED BY THEM. Making the floor read
1134
1063
  // the reason scale inverts the signal it exists to read:
1135
1064
  // - The 2026-07 evidence run (test/fixtures/coral-freeze-pr-risk.json, 54 recorded queries): the floor's
1136
- // obligation set goes 7 → 0. `punchdrink.com/articles/problem-with-tiki-bars-cultural-appropriation`
1065
+ // obligation set goes 7 → 0. `punchdrink.com/articles/problem-with-wavo-bars-cultural-appropriation`
1137
1066
  // — THE receipt this floor was invented to force onto the page — is surfaced by four queries that are
1138
1067
  // all shape "offensive", so it scores ONE reason and drops out. The gate stops firing on the run it
1139
1068
  // was designed for.
@@ -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": 104,
14
+ "total": 105,
15
15
  "byStage": {
16
16
  "matter-frame": [
17
17
  "### Intake asks line shape — rendered by the driver from intake_asks[]",
@@ -22,14 +22,15 @@
22
22
  "Class scope & adjacency — classes scoped IN with a one-line reason each, classes deliberately scoped OUT",
23
23
  "Return shape — hand the frame back through record_matter_frame, write NO file, return ONLY a 2-3 line summary",
24
24
  "Scope reasoning — search-wide/cite-narrow, the in-scope-by-reach routes, and the reopen trigger behind each exclusion",
25
- "search_channels — the domains where real use of THIS matter's goods would show"
25
+ "confusable_forms — the forms a buyer could confuse, searched on the stores",
26
+ "search_channels — the domains where real use of THIS matter's goods would show",
27
+ "set_aside — the profile stores and forms the frame does not search, each with its reason"
26
28
  ],
27
29
  "clearance-variants": [
28
30
  "### Scope ledger rows — {Layer, Item, Status: applied|dropped, Reason, Reopen trigger} across variant / field / source / jurisdiction",
29
31
  "Archetype — primary (one of six) + modifiers + one-line reasoning",
30
32
  "Cross-mark themes — shared elements, shared conceptual themes, shared industry-incumbent alerts (multi-mark requests)",
31
33
  "Distinctiveness & registrability read — spectrum placement per market language, descriptive/generic/laudatory/geographic/deceptive/offensive flags, acquired-distinctiveness note",
32
- "Famous-mark flags per element + famous_mark_calls_needed[]",
33
34
  "Formative root: <root> — the shortest distinctive stem a family of marks would share, on its own line",
34
35
  "Output paths + return shape — write the prose manifest and the structured model to the named absolute paths, return ONLY the path + a 2-3 line summary",
35
36
  "Per-jurisdiction sub-queries",
@@ -41,6 +42,7 @@
41
42
  "Verify? ✅ column on every transliteration row of the prose Variants table",
42
43
  "Watchlist union / client-exclusion / 24-owner cap applied by the model before emitting watchlist_owners[]",
43
44
  "elements[] — {value, kind: distinctive|common|saturated-common}",
45
+ "elements[].famous_mark_flag — the elements Step 2's famous-mark check flags",
44
46
  "incumbent_classes[] — the industry-incumbent alert's Nice classes",
45
47
  "variants[].category — one of core|phonetic|visual|transliteration|numeric|composite|other",
46
48
  "variants[].rationale — one line per variant",
@@ -73,13 +75,12 @@
73
75
  "register-unit": [
74
76
  "`CROSS-CHECK REQUIRED: <what> — <why>` — the check that is needed and why",
75
77
  "`CROSS-CHECK REQUIRED:` exact line shape — prefix, em-dash, mark in CAPS, own line",
76
- "escalation judgement — the CROWD BOUND: attempt each dangerous-category slice once class-scoped, gate on the RESULT, stop terminal on a crowd, and write a block only for the distinctive anchor",
78
+ "escalation judgement — the CROWD BOUND: attempt each dangerous-category slice once class-scoped, gate on the RESULT, decide on a crowd, and write a block only for the distinctive anchor",
77
79
  "final return — the absolute output path plus queries-enumerated count, incomplete-block count, records carried forward",
78
80
  "proposal `romanization` beside a single non-Latin term — plain-ASCII Latin form, space-separated syllables",
79
81
  "proposal class scoping — pass nice_classes:[...] AND in_scope_classes:[...] on every proposal/call",
80
82
  "proposal envelope — predicate enum {exact, default, wildcard, phonetic, owner}, term vs terms, rationale",
81
83
  "reading the band back and judging it — keep proposing until the axis's dangerous named band is covered",
82
- "recall carry-or-justify — for each prior-confirmed conflict, a reasoned drop row written over the fetched record (primary-sweep only)",
83
84
  "supplemental proposals — WHICH queries the manifest/frame warrant beyond the dictated set",
84
85
  "the dictated entry list — qid, predicate, terms, owner, nice_classes, regions, when-guard, expected_kind, covered_by"
85
86
  ],