clearotron 0.2.2 → 0.2.3

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 (118) hide show
  1. package/.env.example +28 -0
  2. package/.nvmrc +1 -1
  3. package/INSTALL.md +13 -11
  4. package/README.md +11 -16
  5. package/THIRD-PARTY-NOTICES.md +1 -1
  6. package/bin/clearotron.mjs +22 -0
  7. package/bin/example.mjs +4 -0
  8. package/bin/onboard.mjs +195 -19
  9. package/bin/start.mjs +154 -8
  10. package/build-info.json +2 -2
  11. package/demo/full-country-search/run/_driver/framework.json +1 -1
  12. package/demo/global-preliminary-search/run/_driver/framework.json +1 -1
  13. package/demo/knockout-search/run/_driver/framework.json +1 -1
  14. package/demo/multi-country-focus-search/run/_driver/framework.json +1 -1
  15. package/docs/E2E.md +5 -5
  16. package/docs/PORTAL.md +2 -2
  17. package/docs/README.md +5 -4
  18. package/docs/SECURITY.md +17 -0
  19. package/docs/architecture/04-configuration-reference.md +2 -0
  20. package/docs/architecture/05-config-governance.md +14 -0
  21. package/docs/architecture/05-customer-profiles.md +2 -2
  22. package/docs/configuration.md +4 -3
  23. package/driver/CHANGELOG.md +56 -0
  24. package/driver/common-law-receipts.mjs +1 -1
  25. package/driver/config-staleness.mjs +1 -1
  26. package/driver/dev-portal.mjs +2 -2
  27. package/driver/driver.config.mjs +1 -1
  28. package/driver/engine/cli-version.mjs +103 -0
  29. package/driver/enqueue-schema.mjs +8 -8
  30. package/driver/feedback-store.mjs +1 -1
  31. package/driver/findings-model.mjs +52 -2
  32. package/driver/flag-snapshot.mjs +20 -0
  33. package/driver/framework.mjs +1 -1
  34. package/driver/gateway.mjs +18 -1
  35. package/driver/package.json +3 -3
  36. package/driver/party-facts.mjs +2 -2
  37. package/driver/phase0.mjs +1 -1
  38. package/driver/pipeline.mjs +5 -5
  39. package/driver/plain-register.mjs +120 -0
  40. package/driver/portal-access.mjs +1 -1
  41. package/driver/portal-config-view.mjs +40 -1
  42. package/driver/portal-families.mjs +1 -1
  43. package/driver/portal-report.mjs +1 -1
  44. package/driver/portal-service.mjs +26 -7
  45. package/driver/portal-upstream.mjs +2 -2
  46. package/driver/predelivery-lint.mjs +121 -1
  47. package/driver/profile-page.html +10 -2
  48. package/driver/profile-service.mjs +4 -4
  49. package/driver/profiles/README.md +3 -3
  50. package/driver/profiles.mjs +144 -12
  51. package/driver/publish/index.mjs +2 -2
  52. package/driver/publish/knockout.mjs +7 -1
  53. package/driver/publish/render-knockout.mjs +453 -118
  54. package/driver/publish/render.mjs +2 -2
  55. package/driver/reference-strip-signatures.mjs +20 -0
  56. package/driver/search-policy.mjs +2 -2
  57. package/driver/skills/blind-frame/SKILL.md +1 -1
  58. package/driver/skills/knockout-assess/SKILL.md +84 -0
  59. package/driver/skills/matter-frame/SKILL.md +6 -6
  60. package/driver/skills/matter-frame/watchlist-reference.md +2 -2
  61. package/driver/skills/prelim-common-law/SKILL.md +1 -1
  62. package/driver/skills/prelim-search/SKILL.md +1 -1
  63. package/driver/skills/prelim-search/report-prose.md +82 -0
  64. package/driver/skills/prelim-search/risk-framework-demo.manifest.json +1 -1
  65. package/driver/skills/prelim-search/risk-framework-triage.manifest.json +1 -1
  66. package/driver/skills/prelim-search/risk-framework.manifest.json +1 -1
  67. package/driver/skills/prelim-search/synthesis-rules.md +1 -1
  68. package/driver/skills/prelim-variants/SKILL.md +3 -3
  69. package/driver/stages.mjs +4 -4
  70. package/driver/suite-census.json +134 -38
  71. package/driver/unit-inventory.mjs +184 -27
  72. package/driver/verify.mjs +1 -1
  73. package/driver/whatif-memo-run.mjs +26 -4
  74. package/driver/whatif-memo.mjs +19 -1
  75. package/examples/README.md +2 -2
  76. package/examples/grants.example.json +6 -7
  77. package/mcp-server/CHANGELOG.md +2 -0
  78. package/mcp-server/lib/scrub.mjs +1 -1
  79. package/mcp-server/lib/whatif.mjs +2 -2
  80. package/mcp-server/package.json +1 -1
  81. package/mcp-server/packs/ops/EXAMPLES.md +2 -2
  82. package/mcp-server/server.mjs +1 -1
  83. package/package.json +28 -10
  84. package/portal-ui/dist/assets/{index-Ct3iQtZ2.css → index-1ziUJX1E.css} +43 -2
  85. package/portal-ui/dist/assets/{index-BUGgiYtF.js → index-CEYvXdqH.js} +226 -49
  86. package/portal-ui/dist/index.html +2 -2
  87. package/portal-ui/package.json +1 -1
  88. package/providers/_shared/term-shape.mjs +1 -1
  89. package/providers/oauth-mcp-bridge/CHANGELOG.md +2 -0
  90. package/providers/oauth-mcp-bridge/package.json +1 -1
  91. package/providers/uspto-local/src/index-store.js +2 -2
  92. package/scripts/citation-line-check.mjs +121 -2
  93. package/scripts/env-classify.mjs +67 -3
  94. package/scripts/live-surface-check.mjs +38 -1
  95. package/scripts/nightly-notice.mjs +0 -2
  96. package/scripts/preinstall-node-check.mjs +24 -0
  97. package/scripts/strip-tracker-citations.mjs +29 -6
  98. package/scripts/test-run.mjs +16 -0
  99. package/shared/anon-overlay.mjs +3 -3
  100. package/shared/customer-store.mjs +1 -1
  101. package/shared/invocation.mjs +28 -0
  102. package/shared/names-in-force.mjs +2 -0
  103. package/shared/node-floor.mjs +97 -0
  104. package/shared/staff-domain.mjs +154 -0
  105. package/shared/store-in-repo.mjs +23 -2
  106. package/skills/clearotron-ops/SKILL.md +1 -1
  107. package/driver/recipes/README.md +0 -49
  108. package/driver/skills/prelim-search/risk-framework-aurora.manifest.json +0 -19
  109. package/driver/skills/prelim-search/risk-framework-aurora.md +0 -77
  110. package/driver/skills/prelim-search/risk-framework-zephyr.manifest.json +0 -14
  111. package/driver/skills/prelim-search/risk-framework-zephyr.md +0 -33
  112. package/driver/skills/prelim-search/worked-examples-aurora.md +0 -10
  113. package/driver/skills/prelim-search/worked-examples-zephyr.md +0 -10
  114. package/scripts/ai-page-render-check.mjs +0 -532
  115. package/scripts/clearances-render-check.mjs +0 -480
  116. package/scripts/composer-render-check.mjs +0 -1027
  117. package/scripts/home-render-check.mjs +0 -526
  118. package/scripts/portal-lifecycle-check.mjs +0 -639
