clearotron 0.3.2-beta.2 → 0.3.2-beta.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 (50) hide show
  1. package/.env.example +10 -0
  2. package/INSTALL.md +2 -0
  3. package/README.md +15 -10
  4. package/bin/onboard.mjs +4 -4
  5. package/build-info.json +2 -2
  6. package/docs/CLIENT-MCP.md +54 -54
  7. package/docs/DELIVERY.md +16 -15
  8. package/docs/E2E.md +8 -8
  9. package/docs/GLOSSARY.md +2 -2
  10. package/docs/INTAKE.md +11 -11
  11. package/docs/ONBOARDING.md +17 -16
  12. package/docs/PORTAL.md +18 -16
  13. package/docs/README.md +10 -10
  14. package/docs/SECURITY.md +20 -20
  15. package/docs/architecture/01-product-overview.md +17 -16
  16. package/docs/architecture/02-architecture.md +6 -6
  17. package/docs/architecture/03-run-lifecycle.md +7 -7
  18. package/docs/architecture/04-configuration-reference.md +14 -13
  19. package/docs/architecture/05-config-governance.md +30 -29
  20. package/docs/architecture/05-customer-profiles.md +35 -35
  21. package/docs/architecture/06-operations-runbook.md +20 -20
  22. package/docs/architecture/07-quality-and-audit.md +17 -17
  23. package/docs/architecture/08-development-guide.md +3 -3
  24. package/docs/architecture/09-security-and-data.md +27 -27
  25. package/docs/architecture/README.md +1 -1
  26. package/docs/branding.md +8 -3
  27. package/docs/configuration.md +18 -18
  28. package/docs/writing-standard.md +3 -3
  29. package/driver/CHANGELOG.md +27 -0
  30. package/driver/package.json +1 -1
  31. package/driver/portal-service.mjs +34 -4
  32. package/driver/suite-census.json +110 -44
  33. package/mcp-server/CHANGELOG.md +4 -0
  34. package/mcp-server/package.json +1 -1
  35. package/mcp-server/packs/client/CONNECT.md +6 -6
  36. package/package.json +1 -1
  37. package/portal-ui/dist/assets/{index-BsbasHjM.js → index-Bki5jT_N.js} +3376 -2971
  38. package/portal-ui/dist/assets/{index-DNQpLYZF.css → index-De2RFLbT.css} +972 -205
  39. package/portal-ui/dist/index.html +2 -2
  40. package/portal-ui/package.json +1 -1
  41. package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
  42. package/providers/oauth-mcp-bridge/package.json +1 -1
  43. package/scripts/ask-ai-render-check.mjs +287 -105
  44. package/scripts/release-note-required.mjs +16 -2
  45. package/scripts/settings-render-check.mjs +575 -0
  46. package/shared/brand.mjs +29 -0
  47. package/shared/connect-clients.mjs +99 -47
  48. package/shared/names-in-force.mjs +1 -0
  49. package/shared/stdio-connect.mjs +16 -2
  50. package/shared/writing-standard-classes.mjs +34 -3
package/.env.example CHANGED
@@ -603,3 +603,13 @@ CLEAROTRON_DEMO_PROFILES=
603
603
  # it. Read by bin/start.mjs and shared/brand.mjs.
604
604
  # effect: deployment
605
605
  CLEAROTRON_ORGANISATION_NAME=
606
+
607
+ # ── Who a person asks to change their sign-in ─────────────────────────────────────────────────────
608
+ #
609
+ # The administrator contact: a mail address (`it@example.com`, or `mailto:it@example.com`) or a web
610
+ # address (`https://help.example.com/access`). Preferences tells a signed-in person to contact their
611
+ # Clearotron administrator to change the address, the permissions or the companies on their sign-in;
612
+ # set, those words are a link to this; unset, they are plain text. Any other value is treated as unset,
613
+ # so nothing but a mail or web address ever becomes a link. Read by shared/brand.mjs.
614
+ # effect: deployment
615
+ CLEAROTRON_ADMINISTRATOR_CONTACT=
package/INSTALL.md CHANGED
@@ -438,6 +438,8 @@ CLEAROTRON_CUSTOMERS_DIR=/etc/trademark/profiles # your private customer-config
438
438
  CLEAROTRON_BRAND_NAME=Your Firm # stamped into report titles, the pool index and Excel metadata
439
439
  CLEAROTRON_BRAND_TAGLINE= # empty means ABSENT: no strapline is rendered at all
440
440
  CLEAROTRON_BRAND_PRODUCT=Trademark clearance # what the deliverable is called
441
+ # Optional: a mail or web address. Preferences links "Clearotron administrator" to it; unset, plain words.
442
+ # CLEAROTRON_ADMINISTRATOR_CONTACT=it@your-firm.example
441
443
 
442
444
  # ── Register provider (choose ONE) ─────────────────────────────────────
443
445
  CLEAROTRON_DATABASE=clarivate # REQUIRED — corsearch | clarivate | signa | euipo | uspto-local | free-tier
package/README.md CHANGED
@@ -10,10 +10,11 @@
10
10
  <a href=".nvmrc"><img src="https://img.shields.io/badge/node-%E2%89%A5%2022.13-250902?style=flat-square" alt="Node 22.13+"></a>
11
11
  </p>
12
12
 
13
- Give it a mark, its classes and a territory. Clearotron searches the trademark registers and the open
14
- web for conflicts, reasons about the risk the way a clearance lawyer would, and publishes a written
15
- report with a machine-readable audit trail behind every finding. It runs headless on your own machine:
16
- no gateway, no platform, and nothing about your matters reaches us.
13
+ Before your company commits to a name, find out what stands in its way. Give Clearotron the name, the
14
+ classes you trade in and the territories you sell into: it searches the trademark registers and the
15
+ open web for conflicts, reasons about the risk the way a clearance lawyer would, and publishes a
16
+ written report with a machine-readable audit trail behind every finding. It runs headless on your own
17
+ machine: no gateway, no platform, and nothing about the names you are clearing reaches us.
17
18
 