@@ -456,8 +456,8 @@ function meter(name, entry) {
456
456
  // (emitted by the report-card stage, parsed into card.meta.ord) and the driver already orders +
457
457
  // provenance-checks cards by it — so match on THAT exact, injective key. The old fuzzy owner/mark
458
458
  // containment collapsed distinct findings onto one card (`.find()` first-match): two findings sharing
459
- // a mark, or whose mark is a substring of another's ("aura" ⊂ "aurora"), all matched the first card,
460
- // rendering one card's prose under every finding (the PETCARY "02 On-field conflicts" duplication). The
459
+ // a mark, or whose mark is a substring of another's ("nova" ⊂ "novadex"), all matched the first card,
460
+ // rendering one card's prose under every finding (the "02 On-field conflicts" duplication). The
461
461
  // fuzzy match is kept ONLY as a fallback for legacy artifacts whose cards predate the `- ord:` line.
462
462
  function matchCard(finding, cards) {
463
463
  const list = cards || [];
@@ -30,6 +30,26 @@ export const SIGNATURES = [
30
30
  // the strip's residue always leaves whitespace after the hyphen.
31
31
  re: /\bpre-\s+[a-z]/,
32
32
  },
33
+ {
34
+ name: "a parenthesis opening on a comma",
35
+ // THE THIRD RESIDUE, and the floor could not see it for two years because the first two are
36
+ // WORD-level — a bare possessive, a dangling `pre-` — and this one is PUNCTUATION. It looks like
37
+ // ordinary code to a reader scanning for broken English, which is exactly why a census built from
38
+ // the shapes somebody had noticed reads like a census of the whole problem.
39
+ //
40
+ // Where the removed reference was the entire content of a parenthesis, nothing is left but `(,)`.
41
+ // Where it opened one, the sentence now begins on a comma: `(, 2026-08-21)`. Two ran off the end of
42
+ // their line.
43
+ //
44
+ // THE LOOKAHEAD IS THE WHOLE PRECISION AND IT IS DERIVED, NOT GUESSED. `(,` also occurs in code —
45
+ // inside a character class (`[(,=:…]`, `[;{\s(,]`) and in an alternation (`(,|$)`) — where those two
46
+ // characters are syntax rather than damage. Every code instance is followed by a regex metacharacter
47
+ // and no residue instance is, so the exclusion is `|`, `]` and `=` rather than a list of files that
48
+ // would go stale. Measured against the tree as it stood before the repair: 123 matches, which is the
49
+ // residue count the finding reported, and 0 after it. A signature that reproduces the number it was
50
+ // derived from is one that is reading the same thing.
51
+ re: /\(\s*,(?![|\]=])/,
52
+ },
33
53
  ];
34
54
 
35
55
  // THE RULE'S OWN DEFINITION IS THE ONLY EXEMPTION, and it is named rather than pattern-matched.
@@ -877,7 +877,7 @@ export const RECIPE_KEYS = [
877
877
  * The saved-search SCOPE block — where the machinery points, as against `base`/`components`, which
878
878
  * choose which machinery runs.
879
879
  *
880
- * This is what makes a saved search a saved SEARCH rather than a saved depth. "Zephyr Beverages knockouts — US
880
+ * This is what makes a saved search a saved SEARCH rather than a saved depth. "Drinks knockouts — US
881
881
  * focus" is a label over exactly this: base `knockout`, scope `{jurisdictions:["US"]}`. Without it a
882
882
  * saved search could only ever restate a level that is already one click away, which is why the recipe
883
883
  * store existed for weeks with nothing worth putting in it.
@@ -1066,7 +1066,7 @@ let recipeCache = null;
1066
1066
  * THE STORE IS NAMED, NEVER GUESSED. `CLEAROTRON_RECIPES_DIR` unset means this deployment has no saved
1067
1067
  * searches — not that it should use the ones bundled with the source.
1068
1068
  *
1069
- * `driver/recipes/` ships synthetic demos for two FICTIONAL customers (aurora, zephyr) so the dev
1069
+ * `driver/recipes/` ships synthetic demos for two test accounts so the dev
1070
1070
  * cockpit and the tests have something to render. It used to be the fallback when the env var was
1071
1071
  * unset, which is a foot-gun that only fires in production: the real deployment does not set the
1072
1072
  * variable, so switching saved searches on there would have surfaced invented customers inside the
@@ -27,7 +27,7 @@ Decompose the mark into its element(s) and name the **dominant element** (the sp
27
27
 
28
28
  - **Drop** characters: shorten the element (VELTRIN → VELTRI). A dropped letter is the commonest missed-cluster cause — the shorter root is its own crowded field.
29
29
  - **Add** characters / **composite**: the element living inside a larger mark (DELPHI Diagnostics, Osler Delphi, Delphic). A composite that shares your dominant element is on the board.
30
- - **Phonetic / homophone**: sound-alikes (ZEPHYR / ZEFFYR / ZEPHUR).
30
+ - **Phonetic / homophone**: sound-alikes (PHAROLIS / FAROLIS / PHAROLLIS). Carry the `ph`/`f` pair in particular: it sounds identical, files under a different letter, and survives no letter-distance measure — which is the whole reason this class is separate from the two above.
31
31
  - **Neighbour**: a one-keystroke real-word or famous-mark neighbour (CHROME on a NOVAPULSE clearance). A famous neighbour is carried for diligence even when off-field.
32
32
 
33
33
  For each, give the value, the direction, and one line of rationale.
@@ -119,6 +119,90 @@ Measured tone only — no "extremely difficult", "most dangerous", "massive", "e
119
119
  colour communicates urgency. No quantitative claims from research ("major streaming hit", never
120
120
  "294M streams"; "significant market presence", never revenue figures).
121
121
 
122
+ ## Plain language — the two-register rule
123
+
124
+ The report goes to a lawyer who layers advice on top, and that lawyer's client reads the same page. The
125
+ band, the summary, the basis line and the one-liners are the whole product for the second reader. They
126
+ were the hardest lines on the page.
127
+
128
+ **Everything a reader sees before opening a fold is written for the client's business owner.** Ordinary
129
+ nouns, one idea per sentence, no sentence over 25 words, the conclusion first. On this product that is:
130
+ `chunkSummary`, `basis`, `factors`, `counterFactors`, `mitigation`, every finding's `net`, every
131
+ `registerReads[].read`, the `purpleNotes`, and `standardCaveats`.
132
+
133
+ **Inside a fold the lawyer's vocabulary is allowed** where a plain word would lose precision — a
134
+ finding's `basis`, the provenance, the long `assessment`. Even there, a term is explained the first time
135
+ it appears on the page.
136
+
137
+ The rule is the test, not the list. Ask of every noun: *does the reader already own this word?* These
138
+ are worked examples of the one failure, not its boundary:
139
+
140
+ | Instead of | Write |
141
+ |---|---|
142
+ | proprietor | owner |
143
+ | subsisting | live |
144
+ | senior | earlier, or came first |
145
+ | specification | goods list |
146
+ | DELPH-formative | names built on DELPH- |
147
+ | prevail | win |
148
+ | citable prior rights | earlier marks the office can raise against you |
149
+ | vulnerable to a non-use attack | could be cancelled for not being used |
150
+ | on the record as it stands | on what we found |
151
+ | the marks-and-goods comparison | same name, same goods |
152
+ | the confusion comparison meets on every limb | same name, same goods, same shops |
153
+ | belt-and-braces classes | extra classes |
154
+ | dispatch, instructed | the request, what was asked |
155
+ | lane | class, or channel — whichever you mean |
156
+ | chunk | never; say the batch, or these names |
157
+
158
+ The target is the level of these, each the standard for its line:
159
+
160
+ > **Basis.** "ORBIT is already the name of two satellite-tracking apps on the same app stores, and of an
161
+ > established satellite-communications company. Any of them would likely win a dispute over this name
162
+ > for this software. The word is a weak mark for these goods, which is why this is High and not Very
163
+ > High."
164
+
165
+ > **A finding's `net`.** "Same name, same goods, and their filing came first in every territory we
166
+ > searched. We see no argument against it."
167
+
168
+ > **The batch opener.** "One name screened: ORBIT, rated High."
169
+
170
+ **Shortening by dropping the reason is not the fix.** The "why" stays, in plain words. A visible
171
+ line that is short because it no longer says why is worse than the long one it replaced, and it
172
+ passes every length rule on this page while telling the reader less.
173
+
174
+ **Say it once.** A caveat, a limit or a finding belongs in one place. Four `standardCaveats` that each
175
+ restate "this is not a clearance" are one caveat and three repetitions — and the report's scope block
176
+ already carries that sentence in the renderer's own words, so a caveat repeating it is dropped before
177
+ the reader sees it. Write the one thing the scope block does not say, or write none.
178
+
179
+ **A note is verb-first, one line, and about one of two things.** Write each `purpleNotes` entry as an
180
+ instruction to the reviewing lawyer, opening on the verb. Every note is about the REQUEST — what was
181
+ asked, and whether it holds together — or about the NAME. The report prints the first kind at the top,
182
+ above the conflicts, and the second under that name's cards, so a note that mixes the two lands in one
183
+ place and reads oddly in it. Write two notes.
184
+
185
+ A note about the request NAMES the request in the note — "the request", or "what we were asked". That is
186
+ not a formality: the report sorts the two kinds by what the note talks about, so a request note that
187
+ never mentions the request is filed under the name and prints in the wrong place.
188
+
189
+ > "Check the request. The client is described as a beverages and supplements business, but we were asked
190
+ > to screen satellite-tracking software in Class 9. We screened the software. If beverages is the real
191
+ > business, this screen looked at the wrong market."
192
+
193
+ > "Ask the client whether it already uses ORBIT. The request does not say, and the client's own earlier
194
+ > use would change the picture."
195
+
196
+ A note about the name opens on what the lawyer should do about the name:
197
+
198
+ > "Pull EG Tech's full goods list at clearance. It is the record most likely to change the picture in
199
+ > either direction."
200
+
201
+ > "Search Classes 42 and 38 in their own right. Both carry live ORBIT filings, not spillover from
202
+ > Class 9."
203
+
204
+ A note that restates a finding already on the page is not a note. Cut it.
205
+
122
206
  ## The per-mark opening read (`assessment`)
123
207
 
124
208
  Each mark's report is delivered on its own, and `assessment` is the first thing its reader meets —
@@ -75,11 +75,11 @@ You do not write a file. Hand the frame back by calling `record_matter_frame`; t
75
75
 
76
76
  ### Scope jurisdictions — search wide, cite narrow (instructed scope honored; worldwide / brand-signalled scope leans wide + discloses)
77
77
 
78
- Two scopes live here, and the doctrine is **narrow at citation, never silently at search**: the **search scope** (where the register sweep looks) must be wide enough to catch the conflict; the **citation scope** (what the report flags and ranks) is where rights legally matter. The failure to avoid is the PETCARY miss — *silently* pinning a globally-signalling brand to one country, so the real EU/UK/US conflict is never even searched. Derive both from THIS matter; a customer profile selects the **risk framework** only — it never sets territory.
78
+ Two scopes live here, and the doctrine is **narrow at citation, never silently at search**: the **search scope** (where the register sweep looks) must be wide enough to catch the conflict; the **citation scope** (what the report flags and ranks) is where rights legally matter. The failure to avoid is the pinned-scope miss — *silently* pinning a globally-signalling brand to one country, so the real EU/UK/US conflict is never even searched. Derive both from THIS matter; a customer profile selects the **risk framework** only — it never sets territory.
79
79
 
80
80
  - **Instructed scope is honored (narrow or wide).** When the client names territories — a list, or "X only" — that is the scope for BOTH search and citation; do not override a client who genuinely wants a narrow scope (a sweep of a country with no instructed interest and no reach is wasted). The profile's `defaultJurisdictions`, when present, is the *customer's own standing instruction* — treat it like an instructed set.
81
- - **Worldwide, or the brand's own signals point broader → the SEARCH widens (it never silently narrows).** When the instruction is worldwide, OR the brand signals broader use — a global gTLD (a `.io`/`.com` launch), an investor / expansion announcement, stated target markets, a genuinely global customer base — the **search set includes the major markets (US / EU / UK / CN / JP) plus the signalled markets**. Decide which to actually cite and rank at **selection** (where rights matter), and **disclose** any material market you could not fully cover (a `coverage-limited` row — never a silent clean). The one move forbidden: quietly pinning an unpinned, globally-signalling brand to a single country (the PETCARY miss). **The cost guard stays:** an abstract *"the sector tends to be global"* is **not, by itself, the widener** — the widener is a CONCRETE signal (a worldwide instruction, or the brand's own global footprint), so a deliberately local matter stays local.
82
- - **Genuinely unpinned, no global signal → reason the likely scope, LABEL it inferred, and DISCLOSE it.** Read the product / customer base / channels and scope to where the brand plausibly operates; never silently default to the requester's home country (that is the PETCARY mechanism). Where reach is plausibly broad, lean wide; where clearly local, scope local — either way set `Scope basis: inferred` and record a one-line `Scope assumption:` so a narrowing is visible and the coverage gate can clamp + disclose it.
81
+ - **Worldwide, or the brand's own signals point broader → the SEARCH widens (it never silently narrows).** When the instruction is worldwide, OR the brand signals broader use — a global gTLD (a `.io`/`.com` launch), an investor / expansion announcement, stated target markets, a genuinely global customer base — the **search set includes the major markets (US / EU / UK / CN / JP) plus the signalled markets**. Decide which to actually cite and rank at **selection** (where rights matter), and **disclose** any material market you could not fully cover (a `coverage-limited` row — never a silent clean). The one move forbidden: quietly pinning an unpinned, globally-signalling brand to a single country (the pinned-scope miss). **The cost guard stays:** an abstract *"the sector tends to be global"* is **not, by itself, the widener** — the widener is a CONCRETE signal (a worldwide instruction, or the brand's own global footprint), so a deliberately local matter stays local.
82
+ - **Genuinely unpinned, no global signal → reason the likely scope, LABEL it inferred, and DISCLOSE it.** Read the product / customer base / channels and scope to where the brand plausibly operates; never silently default to the requester's home country (that is the pinned-scope mechanism). Where reach is plausibly broad, lean wide; where clearly local, scope local — either way set `Scope basis: inferred` and record a one-line `Scope assumption:` so a narrowing is visible and the coverage gate can clamp + disclose it.
83
83
  - **In scope by reach.** A territory also counts when a right is **legally effective in an instructed territory by any route** — filed there; an international (Madrid) registration **designating** it; a regional/supranational right covering it (an EUTM covers every EU member; an instructed EU member is covered by an EUTM); a treaty / priority effect. Tag such a territory `in-scope-by-reach` and name the route. Reason the route from the right — never assume it.
84
84
  - **Excluded.** Territories you considered and set aside — neither instructed, nor brand-signalled, nor a major on a worldwide matter — name each, the reason, and a **reopen trigger** (a right or signal pointing there surfaces). A hit effective ONLY outside the scope set is out of *citation* scope (drop it from findings); but a market the brand actually signals belongs in the SEARCH set, not here.
85
85
  - **Primary vs only.** If the instruction names territories of "primary" / "first" interest WITHOUT "only", treat the named ones as the citation core but **let the search lean wide** to the majors / brand-signalled markets (the "primary" wording signals more may matter), and **record the assumption** — a one-line `Scope assumption: <text>`. (A single clarifying question is acceptable instead; the recorded default never blocks the run.)
@@ -88,7 +88,7 @@ Two scopes live here, and the doctrine is **narrow at citation, never silently a
88
88
 
89
89
  *Worked illustrations:*
90
90
  *— Instructed "Switzerland and the EU, only" → search CH + the EUTM/EU layer (+ any Madrid designation reaching them); majors beyond (CN/JP/US/UK) are out of scope — an honored narrow instruction (`Scope basis: instructed`).*
91
- *— A brand on a `.io` domain with an investor deck naming a US/EU launch and no explicit territory list → lean wide: search US/EU/UK/CN/JP + the named launch markets; cite where rights matter; disclose any gap (`Scope basis: inferred`). This is the PETCARY fix — do not pin it to CH.*
91
+ *— A brand on a `.io` domain with an investor deck naming a US/EU launch and no explicit territory list → lean wide: search US/EU/UK/CN/JP + the named launch markets; cite where rights matter; disclose any gap (`Scope basis: inferred`). This is the pinned-scope fix — do not pin it to CH.*
92
92
  *— A local artisan, national-only product, no global signal → scope the home market, `Scope basis: inferred`, `Scope assumption:` recorded — narrow but disclosed, never silent.*
93
93
 
94
94
  ### Off-field sectors
@@ -111,7 +111,7 @@ Two scopes live here, and the doctrine is **narrow at citation, never silently a
111
111
  ### Sector-convergence flags
112
112
  - Sectors off-field today where commercial trajectories may bring them adjacent
113
113
  - Brief reason
114
- - Example: "Aurora Interactive's broader AI / cloud AI narrative could theoretically bring vector-DBs adjacent to game-dev tooling, but Aurora dev kit is specifically game-tooling not LLM-tooling; convergence path exists but weak"
114
+ - Example: "Foxglade Interactive's broader AI / cloud AI narrative could theoretically bring vector-DBs adjacent to game-dev tooling, but Foxglade dev kit is specifically game-tooling not LLM-tooling; convergence path exists but weak"
115
115
 
116
116
  ### Machine lines the task message dictates
117
117
 
@@ -132,7 +132,7 @@ driver's fixed query shapes:
132
132
 
133
133
  ## Reasoning posture
134
134
 
135
- - When in doubt about an **off-field / adjacency** decision, INCLUDE rather than exclude — matter-frame should not silently filter a *field*; downstream review (`placement-inquiry`) can deprioritise later with reasoning. **Jurisdiction scope follows its own rule (see *Scope jurisdictions*): search wide, cite narrow.** An instructed-narrow scope is honored; a worldwide or brand-signalled scope widens the SEARCH to the majors + signalled markets; an inferred scope is labelled and disclosed. Do not silently pin a globally-signalling brand to one country (the PETCARY miss), and do not widen a deliberately-local matter on abstract "the sector is global" reasoning (a wasted sweep).
135
+ - When in doubt about an **off-field / adjacency** decision, INCLUDE rather than exclude — matter-frame should not silently filter a *field*; downstream review (`placement-inquiry`) can deprioritise later with reasoning. **Jurisdiction scope follows its own rule (see *Scope jurisdictions*): search wide, cite narrow.** An instructed-narrow scope is honored; a worldwide or brand-signalled scope widens the SEARCH to the majors + signalled markets; an inferred scope is labelled and disclosed. Do not silently pin a globally-signalling brand to one country (the pinned-scope miss), and do not widen a deliberately-local matter on abstract "the sector is global" reasoning (a wasted sweep).
136
136
  - Be concrete about WHY for each item. A senior lawyer reading matter-context.md should be able to disagree with specific items and see exactly where your reasoning broke — not just see a conclusion.
137
137
  - If you find yourself rating something as in-lane that a senior lawyer would obviously drop, surface the tension rather than smoothing it.
138
138
 
@@ -47,6 +47,6 @@ watchlists come from in any case, and is why this file was never authority.
47
47
 
48
48
  ## Cross-sector
49
49
 
50
- - **Aurora Interactive** — `major_brand_owners`. Broad AURORA / NEBULA / VANGUARD families;
51
- on an Aurora Interactive-adjacent matter watch the relevant family token and name
50
+ - **Foxglade Interactive** — `major_brand_owners`. Broad FOXGLADE / NEBULA / VANGUARD families;
51
+ on a Foxglade Interactive-adjacent matter watch the relevant family token and name
52
52
  ecosystem partners with their partnership context rather than as bare conflicts.
@@ -173,7 +173,7 @@ For game-title rows, the `developer_of_record` and `publisher_of_record` columns
173
173
  | Finding | Source / Platform | URL | Notes |
174
174
  |---|---|---|---|
175
175
  | Sony "Pulse Elevate" portfolio | Sony products | https://... | Sony uses "Elevate" in audio products; flagged for prelim-register cross-check |
176
- | Aurora "Borealis" console "Raise Your Play" tagline (prior usage) | Aurora Interactive marketing | https://... | Client's own prior use — note as supportive |
176
+ | Foxglade "Borealis" console "Raise Your Play" tagline (prior usage) | Foxglade Interactive marketing | https://... | Client's own prior use — note as supportive |
177
177
 
178
178
  ### PR / reputational risk
179
179
 
@@ -259,7 +259,7 @@ The driver runs the delivery stage on **every** completed run — the search is
259
259
 
260
260
  **Methodology lives in the Excel Methodology tab, NOT in the email body.** Do not include paragraph-length descriptions of search approach, sub-skills invoked, variants generated, etc. in the client-facing email. That bloat is what the render-layer split fixes.
261
261
 
262
- **The email is a COVER NOTE; the HTML report is the single master document (wp50).** The house default for every customer: the email carries only the headline risk tier, the report link, and any surviving scope/verdict/handling flags plus reviewer's open questions — no findings body, no client-voice prose, no hand-holding preamble. Client-voice findings live in the **HTML report** (the one surface the client reads). The last exception — Aurora's review-table overlay (`delivery.email:'table'`) — is DELETED as of 2026-07-28, along with the knob that asked for it: a second full findings surface in the mail was the seam that let per-conflict tiers diverge from the report, and on the knockout lane it shipped internal purple notes over the wire. A lawyer who needs a bespoke, forwardable client mail asks the assistant to draft one from the run's `report-data.json` — formatting is judgement, and it belongs where judgement lives, not in a profile enum. The operative delivery spec is [delivery-contract.md](delivery-contract.md) + the driver's `report-synthesis` stage. (The `client-summary` stage, which re-voiced the findings into a separate client-facing document, is RETIRED as of 2026-08-01 — the one report is the one surface.)
262
+ **The email is a COVER NOTE; the HTML report is the single master document (wp50).** The house default for every customer: the email carries only the headline risk tier, the report link, and any surviving scope/verdict/handling flags plus reviewer's open questions — no findings body, no client-voice prose, no hand-holding preamble. Client-voice findings live in the **HTML report** (the one surface the client reads). The last exception — a customer's review-table overlay (`delivery.email:'table'`) — is DELETED as of 2026-07-28, along with the knob that asked for it: a second full findings surface in the mail was the seam that let per-conflict tiers diverge from the report, and on the knockout lane it shipped internal purple notes over the wire. A lawyer who needs a bespoke, forwardable client mail asks the assistant to draft one from the run's `report-data.json` — formatting is judgement, and it belongs where judgement lives, not in a profile enum. The operative delivery spec is [delivery-contract.md](delivery-contract.md) + the driver's `report-synthesis` stage. (The `client-summary` stage, which re-voiced the findings into a separate client-facing document, is RETIRED as of 2026-08-01 — the one report is the one surface.)
263
263
 
264
264
  Structure (content shared across templates):
265
265
 
@@ -19,6 +19,88 @@ writes lives in that stage's own file — the finding sentence and the grouped r
19
19
  [synthesis-rules.md](synthesis-rules.md), the card and section shape in
20
20
  [delivery-contract.md](delivery-contract.md). What is below is general and is stated only here.
21
21
 
22
+ ## Two registers, and the reader of the first one is not a lawyer
23
+
24
+ **The reader is a lawyer who layers advice on top — and that lawyer's client reads the same page.** The
25
+ band, the summary, the basis line and the one-liners are the whole product for the second reader, and
26
+ they were the hardest text on it: single sentences of seventy-odd words in the vocabulary of the
27
+ profession.
28
+
29
+ So the page has two registers, and which one a line is written in is decided by whether the reader has
30
+ to open something to see it.
31
+
32
+ **DEFAULT-VISIBLE TEXT CARRIES NO LEGAL OR ENGINE VOCABULARY.** Default-visible means anything a reader
33
+ meets before opening a fold: the summary and the opening line, the basis for the rating, the points for
34
+ and against and what would soften them, the one sentence under each name, the four answers, the
35
+ reviewer's notes, the caveats, the coverage rows and the lines saying what needs a decision. Ordinary
36
+ nouns. One idea per sentence. **No visible sentence over 25 words.** The conclusion first.
37
+
38
+ Which stored fields those are is not prose's business and is not listed here — `DEFAULT_VISIBLE_FIELDS`
39
+ in `driver/plain-register.mjs` names them for both products, in one place, so a field renamed once does
40
+ not leave a doctrine file quietly describing a shape that no longer exists.
41
+
42
+ **INSIDE A FOLD THE PROFESSION'S VOCABULARY IS ALLOWED**, where a plain word would lose precision: the
43
+ card's detail paragraph, provenance, the drill bullets. Even there a term is glossed at first use on the
44
+ page. Nothing is removed from the drill and the workbook is unchanged.
45
+
46
+ ### The swaps, as worked examples rather than a banned list
47
+
48
+ A list of forbidden words is not the mechanism and must never become one. These are what the plain form
49
+ sounds like; the instruction is always to rewrite the sentence, never to substitute the word and leave
50
+ the rest of a lawyer's sentence standing around it.
51
+
52
+ | written for a lawyer | what the reader needs |
53
+ |---|---|
54
+ | proprietor | owner |
55
+ | subsisting | live |
56
+ | senior | earlier, or came first |
57
+ | specification | goods list |
58
+ | DELPH-formative | names built on DELPH- |
59
+ | prevail | win |
60
+ | citable prior rights | earlier marks the office can raise against you |
61
+ | vulnerable to a non-use attack | could be cancelled for not being used |
62
+ | on the record as it stands | on what we found |
63
+ | the marks-and-goods comparison | same name, same goods |
64
+ | the confusion comparison meets on every limb | same name, same goods, same shops |
65
+ | belt-and-braces classes | extra classes |
66
+ | dispatch, instructed | the request, what was asked |
67
+ | lane | class, or channel — whichever is meant |
68
+ | chunk | never; it is an engine word |
69
+
70
+ ### The standard, in full sentences
71
+
72
+ A basis line, rewritten: *"ORBIT is already the name of two satellite-tracking apps on the same app
73
+ stores, and of an established satellite-communications company. Any of them would likely win a dispute
74
+ over this name for this software. The word is a weak mark for these goods, which is why this is High and
75
+ not Very High."*
76
+
77
+ An answer, rewritten: *"Blocked. An identical earlier mark for the same goods stops registration in
78
+ Switzerland, the EU and the US. Below that, earlier DELPH- marks in EU class 5 and US class 42 can be
79
+ raised against the application."*
80
+
81
+ A card's detail — allowed the fold's vocabulary, still written plainly: *"Same name, same goods, and
82
+ their Swiss filing came first in every territory we searched. We see no argument against it."*
83
+
84
+ A note, rewritten: *"Ask the client whether it already uses ORBIT. Its own earlier use would change the
85
+ picture and is not reflected here."*
86
+
87
+ **Shortening by dropping the reason is not the fix.** The "why" stays, in plain words. A visible line
88
+ that is short because it no longer says why is worse than the long one it replaced.
89
+
90
+ ### What reads this, and what does not
91
+
92
+ **No delivery gate fails on any of it, and no report is ever marked for it.** The reviewing stage that
93
+ already rejects engine vocabulary applies this test to the default-visible fields and hands back a
94
+ rewrite, in the same pass and the same voice it uses for everything else. A hit is a sentence rewritten.
95
+ It is never a disclosure to the client, never a run failure, and it moves no band, no evidence and
96
+ nothing that is searched — this is presentation.
97
+
98
+ The reviewer is helped by `driver/plain-register.mjs`, which offers the plain form beside the term it
99
+ found and never decides anything. **It cannot flag the mark the run is clearing**: half of these words
100
+ are ordinary English and several — PREVAIL, SENIOR, SPECIFICATION — are perfectly good trademarks, so
101
+ every term the run is about is removed from the text before it is read. A check that could not tell a
102
+ mark from the profession's vocabulary would put its noise on the one report where it matters most.
103
+
22
104
  ## Fact · assessment · prescription
23
105
 
24
106
  Three different things, and only two of them belong in a report.
@@ -2,7 +2,7 @@
2
2
  "schema_version": 1,
3
3
  "framework_key": "demo",
4
4
  "title": "Demo Brand Owner risk framework",
5
- "source_deck": "Demo Brand Owner risk framework, doc 50 shape",
5
+ "source_deck": "Demo Brand Owner risk framework, authored in-house for the demonstration account",
6
6
  "entity_label": "Demo Brand Owner",
7
7
  "bands": [
8
8
  { "label": "Very High", "tone": "severe" },
@@ -2,7 +2,7 @@
2
2
  "schema_version": 1,
3
3
  "framework_key": "house-triage",
4
4
  "title": "House knockout triage ladder (Stage 0)",
5
- "source_deck": "Ported from the interactive knockout-searches skill's 5-tier rating system (SKILL.md rating table + calibration rules), doc 50 shape",
5
+ "source_deck": "Cordillera house triage scale for knockout screening, authored in-house; five rating tiers",
6
6
  "entity_label": "the client",
7
7
  "bands": [
8
8
  { "label": "Very High", "tone": "severe" },
@@ -2,7 +2,7 @@
2
2
  "schema_version": 1,
3
3
  "framework_key": "house-default",
4
4
  "title": "Generic default risk framework",
5
- "source_deck": "Generic house default IP Risk Assessment Framework.pptx (Privileged & Confidential), completed by the reviewing lawyer 2026-08-31; supersedes the 2026-07-05 transcription (doc 50), which was partial",
5
+ "source_deck": "Cordillera house risk framework, authored in-house and reviewed by counsel; 2026-08-31 revision",
6
6
  "entity_label": "the company",
7
7
  "bands": [
8
8
  { "label": "Very High", "tone": "severe" },
@@ -14,7 +14,7 @@ Across the synthesis, two postures are load-bearing:
14
14
 
15
15
  3. **Optics is annotation, not a rating.** Partner-relationship, channel-conflict, PR, and reputational concerns — however prominently `matter-context` or the manifest raise them — are surfaced as client-facing annotations (the separate PR / reputational section below; a "relationship-level heads-up"). PR is a separate category, **never rated on the framework's bands**, and an existing business relationship is an *either-way* practical factor, not a band multiplier. The rating answers what the framework in force asks — anchored in *if this party sued us, would they win?*, set by the consumer-confusion read (mark-as-whole × G&S). Never let optics/PR/partner/size move the band; annotate beside it.
16
16
 
17
- 4. **The framework in force rates the matter (doc 50).** Every run reads exactly one risk framework — **the customer's own framework if the profile has one on file, else the house default. Nothing in between.** That framework is the customer's own legal judgment written down: reason each conflict *with* it — its band definitions (Legal position × Practical position × Potential outcomes), or its matrix where it states one — and state the rating as **its band word, verbatim**. It is a reasoning authority, never a lookup table: no numeric thresholds, no score-to-band mapping, no vocabulary borrowed from another framework. **The band follows the words** — state the likelihood in plain words first (*"the prior owner is likely to win, though credible defences exist"*), then give the band those words require under the framework's own definitions; the prose read and the band are one judgment and may not disagree. Where the framework states **ceilings or matrix mappings, honour them exactly as written** — they are that framework's own anti-escalation mechanism, and no practical or optics factor lifts a rating past what its stated method yields. A conflict the client **clearly wins with no material risk is not a rated conflict at all** (most frameworks' lowest band still describes *real* residual risk — nuisance claims, weak strategic complaints, registration obstacles — never clear wins): surface it unrated as commercial awareness if worth knowing, else omit it. Voice the client side as the framework's **entity** names it (*"the company"*, *"Zephyr/Nimbus/Kestrel"*, *"Aurora Interactive"*). The three-question discipline stays: **legal risk** (*would they win?* — the confusion read net of merits defences), **practical risk** (*would they act?* — enforcement reality), and **impact** (*if they did, what follows?*). The framework's band definitions integrate the first two — reason both, in that order, through its own terms. **Impact stays client-surfaced, never rating-moving**: name the consequences for the client to weigh (we do not conclude acceptability), beside the band, exactly as before.
17
+ 4. **The framework in force rates the matter (doc 50).** Every run reads exactly one risk framework — **the customer's own framework if the profile has one on file, else the house default. Nothing in between.** That framework is the customer's own legal judgment written down: reason each conflict *with* it — its band definitions (Legal position × Practical position × Potential outcomes), or its matrix where it states one — and state the rating as **its band word, verbatim**. It is a reasoning authority, never a lookup table: no numeric thresholds, no score-to-band mapping, no vocabulary borrowed from another framework. **The band follows the words** — state the likelihood in plain words first (*"the prior owner is likely to win, though credible defences exist"*), then give the band those words require under the framework's own definitions; the prose read and the band are one judgment and may not disagree. Where the framework states **ceilings or matrix mappings, honour them exactly as written** — they are that framework's own anti-escalation mechanism, and no practical or optics factor lifts a rating past what its stated method yields. A conflict the client **clearly wins with no material risk is not a rated conflict at all** (most frameworks' lowest band still describes *real* residual risk — nuisance claims, weak strategic complaints, registration obstacles — never clear wins): surface it unrated as commercial awareness if worth knowing, else omit it. Voice the client side as the framework's **entity** names it (*"the company"*, *"Coastline/Nimbus/Kestrel"*, *"Foxglade Interactive"*). The three-question discipline stays: **legal risk** (*would they win?* — the confusion read net of merits defences), **practical risk** (*would they act?* — enforcement reality), and **impact** (*if they did, what follows?*). The framework's band definitions integrate the first two — reason both, in that order, through its own terms. **Impact stays client-surfaced, never rating-moving**: name the consequences for the client to weigh (we do not conclude acceptability), beside the band, exactly as before.
18
18
 
19
19
  ## Contents
20
20
 
@@ -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
 
package/driver/stages.mjs CHANGED
@@ -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:375), 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.`,