18
19
  [Quickstart](QUICKSTART.md) · [Install & operate](INSTALL.md) · [Docs](docs/README.md) · [Security](docs/SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Licence](#licence)
19
20
 
@@ -27,7 +28,7 @@ npx clearotron demo
27
28
 
28
29
  That fetches the published package — it will ask once before downloading — then replays finished
29
30
  clearances into a local portal and prints the portal's address and the passphrase to sign in with. Open
30
- the address in your browser. No account, no credentials, no network calls to us.
31
+ the address in your browser. No sign-up, no credentials, no network calls to us.
31
32
 
32
33
  The demo runs for as long as that window stays open, and removes everything it made when you close it —
33
34
  nothing of it is left on the machine, and running it again later starts clean. If you want to keep the
@@ -82,8 +83,8 @@ Then start the product and open the portal address it prints:
82
83
  clearotron start
83
84
  ```
84
85
 
85
- That is the portal a brand owner uses. Ordering a clearance is the same screen — describe it in a
86
- sentence, or set the classes, marketplaces and search depth yourself:
86
+ That is the portal everyone at your company uses. Ordering a clearance is the same screen — describe it
87
+ in a sentence, or set the classes, marketplaces and search depth yourself:
87
88
 
88
89
  ![The new-clearance screen — classes, marketplaces and the four search depths](docs/assets/portal-new-clearance.png)
89
90
 
@@ -115,12 +116,16 @@ clearotron run --job my-job.json
115
116
  **HTTP** face whose read tools serve a signed-in identity while its write verbs — `start_run`
116
117
  among them, which spends — need an ops token. [Connect it](mcp-server/CONNECT.md).
117
118
  - **The engine is not coupled to a vendor.** [`driver/register-plan.mjs`](driver/register-plan.mjs) — which decides what gets searched — takes a capabilities object as a parameter and imports no provider at all. An unknown register id throws rather than falling back.
119
+ - **A law firm runs one installation for every company it acts for.** Each company is set up once, with
120
+ its own classes, marketplaces and risk framework, and each person sees only the companies they are
121
+ given. [Adding a company](docs/ONBOARDING.md) · [A connector for those companies' people](docs/CLIENT-MCP.md).
118
122
 
119
123
  ## Security
120
124
 
121
- Reports carry client matter. Treat the pool, the archive and the delivery packets as you would a case file.
125
+ Reports describe names your company has not announced yet. Treat the pool, the archive and the delivery
126
+ packets as you would any unreleased plan — and, at a law firm, as you would a case file.
122
127
 
123
- **The authors of this software receive nothing** — no marks, no client context, no results, no usage
128
+ **The authors of this software receive nothing** — no marks, no company context, no results, no usage
124
129
  reports, no crash reports. There is no telemetry in this tree and no endpoint we control: every
125
130
  destination is a register, a reasoning provider or a search provider you configured with your own
126
131
  credential.
@@ -139,7 +144,7 @@ credential.
139
144
  | Check it works before spending anything | [docs/E2E.md](docs/E2E.md) |
140
145
  | Understand the architecture | [docs/architecture/](docs/architecture/) · [decisions](docs/decisions/) |
141
146
  | Run it under your own name, or fork it | [docs/branding.md](docs/branding.md) · [TRADEMARKS.md](TRADEMARKS.md) |
142
- | Write a sentence a customer will read | [docs/writing-standard.md](docs/writing-standard.md) · [docs/writing-rules.md](docs/writing-rules.md) |
147
+ | Write a sentence a user will read | [docs/writing-standard.md](docs/writing-standard.md) · [docs/writing-rules.md](docs/writing-rules.md) |
143
148
 
144
149
  ## Development
145
150
 
package/bin/onboard.mjs CHANGED
@@ -2015,7 +2015,7 @@ export async function runCheck() {
2015
2015
  if (!prov) {
2016
2016
  blocking(`no register is selected — CLEAROTRON_DATABASE is not set and there is NO default, so every search refuses until one is`);
2017
2017
  info(` set it to one of: ${PROVIDERS.map((p) => p.id).join(", ")} — any one of them is enough, and none needs another`);
2018
- info(` re-run \`${invoke("install")}\`, or set it on the Global config page`);
2018
+ info(` re-run \`${invoke("install")}\`, or set it on the Installation settings page`);
2019
2019
  }
2020
2020
  else {
2021
2021
  const spec = PROVIDERS.find((p) => p.id === prov.v);
@@ -3565,7 +3565,7 @@ try {
3565
3565
  //
3566
3566
  // NOTHING DOWNSTREAM NEEDED CHANGING, and that is the owner's point rather than luck: CLEAROTRON_DATABASE
3567
3567
  // is single-valued with no default, a run already refuses by name when it is unset
3568
- // (driver.config.mjs), and the Global config page already renders "No register is selected."
3568
+ // (driver.config.mjs), and the Installation settings page already says a register is needed.
3569
3569
  // One register per install, any one of them sufficient, none a precondition for another.
3570
3570
  let registerSelected = true;
3571
3571
  // What THIS step collected, so abandoning the selection can take it back. Measured: a register with
@@ -3591,7 +3591,7 @@ try {
3591
3591
  say("");
3592
3592
  info("No register is selected, and nothing register-related will be written.");
3593
3593
  info(`Every search refuses until one is set — \`${invoke("doctor")}\` says so on every run, and the`);
3594
- info(" Global config page says it too. Re-run setup, or set it there, when you have a credential.");
3594
+ info(" Installation settings page says it too. Re-run setup, or set it there, when you have a credential.");
3595
3595
  }
3596
3596
  for (const k of registerSelected ? (spec.optionalCredentials ?? []) : []) {
3597
3597
  if (present(candidate[k])) { ok(`${k} already adopted from your environment`); continue; }
@@ -4142,7 +4142,7 @@ try {
4142
4142
  say(` \`${invocationPrefix()}clearotron demo\` and \`${invocationPrefix()}clearotron start\` work now. A real`);
4143
4143
  say(" clearance needs one register — any one is enough, and none requires another:");
4144
4144
  say(` ${PROVIDERS.map((p) => p.id).join(", ")}`);
4145
- say(` Set it by re-running \`${invocationPrefix()}clearotron install\`, or on the Global config page.`);
4145
+ say(` Set it by re-running \`${invocationPrefix()}clearotron install\`, or on the Installation settings page.`);
4146
4146
  say(` \`${invocationPrefix()}clearotron doctor\` says which state this install is in, at any time.\n`);
4147
4147
  }
4148
4148
  } catch (e) {
package/build-info.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "8393489759f73ec4177b2fce74005780b7b4a567",
3
- "version": "0.3.2-beta.2"
2
+ "commit": "d4059c3a0c77911c1592fda411dc27948b4aaa95",
3
+ "version": "0.3.2-beta.3"
4
4
  }
@@ -5,44 +5,44 @@ definitions.*
5
5
 
6
6
  > **Just want to connect your own app to your own runs?** You do not need any of this — spawn the stdio
7
7
  > server from your clone. [`mcp-server/CONNECT.md`](../mcp-server/CONNECT.md) is four lines of
8
- > copy-paste. This document is for publishing a connector your *customers* sign in to.
8
+ > copy-paste. This document is for publishing a connector that each *company's* people sign in to.
9
9
 
10
- What a customer reaches once you have, what they cannot, and how to turn it on. For the staff/ops MCP
10
+ What they reach once you have, what they cannot, and how to turn it on. For the staff/ops MCP
11
11
  faces see `docs/architecture/09-security-and-data.md`.
12
12
 
13
13
  ## The three faces, in one table
14
14
 
15
15
  One codebase (`mcp-server/server.mjs`), three processes. The separation is per-process **configuration**,
16
- never a runtime branch — "a client cannot reach staff read-all" is a fact about which binary is listening,
17
- not about a flag being right.
16
+ never a runtime branch — "a company's person cannot reach staff read-all" is a fact about which binary is
17
+ listening, not about a flag being right.
18
18
 
19
19
  | Face | Where it listens | Who | Reach |
20
20
  |---|---|---|---|
21
- | **Staff** | your staff hostname → `TRADEMARK_MCP_HTTP_PORT` (default 18790) | firm staff (staff CF Access AUD) | every read tool, all runs |
22
- | **Client** | your client hostname → `CLIENT_MCP_HTTP_PORT` (default 18811) | customers (client CF Access AUD) | see below |
21
+ | **Staff** | your staff hostname → `TRADEMARK_MCP_HTTP_PORT` (default 18790) | your staff (staff CF Access AUD) | every read tool, all runs |
22
+ | **Client** | your client hostname → `CLIENT_MCP_HTTP_PORT` (default 18811) | the people of each company (client CF Access AUD) | see below |
23
23
  | **Ops** | loopback only, on its own port, **no hostname** | the portal's trigger lane | reads + write verbs |
24
- | **API key** | a hostname with no Access app in front → its own `CLIENT_MCP_HTTP_PORT` | a client agent that cannot sign in | same as Client |
24
+ | **API key** | a hostname with no Access app in front → its own `CLIENT_MCP_HTTP_PORT` | a company's agent that cannot sign in | same as Client |
25
25
 
26
26
  Every port above is the code default and each face is a separate process, so on one machine give
27
- each its own. The ops face is never on the internet. If you are looking for "the customer one", it
28
- is the Client face. The API-key door is that same client face reached with a credential instead of a
27
+ each its own. The ops face is never on the internet. If you are looking for the face a company's people
28
+ use, it is the Client face. The API-key door is that same client face reached with a credential instead of a
29
29
  browser login — see "The API-key door" below.
30
30
 
31
- ## The two client principals
31
+ ## The two principals outside your staff
32
32
 
33
33
  **`user` — a run-bound report link.** A read-only token pinned to one run, no enumeration. The scope
34
34
  is served as it always was; what changed with the move to a single report document is how anyone
35
35
  comes by one. The token is minted into THE report's "Ask your AI" block against the STAFF connector
36
36
  (`CLEAROTRON_MCP_URL`, `render.mjs`), and `portal-report.mjs` strips that whole block for every non-staff
37
- reader at serve time — so **no client-facing surface hands one out any more**. A signed-in client
38
- reaches the connector as `account` instead, at an address the portal serves live from
37
+ reader at serve time — so **no surface a non-staff reader sees hands one out any more**. A signed-in
38
+ person reaches the connector as `account` instead, at an address the portal serves live from
39
39
  `/portal/api/mcp-access` (`CLEAROTRON_CLIENT_MCP_URL`, no baked credential). A run-bound link for a
40
40
  recipient who has no login is now a deliberate act:
41
41
  `node mcp-server/mint-token.mjs --scope user --run <runId>`.
42
42
 
43
- **`account` — a signed-in client.** A CF-verified client identity with **no token**, resolved to the
44
- accounts their email is granted (`CLEAROTRON_ACCESS_FILE` — the same guest list the portal's client door uses).
45
- Enrolment is therefore the portal's: no second credential to mint, rotate or revoke, and revoking portal
43
+ **`account` — a signed-in person.** A CF-verified identity from outside your staff with **no token**,
44
+ resolved to the companies their email is granted (`CLEAROTRON_ACCESS_FILE` — the same guest list the portal
45
+ uses). Enrolment is therefore the portal's: no second credential to mint, rotate or revoke, and revoking portal
46
46
  access revokes this with it. **Off unless `CLIENT_MCP_ACCOUNT_ACCESS=1`.**
47
47
 
48
48
  **Who turns that on. The installer, since 2026-09-03** — ruling, settled
@@ -53,9 +53,9 @@ come from one authority, `enablePlan` in `shared/client-door.mjs`, which is also
53
53
 
54
54
  **This supersedes the 2026-08-31 ruling** *"On demand is fine"*, under which nothing at install and no
55
55
  rebuild's enable list could start this unit, because starting it WAS the consent that opened
56
- client-account access. The owner changed the posture knowingly: **the per-account key is the gate, not
57
- whether a process runs.** A door with no key issued refuses everything, which is the same protection by
58
- a mechanism that does not depend on a reader finding a verb.
56
+ access for each company's people. The owner changed the posture knowingly: **the per-person key is the
57
+ gate, not whether a process runs.** A door with no key issued refuses everything, which is the same
58
+ protection by a mechanism that does not depend on a reader finding a verb.
59
59
 
60
60
  **`npx clearotron disconnect` therefore revokes a person, not a service** (Q3). It writes the caller's key
61
61
  ids to the denylist and strikes them from the record; it does not stop the unit and does not touch
@@ -63,58 +63,58 @@ ids to the denylist and strikes them from the record; it does not stop the unit
63
63
  `npx clearotron disconnect --everyone`, which states how many keys and how many people that is before
64
64
  acting — and does not stop the service either.
65
65
 
66
- An `account` principal reaches **eighteen** tools, for its own accounts only — everything carrying
66
+ An `account` principal reaches **eighteen** tools, for its own companies only — everything carrying
67
67
  `accountSafe: true` in `TOOL_SCOPES` (`shared/scope.mjs`), and nothing else:
68
68
 
69
69
  | Layer | Tools |
70
70
  |---|---|
71
- | the report | `brief`, `read_artifact` (the report; `clientSummary` is retired from client reach and stays an ops-only internal source), `list_findings` (curated cards) |
71
+ | the report | `brief`, `read_artifact` (the report; `clientSummary` is retired from this face and stays an ops-only internal source), `list_findings` (curated cards) |
72
72
  | the evidence behind it | `list_evidence`, `list_searches`, `get_search_coverage` |
73
73
  | the audit chain | `read_artifact` over `audit`, `narrative`, `registerFindings`, `commonLaw`, `caseLaw`, `matterContext` and `registerUnit:<axis>`; `list_findings` on the raw `kind` path; `get_finding`, `get_run`, `trace`, `decision_timeline` |
74
74
  | the run lifecycle | `list_runs`, `describe_options`, `plan_run`, `start_run`, `stop_run` |
75
75
  | what-if | `what_if_plan` (free), `what_if_run` (queues a sandbox job), `what_if_result` (collects it) |
76
76
 
77
- The evidence layer exists because a client lawyer defending a filing decision needs the records
77
+ The evidence layer exists because a lawyer defending a filing decision needs the records
78
78
  under the report, not just its prose. It projects named structured fields and enums derived from
79
79
  them — `mcp-server/lib/evidence.mjs` states that there is no code path forwarding free prose, and
80
80
  that is the one declared exception to the scrub.
81
81
 
82
- **The audit chain is open by ruling, 2026-08-27** ("I don't see why we don't open it or just
83
- give it to clients. Ignore the call spend."). The same lawyer who needs the records also has to be
84
- able to show *how* the answer was reached, so the decision chain is client product now. Unlike the
82
+ **The audit chain is open by ruling, 2026-08-27**, with the call spend set aside. The same lawyer who
83
+ needs the records also has to be able to show *how* the answer was reached, so the decision chain is
84
+ part of what this face serves now. Unlike the
85
85
  evidence layer this one does forward prose — a chain of reasoning is prose — so it is bounded a
86
86
  different way: `mcp-server/lib/audit-view.mjs` names the structural fields that travel and puts the
87
- surviving prose through the report's own client-safety passes, never a second copy of them.
87
+ surviving prose through the report's own scrub passes, never a second copy of them.
88
88
 
89
89
  Three things stayed behind, and each has a reason rather than a habit:
90
90
 
91
91
  - **Cost.** `get_telemetry` and `get_provider_usage` exist to report model identity and billed
92
92
  counts. Every other decision-chain read is model-free by construction — `events.mjs`, `trace.mjs`
93
- and `getStages` each say so on their own surface — so sealing exactly these two costs a client
93
+ and `getStages` each say so on their own surface — so sealing exactly these two costs the reader
94
94
  nothing of the chain.
95
95
  - **The engine's judgment of its own output.** `skepticFlags` and `seniorEyeReview` are the reviewers
96
96
  writing about our draft, the same class as the `withdrawn_reason` ruling. The verdict they
97
97
  produced travels; the critique does not.
98
98
  - **The unruled reads.** `get_coverage`, `search`, `search_runs`, `diff_artifact`, `run_changes`,
99
99
  `list_profiles`, delivery/outbox, `feed_context` and what-if. Nobody has decided what these should
100
- show a client, and an undecided tool is denied — `get_search_coverage` is deliberately not
100
+ show a company's people, and an undecided tool is denied — `get_search_coverage` is deliberately not
101
101
  `get_coverage`, the latter being the engineering artifact-validity view.
102
102
 
103
103
  **What-if is a QUEUED JOB on this surface, and that is what keeps the door honest.** The remote faces
104
104
  never spawn the engine — `http-server.mjs` states it as a configuration fact and `lib/whatif.mjs`'s lazy
105
- import of `driver/pipeline.mjs` is what holds it — so a client's `what_if_run` does not execute. It
105
+ import of `driver/pipeline.mjs` is what holds it — so a `what_if_run` on this face does not execute. It
106
106
  validates, enqueues into `<runDir>/_experiments/_queue/`, and returns an `experimentId`;
107
107
  `driver/whatif-worker.mjs`, drained by the runner in an OS service process, is what spawns the sandbox.
108
- The client collects the diff with `what_if_result`. The original run is never modified — the experiment
108
+ The caller collects the diff with `what_if_result`. The original run is never modified — the experiment
109
109
  writes only under `_experiments/`.
110
110
 
111
111
  Four things about it are worth knowing before you offer it:
112
112
 
113
- - **The confirmation-token handshake stays, and a client must ALSO name the run.** A token is plain
114
- base64url JSON that nothing signs, so a token-only call would slip past the account gate, which keys on
113
+ - **The confirmation-token handshake stays, and the caller must ALSO name the run.** A token is plain
114
+ base64url JSON that nothing signs, so a token-only call would slip past the grant check, which keys on
115
115
  `runId`. Naming the run puts the grant check in the path; `whatIfEnqueue` then proves the token names
116
116
  the same run, so neither half can be satisfied alone.
117
- - **A client cannot choose the model.** The tier is cost and method both, and it is the one argument on
117
+ - **The caller cannot choose the model.** The tier is cost and method both, and it is the one argument on
118
118
  the one tool that spends. Express the change with `instructions`.
119
119
  - **Nothing bounds the spend, by ruling.** `start_run` is stamped `clientPrincipal: true` at the
120
120
  chokepoint so `runCaps.dailyRuns` bites it; a what-if job carries no such stamp, because the owner
@@ -125,15 +125,15 @@ Four things about it are worth knowing before you offer it:
125
125
  - **It starts on the timer, not instantly.** The systemd `.path` unit watches the clearance queue dirs,
126
126
  not run dirs, so a queued what-if is picked up on the runner's 90s tick.
127
127
 
128
- **A forwardable report link did not move.** `USER_ARTIFACTS` gated `read_artifact` for both client
129
- kinds, so the account layer was given its own set (`ACCOUNT_ARTIFACTS`) rather than the shared one
128
+ **A forwardable report link did not move.** `USER_ARTIFACTS` gated `read_artifact` for both principal
129
+ kinds, so the `account` layer was given its own set (`ACCOUNT_ARTIFACTS`) rather than the shared one
130
130
  being widened: a run-bound `user` token rides inside a delivered PDF and can be forwarded to anyone,
131
- and the ruling was about clients the firm enrolled.
131
+ and the ruling was about people the operator enrolled.
132
132
 
133
- ## What a client sees of a report
133
+ ## What a company's people see of a report
134
134
 
135
- The client cut, which is **what THE report already shows them — no more, and no less**:
136
- `report.html` as served to a client through the portal's `readReport()`
135
+ The cut, which is **what THE report already shows them — no more, and no less**:
136
+ `report.html` as served to a non-staff reader through the portal's `readReport()`
137
137
  preparation (`mcp-server/lib/scrub.mjs` states the rule; `driver/portal-report.mjs` is the serve-time
138
138
  counterpart).
139
139
 
@@ -142,23 +142,23 @@ hash, and the `tier`/`label` card shorthand the report footer says is "removed o
142
142
 
143
143
  **Kept, deliberately:** the Methodology section and the register/common-law provider names. Both are in the
144
144
  delivered report already (`render.mjs` renders Methodology via `plainScopeNote`; provider names are named
145
- for provenance honesty). A scrubber stricter than the report would delete content the client was already
145
+ for provenance honesty). A scrubber stricter than the report would delete content the reader was already
146
146
  sent and make the connector a different product from the PDF in their inbox. **If you want less exposed,
147
147
  change the report render or the serve-time preparation (`portal-report.mjs`) — it flows here for free.
148
148
  Never add an MCP-only rule.**
149
149
 
150
- ## The daily allowance — read this before enabling a demo tenant
150
+ ## The daily allowance — read this before enabling a demo company
151
151
 
152
- A client starting a run over MCP spends real money. The control is `runCaps.dailyRuns` on the customer
152
+ Someone starting a run over this face spends real money. The control is `runCaps.dailyRuns` on the company
153
153
  profile, enforced at the runner's admission gate (so it covers every door) plus a portal pre-check.
154
154
 
155
155
  It only bites jobs stamped `clientPrincipal: true`, and **that stamp is positive-only — absence means
156
- uncapped**. `authorize()` forces it for an `account` principal, so a client cannot omit it or pass `false`.
157
- Staff runs deliberately never consume a client's allowance.
156
+ uncapped**. `authorize()` forces it for an `account` principal, so a caller cannot omit it or pass `false`.
157
+ Staff runs deliberately never consume a company's allowance.
158
158
 
159
- **For a demo or pitch account, set `dailyRuns` low (1–2) and `maxQueued: 1`.** Without `dailyRuns` the
160
- account is uncapped by day and can exhaust the weekly engine capacity in a sitting. Prefer a synthetic
161
- account for demos so the data is disposable too.
159
+ **For a demo or pitch company, set `dailyRuns` low (1–2) and `maxQueued: 1`.** Without `dailyRuns` the
160
+ company is uncapped by day and can exhaust the weekly engine capacity in a sitting. Prefer a synthetic
161
+ company for demos so the data is disposable too.
162
162
 
163
163
  ## The API-key door — for agents that cannot sign in
164
164
 
@@ -173,12 +173,12 @@ node mcp-server/mint-token.mjs --scope account --sub lawyer@acme.example [--acco
173
173
  ```
174
174
 
175
175
  **The key proves WHO; the grants file still decides WHAT.** `--sub` names an identity that must appear in
176
- `CLEAROTRON_ACCESS_FILE`, and the accounts are resolved from that file **on every request** — never baked into
176
+ `CLEAROTRON_ACCESS_FILE`, and the companies are resolved from that file **on every request** — never baked into
177
177
  the token. `--accounts` is a CAP (an intersection on top of the grant) and can only narrow it. So there are
178
178
  two independent revocation levers: **delete the grants row** (instant, needs no re-minting) or **denylist
179
179
  the `jti`** (`TRADEMARK_MCP_TOKEN_DENYLIST`).
180
180
 
181
- It resolves to the **same `account` principal** as a signed-in client, so the tool set, `authorize()`, the
181
+ It resolves to the **same `account` principal** as a signed-in person, so the tool set, `authorize()`, the
182
182
  scrub and `runCaps` above all apply unchanged — there is no second policy to keep in step.
183
183
 
184
184
  **It is a separate process, not a flag on the client door** (`CLIENT_MCP_TOKEN_ONLY=1`, its own
@@ -196,7 +196,7 @@ A key may be presented as `Authorization: Bearer <key>`, a bare `Authorization:
196
196
  say in which, and `?token=` is the fallback that needs no header at all. On the CF-fronted doors
197
197
  `Authorization` is deliberately **not** read as a trademark key: there it belongs to the proxy/agent.
198
198
 
199
- **Give a key a real account with real `runCaps`, never `generic`** — `generic` is cap-exempt, so a
199
+ **Give a key a real company with real `runCaps`, never `generic`** — `generic` is cap-exempt, so a
200
200
  long-lived credential pointed at it can spend without limit.
201
201
 
202
202
  ### Standing it up
@@ -209,7 +209,7 @@ long-lived credential pointed at it can spend without limit.
209
209
  `CLIENT_MCP_ACCOUNT_ACCESS=1` + `CLEAROTRON_ACCESS_FILE`, and drop the CF AUD lines (unused here).
210
210
  3. Verify before exposing: `curl 127.0.0.1:<port>/healthz`; the boot log says `API-KEY door — no auth
211
211
  proxy in front`; an MCP `initialize` **without** a key is a 401.
212
- 4. Mint a key, hand it over with the address. `tools/list` must be exactly the eleven account tools
212
+ 4. Mint a key, hand it over with the address. `tools/list` must be exactly the eleven `account` tools
213
213
  above — anything more means the door resolved a wider principal than `account`.
214
214
 
215
215
  ## Turning it on
@@ -227,19 +227,19 @@ template, and a placeholder that looks configured is worse than unset.**
227
227
  and `client ACCOUNT access ON`. Starting with the flag set and no grants file is a **FATAL**
228
228
  refusal, by design.
229
229
  4. At your edge: confirm the client hostname resolves to that port and that the **client** Access app
230
- fronts it — not the staff one. Enrol the client emails on that app's policy.
230
+ fronts it — not the staff one. Enrol the addresses of each company's people on that app's policy.
231
231
  5. `CLEAROTRON_CLIENT_MCP_URL` must be set for the portal process too, or `/portal/api/mcp-access` answers
232
232
  `{url:null}` and the Use-your-AI screen correctly shows its empty state.
233
233
 
234
234
  The client connector address is served **live**: `/portal/api/mcp-access` reads `CLEAROTRON_CLIENT_MCP_URL`
235
- at request time, so a change reaches every client screen on the next load — no re-render involved. The
235
+ at request time, so a change reaches every screen that shows it on the next load — no re-render involved. The
236
236
  render no longer reads this variable at all: the block baked into
237
237
  `report.html` is the STAFF connector (`CLEAROTRON_MCP_URL`), and it is stripped for non-staff readers at
238
238
  serve time.
239
239
 
240
- ## What briefs the client's assistant
240
+ ## What briefs a company's assistant
241
241
 
242
- `skills/clearotron-client/SKILL.md`, served as the MCP `instructions` field on initialize — clients surface
242
+ `skills/clearotron-client/SKILL.md`, served as the MCP `instructions` field on initialize — MCP clients surface
243
243
  it to their model on connect. It sets voice (plain language, no codes), the tool ladder, the verdict
244
244
  vocabulary, evidence drill-through, and the three "never"s.
245
245
 
package/docs/DELIVERY.md CHANGED
@@ -152,9 +152,9 @@ cap. An integrator that only runs during the day leaves a failure unreported unt
152
152
  nothing in the product can compensate for that: the product composes the notice and records that it is
153
153
  owed, and sending is yours.
154
154
 
155
- **Two things that make that list incomplete, both silent.** A token scoped to named accounts sees only
156
- those accounts' runs, so a run for an account the token does not carry is invisible rather than absent —
157
- mint the integrator's token to cover every account it delivers for, and re-mint it when one is added.
155
+ **Two things that make that list incomplete, both silent.** A token scoped to named companies sees only
156
+ those companies' runs, so a run for a company the token does not carry is invisible rather than absent —
157
+ mint the integrator's token to cover every company it delivers for, and re-mint it when one is added.
158
158
  And a `limit` you pass yourself is obeyed as given: for this query, do not pass one.
159
159
 
160
160
  The filesystem loop below remains equivalent for integrators that do have data-plane access.
@@ -194,29 +194,29 @@ double-sends (`.sent` guards it).
194
194
 
195
195
  ## One report per run
196
196
 
197
- A run publishes ONE report document per mark, on every lane. There is no internal variant and no client
198
- variant, and nothing writes `report.client.html` — internal working material (staff notes, the
197
+ A run publishes ONE report document per mark, on every lane. There is no internal variant and no second
198
+ one for outside readers, and nothing writes `report.client.html` — internal working material (staff notes, the
199
199
  model's register estimate) is not stripped from the report, it is not in the report: it lives in the
200
200
  audit workbook. Two renderings of one run is how the wrong link gets sent.
201
201
 
202
202
  Beside it the run publishes `report-data.json` (`schema: "report-data/1"`): the run as data — level
203
203
  identity, bands, per-mark points, evidence links, register counts. That is the input a bespoke,
204
- forwardable client email is drafted from. The engine composes exactly one email shape, a cover note
205
- pointing at the report; per-customer formatting is not a config knob.
204
+ forwardable email is drafted from. The engine composes exactly one email shape, a cover note
205
+ pointing at the report; per-company formatting is not a config knob.
206
206
 
207
207
  The second document was a real hazard while it existed: any surface that opened a report **by file
208
- path** bypassed the serve-time preparation, so a client-facing path pointed at the wrong file served
208
+ path** bypassed the serve-time preparation, so a non-staff path pointed at the wrong file served
209
209
  the internal report. Two properties close that, and both are load-bearing for anyone building a
210
- client-facing surface on this engine:
210
+ surface for non-staff readers on this engine:
211
211
 
212
- - **Publish writes one file.** No lane produces `report.client.html`, and the per-customer index
212
+ - **Publish writes one file.** No lane produces `report.client.html`, and the per-company index
213
213
  (`customer/<key>/index.html`) links `report.html` (`publish/index.mjs`) with no split language.
214
214
  - **One preparation chokepoint.** What a non-staff reader receives is prepared by the portal's
215
215
  `readReport()` (`driver/portal-report.mjs`, `staff:false`), which removes the reader-visible
216
216
  deltas — `[internal]` review tails, the internal band/reviewer shorthand, the staff Ask-your-AI
217
217
  connector — in one place, rather than at render time into a second document.
218
218
 
219
- **A client-facing surface must read through `readReport()`, never open a report by path.** That is
219
+ **A surface for non-staff readers must read through `readReport()`, never open a report by path.** That is
220
220
  the whole guarantee: the preparation is on the read, so a surface that skips it serves unprepared
221
221
  bytes. Pool directories from before the change may still hold a `report.client.html`; nothing reads
222
222
  those files, and `publish/pool-admin.mjs` deliberately leaves them alone rather than retrofitting
@@ -256,9 +256,10 @@ There is no preflight that fails a run before anything touches the pool, and no
256
256
  `deliveryFlagLines`): one plain sentence per failing check, with a count. The checks' own `detail`
257
257
  never leaves the internal lane — it quotes fetch causes, register URIs, model field names and
258
258
  instructions to whoever re-runs the job. **The cover note carries no machine-check block**, on
259
- either lane: `emailBodyHtml` is sent verbatim to `forwarderEmail`, which on a client-started run is
260
- the client's own address, and a failing internal check does not change what was searched, so that
261
- reader cannot act on it. One enumeration surface, and it is the one the reviewer already opens.
259
+ either lane: `emailBodyHtml` is sent verbatim to `forwarderEmail`, which on a run started by someone outside
260
+ the operator's staff is that person's own address, and a failing internal check does not change what
261
+ was searched, so that reader cannot act on it. One enumeration surface, and it is the one the reviewer
262
+ already opens.
262
263
 
263
264
  ### The knockout lane runs a SUBSET of the predelivery lint
264
265
 
@@ -275,7 +276,7 @@ the store-rendered end-state: `publishKnockout` renders from validated `knockout
275
276
  `validateMergedFindings` plus the per-chunk validators are its own lint — schema, ladder vocabulary,
276
277
  plan-parity, degraded-parity, tone, quantitative claims, URL receipts. Most clearance checks then
277
278
  read surfaces this lane does not produce: no register record store (it counts hits, it does not
278
- retrieve records), no actions register, no verdict sidecar, no client summary, no card assembly, no
279
+ retrieve records), no actions register, no verdict sidecar, no `clientSummary`, no card assembly, no
279
280
  reviewer correction cycle, no intake-ask register.
280
281
 
281
282
  So the checks that run are exactly those whose whole input is model-authored text —
package/docs/E2E.md CHANGED
@@ -89,15 +89,15 @@ frozen demo profile, and the file to open to prove which profile resolved).
89
89
 
90
90
  ### Tier 1b — the UI PORTAL (browse the dev instance; develop UI features against it)
91
91
 
92
- The pool already contains the whole UI (archive index, per-run report + client report + audit
93
- workbook, per-customer pages); production serves it with a real web server behind the auth proxy.
92
+ The pool already contains the whole UI (archive index, per-run report + audit workbook, per-company
93
+ pages); production serves it with a real web server behind the auth proxy.
94
94
  The dev stand-in is `driver/dev-portal.mjs` — zero-dep, **loopback-only** (refuses anything else):
95
95
 
96
96
  ```bash
97
97
  CLEAROTRON_REPORTS_DIR=$HOME/trademark-dev/pool node driver/dev-portal.mjs # http://127.0.0.1:18899/
98
98
  ```
99
99
 
100
- - `/` → the archive index · `/<run>/report.html` → the report · `/customer/<key>/` → customer pages
100
+ - `/` → the archive index · `/<run>/report.html` → the report · `/customer/<key>/` → company pages
101
101
  - `/profiles.html` + `/profiles/*` → the profile editor UI + a reverse-proxy to the profile-service
102
102
  (run that in ITS dev mode: `PROFILE_AUTH_DISABLED=1 PROFILE_DEV=1 PROFILE_PORT=<dev port>`)
103
103
  - the MCP HTTP face runs separately in its own dev mode (`TRADEMARK_MCP_DEV=1
@@ -115,7 +115,7 @@ so a dev instance beside a live one must be given its own (`PORTAL_PORT`, `PROFI
115
115
  silently — each is a proxy to a port, and the port is all it knows. `/recipes/*` is the worse half:
116
116
  its save endpoint writes and git-commits into whichever recipe store it reached.
117
117
 
118
- A pass here looks like: index, run report, the demo customer page and the profile-editor UI all
118
+ A pass here looks like: index, run report, the demo company's page and the profile-editor UI all
119
119
  render against the Tier-1 pool; the `/profiles/*` proxy round-trips; traversal and non-loopback binds
120
120
  are refused (unit-tested).
121
121
 
@@ -147,8 +147,8 @@ correct.
147
147
  ## Tier 3 — the paid cutover run (once, ~$40)
148
148
 
149
149
  Same loop as Tier 2 with the real engine (`CLEAROTRON_CLAUDE_PATH=claude`) + real provider credentials +
150
- a real matter. Validates model/vendor OUTPUT QUALITY, not machinery (Tiers 0–2 already proved that).
151
- Run it once, at cutover — it bills a real matter against real vendor credentials, so it is not a
150
+ a real request. Validates model/vendor OUTPUT QUALITY, not machinery (Tiers 0–2 already proved that).
151
+ Run it once, at cutover — it bills a real clearance against real vendor credentials, so it is not a
152
152
  loop you repeat to debug something Tier 1 could have shown you.
153
153
 
154
154
  ## Engine selection is process-wide — what that rules out
@@ -159,7 +159,7 @@ stage of every job in that activation**. There is no per-stage override and no p
159
159
 
160
160
  Two round shapes this rules out, worth knowing before a plan assumes them:
161
161
 
162
- - **A same-instance parallel A/B is not available.** Comparing codex against anthropic on one matter
162
+ - **A same-instance parallel A/B is not available.** Comparing codex against anthropic on one request
163
163
  means flipping `CLEAROTRON_AI` and running the arms **sequentially**, or standing up a second instance
164
164
  with its own env, pool and ports. Two engines cannot run concurrently under one driver.
165
165
  - **Reviewer family diversity is not available** by routing one stage elsewhere. Sending the refutation
@@ -195,7 +195,7 @@ model-attributable — on 2026-08-15. Each failure was cheap; the sequence was n
195
195
 
196
196
  **Ask whether the artifacts already separate the variables, before spending a run.** That question was
197
197
  finally answered with no new run at all: within the runs already in hand, two axes were clean 9/9 while
198
- a third faulted 9/9 — same model, same matter, same date, same provider. A within-run comparison
198
+ a third faulted 9/9 — same model, same mark, same date, same provider. A within-run comparison
199
199
  attributes by construction. Three runs were spent discovering that.
200
200
 
201
201
  **Read the instrument's own vitals before you read its result.** One of the three failures was a spawn
package/docs/GLOSSARY.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Words this codebase uses in a particular way. They are here because they are already in the tree —
4
4
  in file names, comments and test titles — and a contributor meeting one should not have to reverse
5
- it out of the code. Product vocabulary a client would meet is in [`../README.md`](../README.md); the
6
- tenant, account and project model is in [`../INSTALL.md`](../INSTALL.md) under "The four things, and
5
+ it out of the code. Product vocabulary a user meets is in [`../README.md`](../README.md); the
6
+ organisation, company and project model is in [`../INSTALL.md`](../INSTALL.md) under "The four things, and
7
7
  what contains what".
8
8
 
9
9
  Nothing here is a rule. Each line says what the word points at, and names the file that owns it.
package/docs/INTAKE.md CHANGED
@@ -43,11 +43,11 @@ the default agent's workspace queue.
43
43
  - neither `classes` (or `marks[].classes`) **nor** a goods description (`goods`/`use`) —
44
44
  either one suffices, and the request is not the only place classes may come from. When it
45
45
  names none, the door resolves the same ladder the run does (request → saved search →
46
- project overlay → customer profile, `effective-scope.mjs`) and admits the job if any layer
46
+ project overlay → company profile, `effective-scope.mjs`) and admits the job if any layer
47
47
  supplies them, recording which one in a warning. A request under a project that carries the
48
48
  classes is therefore admitted, not clarified. The clarify stands only when no layer has any,
49
49
  and it then names what was consulted
50
- - a `profileKey` that names no known customer (a typo must clarify, never silently
50
+ - a `profileKey` that names no known company (a typo must clarify, never silently
51
51
  mis-route to the generic profile on a paid run)
52
52
  - an unknown `product`/`recipeKey`, both selectors set at once, a `deliveryRoute`
53
53
  outside `email | portal`, `caseLaw` or `nativeLanguage: false` (neither is a request setting —
@@ -70,7 +70,7 @@ the default agent's workspace queue.
70
70
 
71
71
  Other consumed fields (see `EXAMPLE_JOB` in `enqueue-schema.mjs` for the full annotated shape):
72
72
  `forwarderEmail`, `forwarderDomain`, `provider`, `marks[] = [{ref,name,classes}]`, `customer`
73
- (applicant → self-exclusion set), `profileKey` (customer account → profile/framework/template),
73
+ (applicant → self-exclusion set), `profileKey` (the company → profile/framework/template),
74
74
  `upfrontInstructions`, `brief`, `rawRequest`, `deliverableSpec`, `commercialFlexibility`,
75
75
  `priorUse`, `deadline` (ISO-8601, drives the deadline envelope), `enqueuedAt`, and
76
76
  `conversationId` — best-effort email-thread id consumed by the **matter dedup** below.
@@ -79,11 +79,11 @@ machinery runs:
79
79
 
80
80
  - `jurisdictions` — the instructed territories, e.g. `["US","EU","JP"]`. Present ⇒ **authoritative**:
81
81
  the matter frame is told not to widen past them, the register plan derives its regions from them and
82
- the native-language lanes deepen only inside them. Omit ⇒ the project/customer `defaultJurisdictions`. Names and
83
- codes both read; deduped case-insensitively; max 20.
82
+ the native-language lanes deepen only inside them. Omit ⇒ the project's or the company's
83
+ `defaultJurisdictions`. Names and codes both read; deduped case-insensitively; max 20.
84
84
  - `platforms` — extra marketplaces to sweep, as bare store domains. **Additive only**: unioned onto
85
- the account's own before the profile freeze, so the common-law grid floor and batch size re-derive
86
- from the surface actually swept. A request can widen a client's mandated marketplaces and has no way
85
+ the company's own before the profile freeze, so the common-law grid floor and batch size re-derive
86
+ from the surface actually swept. A request can widen a company's mandated marketplaces and has no way
87
87
  to narrow them. Max 10; `web` is implicit and must not be listed.
88
88
 
89
89
  `platforms` belongs to the **clearance** pipeline alone. A knockout has no marketplace grid to add to
@@ -98,13 +98,13 @@ the instructed territories when the request names any, and "Global — all juris
98
98
  names none (`driver/stages-knockout.mjs`).
99
99
 
100
100
  Selection fields (`driver/products.mjs` declares the four): `product` (one of the four in the offering), `recipeKey` (a
101
- saved customer search — mutually exclusive with `product`), `nativeLanguage` (the one toggle, and only
101
+ company's search template — mutually exclusive with `product`), `nativeLanguage` (the one toggle, and only
102
102
  on a Multi-country focus search — `true` adds it, and `false` is refused because it never switched
103
103
  anything off), `geography` (`{mode: worldwide|named|account-default}` — stated,
104
104
  because "everywhere" and "I said nothing" resolve differently), `deliveryRoute` (`email` default |
105
105
  `portal`), `parentRunId` (escalation lineage). `caseLaw` is NOT a field: it is what a Full country
106
106
  search IS, and sending it is refused rather than dropped. The runner's admission gate resolves
107
- job → project → customer → **the resolved scope** and PARKS AS CLARIFY any selection this
107
+ job → project → company → **the resolved scope** and PARKS AS CLARIFY any selection this
108
108
  build/deployment cannot run — never a silent substitution, never a drop.
109
109
 
110
110
  ### Prose sidecars (optional)
@@ -141,10 +141,10 @@ above each queue:
141
141
 
142
142
  - **signature** dimension: normalized `forwarder|mark|classes|customer|ref`
143
143
  - **thread** dimension: same `conversationId` **and** same mark (distinct marks forwarded in
144
- one email are separate matters and all run)
144
+ one email are separate requests and all run)
145
145
 
146
146
  A duplicate parks as `.duplicate` (recoverable), never runs. `"dupOverride": true` is the
147
- explicit requester-confirmed force-run (it still records the matter, so later true duplicates
147
+ explicit requester-confirmed force-run (it is still recorded, so later true duplicates
148
148
  are caught). A **failed** run drops its ledger entry so a genuine re-send is never blocked.
149
149
 
150
150
  ## Relevant environment