clearotron 0.2.4 → 0.3.0-beta.1
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.
- package/.env.example +13 -2
- package/CONTRIBUTING.md +1 -1
- package/INSTALL.md +62 -38
- package/bin/brandowner.mjs +18 -169
- package/bin/clearotron.mjs +3 -1
- package/bin/connect.mjs +28 -19
- package/bin/disconnect.mjs +3 -3
- package/bin/example.mjs +7 -7
- package/bin/framework-preflight.mjs +49 -0
- package/bin/grant.mjs +151 -93
- package/bin/onboard.mjs +220 -138
- package/bin/start.mjs +146 -137
- package/bin/stop.mjs +2 -2
- package/bin/update.mjs +1 -1
- package/build-info.json +2 -2
- package/docs/CLIENT-MCP.md +2 -2
- package/docs/E2E.md +12 -2
- package/docs/ONBOARDING.md +1 -1
- package/docs/PORTAL.md +14 -13
- package/docs/SECURITY.md +23 -24
- package/docs/architecture/04-configuration-reference.md +12 -5
- package/docs/architecture/05-config-governance.md +7 -7
- package/docs/architecture/07-quality-and-audit.md +1 -1
- package/docs/architecture/08-development-guide.md +5 -0
- package/docs/configuration.md +118 -0
- package/docs/decisions/0004-documentation-structure.md +2 -2
- package/docs/decisions/0006-what-the-public-repository-carries.md +2 -2
- package/driver/CHANGELOG.md +70 -0
- package/driver/ask-ledger.mjs +2 -2
- package/driver/cancel.mjs +27 -0
- package/driver/case-law-sources.mjs +3 -3
- package/driver/company-bundle.mjs +261 -0
- package/driver/compare.mjs +1 -1
- package/driver/compose-read.mjs +2 -2
- package/driver/config-inventory.mjs +2 -2
- package/driver/contract-audit.mjs +1 -1
- package/driver/contract-e3-backlog.mjs +1 -1
- package/driver/contract-vocabulary.mjs +1 -1
- package/driver/declination-call.mjs +1 -1
- package/driver/deliver-trigger.sh +3 -3
- package/driver/dev-portal.mjs +3 -1
- package/driver/digest-queue.mjs +1 -1
- package/driver/disposition-tool.mjs +1 -1
- package/driver/doc-constants.mjs +1 -1
- package/driver/drain-posture.mjs +2 -2
- package/driver/drainer-identity.mjs +1 -1
- package/driver/driver.config.mjs +44 -9
- package/driver/effective-scope.mjs +30 -1
- package/driver/effort-model.mjs +6 -6
- package/driver/engine/CONTRACT.md +2 -2
- package/driver/engine/anthropic-agent.mjs +11 -11
- package/driver/engine/jx-turn.mjs +1 -1
- package/driver/engine/mcp/gather-config.mjs +29 -4
- package/driver/engine/mcp/recording-server.mjs +73 -1
- package/driver/engine/openai-agent.mjs +1 -1
- package/driver/engine/probe.mjs +28 -4
- package/driver/enqueue-schema.mjs +23 -3
- package/driver/findings-model.mjs +2 -2
- package/driver/flag-snapshot.mjs +2 -2
- package/driver/floor-duty.mjs +2 -2
- package/driver/frame-diff-model.mjs +1 -1
- package/driver/framework-preflight.mjs +143 -0
- package/driver/gateway.mjs +9 -1
- package/driver/hit-list.mjs +1 -1
- package/driver/jx-lanes.mjs +1 -1
- package/driver/jx.mjs +1 -1
- package/driver/knockout-assess-record.mjs +1 -1
- package/driver/knockout-review-record.mjs +435 -0
- package/driver/order-probe.mjs +1 -1
- package/driver/outbox-backoff.mjs +2 -2
- package/driver/owner-use-check.mjs +2 -2
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +105 -8
- package/driver/pipeline.mjs +81 -30
- package/driver/plain-register.mjs +77 -3
- package/driver/portal-access.mjs +141 -74
- package/driver/portal-config-view.mjs +59 -70
- package/driver/portal-report.mjs +4 -4
- package/driver/portal-service.mjs +348 -141
- package/driver/portal-upstream.mjs +105 -17
- package/driver/predelivery-lint.mjs +43 -18
- package/driver/product-rows.mjs +1 -1
- package/driver/products.mjs +1 -1
- package/driver/profile-page.html +30 -5
- package/driver/profile-service.mjs +197 -26
- package/driver/profiles.mjs +48 -1
- package/driver/publish/index.mjs +31 -13
- package/driver/publish/knockout.mjs +9 -5
- package/driver/publish/office-record-links.mjs +189 -0
- package/driver/publish/parse.mjs +3 -3
- package/driver/publish/publish-inputs.mjs +26 -0
- package/driver/publish/render-knockout.mjs +42 -42
- package/driver/publish/render.mjs +29 -4
- package/driver/publish/report-data.mjs +2 -2
- package/driver/publish/seed-pool.mjs +1 -1
- package/driver/publish/templates/report.css +8 -8
- package/driver/publish/xlsx.mjs +49 -7
- package/driver/queue-watch-verdict.mjs +2 -2
- package/driver/recipe-service.mjs +1 -1
- package/driver/record-carry.mjs +1 -1
- package/driver/reference-score.mjs +1 -1
- package/driver/reference-strip-signatures.mjs +1 -1
- package/driver/register-availability.mjs +4 -3
- package/driver/register-count.mjs +3 -3
- package/driver/register-records.mjs +1 -1
- package/driver/repair-composers.mjs +1 -1
- package/driver/repairs.mjs +3 -3
- package/driver/replay-archive.mjs +1 -1
- package/driver/report-card-record.mjs +1 -1
- package/driver/result-noun-fields.mjs +5 -0
- package/driver/roster-verdict.mjs +48 -5
- package/driver/run-activity.mjs +1 -1
- package/driver/run-requirements.mjs +18 -5
- package/driver/runner.mjs +24 -15
- package/driver/search-policy.mjs +8 -8
- package/driver/senior-rights.mjs +1 -1
- package/driver/skills/prelim-search/delivery-contract.md +1 -1
- package/driver/skills/prelim-search/risk-framework-triage.md +10 -7
- package/driver/stages-knockout.mjs +72 -6
- package/driver/stages.mjs +10 -10
- package/driver/suite-census.json +238 -70
- package/driver/synthesis-record.mjs +2 -2
- package/driver/systemd/clearotron-client-mcp.service +3 -3
- package/driver/systemd/clearotron-deploy.service +2 -2
- package/driver/systemd/clearotron-mcp-face.service +1 -1
- package/driver/systemd/clearotron-portal.service +3 -3
- package/driver/systemd/clearotron-worker.service +5 -5
- package/driver/systemd/install-census.mjs +1 -1
- package/driver/systemd/render-units.mjs +9 -9
- package/driver/terminal-clamp.mjs +1 -1
- package/driver/trigger-cap.mjs +18 -2
- package/driver/unit-inventory.mjs +8 -8
- package/driver/usage-ledger.mjs +5 -3
- package/driver/verify-knockout.mjs +7 -7
- package/driver/verify.mjs +5 -5
- package/driver/whatif-memo-run.mjs +1 -1
- package/driver/whatif-queue.mjs +3 -3
- package/driver/whatif-worker.mjs +2 -2
- package/examples/README.md +1 -1
- package/examples/grants.example.json +25 -24
- package/mcp-server/CHANGELOG.md +10 -0
- package/mcp-server/http-server.mjs +3 -3
- package/mcp-server/key-socket.mjs +1 -1
- package/mcp-server/lib/audit-view.mjs +3 -3
- package/mcp-server/lib/brief.mjs +3 -3
- package/mcp-server/lib/driver.mjs +1 -1
- package/mcp-server/lib/events.mjs +1 -1
- package/mcp-server/lib/http-handler.mjs +2 -2
- package/mcp-server/lib/instructions.mjs +2 -2
- package/mcp-server/lib/knockout.mjs +1 -1
- package/mcp-server/lib/ops.mjs +6 -3
- package/mcp-server/lib/options.mjs +15 -4
- package/mcp-server/lib/plan.mjs +7 -6
- package/mcp-server/lib/runs.mjs +10 -0
- package/mcp-server/lib/whatif.mjs +5 -5
- package/mcp-server/package.json +1 -1
- package/mcp-server/packs/README.md +1 -1
- package/mcp-server/remote/client-mcp-apikey.service +1 -1
- package/mcp-server/remote/client-mcp.service +2 -2
- package/mcp-server/remote/trademark-artifacts-http.service +1 -1
- package/mcp-server/server.mjs +38 -24
- package/package.json +2 -2
- package/portal-ui/dist/assets/{index-KFAHMgdT.js → index-CWTHP0sH.js} +3471 -1901
- package/portal-ui/dist/assets/{index-1ziUJX1E.css → index-KpytsmNH.css} +79 -26
- package/portal-ui/dist/index.html +2 -2
- package/portal-ui/package.json +1 -1
- package/providers/_shared/lane-probe.mjs +9 -3
- package/providers/jx-subclass/lookup.mjs +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/oauth-mcp-bridge/systemd/courtlistener-mcp.service +1 -1
- package/scripts/citation-drift-report.mjs +1 -1
- package/scripts/citation-line-check.mjs +2 -2
- package/scripts/drive-env-check.mjs +1 -1
- package/scripts/e2e.mjs +5 -5
- package/scripts/env-audit.mjs +13 -1
- package/scripts/headless-page.mjs +5 -5
- package/scripts/live-surface-check.mjs +26 -2
- package/scripts/mint-names-in-force.mjs +19 -5
- package/scripts/mint-reference-strip-backlog.mjs +1 -1
- package/scripts/mint-suite-census.mjs +37 -10
- package/scripts/pack-publishable.mjs +1 -1
- package/scripts/preinstall-node-check.mjs +1 -1
- package/scripts/release-await-cut.mjs +3 -3
- package/scripts/release-cut-decision.mjs +1 -1
- package/scripts/release-dist-tag.mjs +1 -1
- package/scripts/release-install-check.mjs +1 -1
- package/scripts/release-notes-lint.mjs +1 -1
- package/scripts/release-publish-guard.mjs +1 -1
- package/scripts/release-version-pr-checks.mjs +2 -2
- package/scripts/release-version.mjs +61 -5
- package/scripts/render-brand-banner.mjs +1 -1
- package/scripts/render-check.mjs +2 -2
- package/scripts/repo-writes.mjs +1 -1
- package/scripts/report-frame-check.mjs +1 -1
- package/scripts/report-screenshot.mjs +2 -2
- package/scripts/retire-bare-refs.mjs +1 -1
- package/scripts/revisit-render-check.mjs +1 -1
- package/scripts/score.mjs +1 -1
- package/scripts/strip-titles-and-attributions.mjs +389 -0
- package/scripts/strip-tracker-citations.mjs +122 -5
- package/scripts/test-run.mjs +4 -4
- package/scripts/third-party-notices.mjs +1 -1
- package/scripts/verify-publishable.mjs +1 -1
- package/shared/access-audience.mjs +2 -2
- package/shared/anon-overlay.mjs +1 -1
- package/shared/brand.mjs +15 -1
- package/shared/bundle-freshness.mjs +1 -1
- package/shared/bundle-rebuild.mjs +1 -1
- package/shared/checkout-move.mjs +2 -2
- package/shared/client-door.mjs +8 -8
- package/shared/connect-clients.mjs +7 -7
- package/shared/connector-signin-probe.mjs +1 -1
- package/shared/env-aliases.mjs +1 -1
- package/shared/env-local.mjs +5 -5
- package/shared/grants-edit.mjs +76 -0
- package/shared/install-auth.mjs +1 -1
- package/shared/listen.mjs +3 -3
- package/shared/mcp-challenge.mjs +1 -1
- package/shared/names-in-force.mjs +2 -1
- package/shared/onboarding-store.mjs +19 -2
- package/shared/reference-guard-classes.mjs +44 -2
- package/shared/register-selection.mjs +1 -1
- package/shared/scope.mjs +223 -56
- package/shared/secret-file.mjs +1 -1
- package/shared/server-units.mjs +1 -1
- package/shared/staff-domain.mjs +45 -78
- package/shared/summary-blocks.mjs +2 -2
- package/shared/systemd-failure.mjs +3 -3
- package/shared/tracked-files.mjs +1 -1
- package/shared/trigger-lane.mjs +1 -1
- package/shared/tty-style.mjs +1 -1
- package/shared/usage-block.mjs +1 -1
- package/shared/vacuous-pass.mjs +1 -1
- package/shared/verb-shim.mjs +1 -1
package/docs/CLIENT-MCP.md
CHANGED
|
@@ -45,7 +45,7 @@ accounts their email is granted (`CLEAROTRON_ACCESS_FILE` — the same guest lis
|
|
|
45
45
|
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
|
-
**Who turns that on. The installer, since 2026-09-03** —
|
|
48
|
+
**Who turns that on. The installer, since 2026-09-03** — ruling, settled
|
|
49
49
|
point 2. `render-units.mjs --apply` and `npx clearotron start --background` both write the settings this
|
|
50
50
|
door refuses to start without and then place and enable `clearotron-client-mcp.service`. The settings
|
|
51
51
|
come from one authority, `enablePlan` in `shared/client-door.mjs`, which is also what
|
|
@@ -79,7 +79,7 @@ under the report, not just its prose. It projects named structured fields and en
|
|
|
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
|
|
82
|
+
**The audit chain is open by ruling, 2026-08-27** ("I don't see why we don't open it or just
|
|
83
83
|
give it to clients. Ignore the call spend."). The same lawyer who needs the records also has to be
|
|
84
84
|
able to show *how* the answer was reached, so the decision chain is client product now. Unlike the
|
|
85
85
|
evidence layer this one does forward prose — a chain of reasoning is prose — so it is bounded a
|
package/docs/E2E.md
CHANGED
|
@@ -6,6 +6,12 @@ How to prove a deployment of this product works end to end, at four cost tiers.
|
|
|
6
6
|
doubles as the **dev/prod instance split**: a dev instance is just a second env-file pointing every
|
|
7
7
|
data-plane path somewhere isolated — the code is identical.
|
|
8
8
|
|
|
9
|
+
> **This page is written for a checkout, and the installed package is not one.** The published package
|
|
10
|
+
> deliberately excludes the test tree, so `driver/test/` does not exist in an npm install — Tier 0 and
|
|
11
|
+
> the `$0` mock engine below are reachable only from a clone. Everything from Tier 1 down applies to an
|
|
12
|
+
> installed deployment as written. If a path here starting `driver/test/` is not on your disk, that is
|
|
13
|
+
> the reason, and it is not a mistake in your configuration.
|
|
14
|
+
|
|
9
15
|
## Tier 0 — offline, in-repo (already in `npm run test:full`, $0)
|
|
10
16
|
|
|
11
17
|
`driver/test/pipeline.anthropic.test.mjs` runs the full clearance pipeline on the production engine
|
|
@@ -38,10 +44,10 @@ CLEAROTRON_WORK_DIR=/home/you/trademark-dev/workspace
|
|
|
38
44
|
CLEAROTRON_REPORTS_DIR=/home/you/trademark-dev/pool
|
|
39
45
|
CLEAROTRON_OUTBOX_DIR=/home/you/trademark-dev/outbox
|
|
40
46
|
CLEAROTRON_AI=anthropic-agent
|
|
41
|
-
CLEAROTRON_CLAUDE_PATH=/home/you/clearotron/driver/test/mock-claude.mjs # $0 mock engine; ABSOLUTE — see below
|
|
47
|
+
CLEAROTRON_CLAUDE_PATH=/home/you/clearotron/driver/test/mock-claude.mjs # $0 mock engine; CHECKOUT ONLY, and ABSOLUTE — see below
|
|
42
48
|
MOCK_VERDICT=CLEAR
|
|
43
49
|
MOCK_SKEPTIC=no flags surfaced # clean skeptic pass under the mock (quotes optional; both work)
|
|
44
|
-
CLEAROTRON_DATABASE=corsearch # REQUIRED, no default
|
|
50
|
+
CLEAROTRON_DATABASE=corsearch # REQUIRED, no default — the mock never calls it
|
|
45
51
|
CORSEARCH_SESSION_KEY=dev-offline # the credential preflight wants it set; never fetches under the mock
|
|
46
52
|
CLEAROTRON_SATPROBE_CODESIDE=0 # the probe dials the provider; a mock run cannot
|
|
47
53
|
CLEAROTRON_BAND_TRUTH_GATE=0 # the gate evidences bands against the production call ledger
|
|
@@ -51,6 +57,10 @@ CLEAROTRON_CUSTOMERS_DIR= # unset ⇒ the dem
|
|
|
51
57
|
# grants file every token-less caller resolves to internal read-all across every customer.
|
|
52
58
|
```
|
|
53
59
|
|
|
60
|
+
**From an installed package there is no such file at all** — the test tree is excluded from it, so
|
|
61
|
+
before reading the next paragraph check that the path exists. That check is worth making first because
|
|
62
|
+
the failure below looks the same and has a different cause.
|
|
63
|
+
|
|
54
64
|
`CLEAROTRON_CLAUDE_PATH` must be absolute, and this is the one that catches everybody: the engine child is
|
|
55
65
|
spawned with the RUN DIRECTORY as its cwd, so`driver/test/mock-claude.mjs` is looked for
|
|
56
66
|
*inside the run* and never found. A run now refuses at preflight, before it creates a run directory,
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -41,7 +41,7 @@ store, and writes nothing — so the first real run is one you have already read
|
|
|
41
41
|
|
|
42
42
|
## The framework is always set, and the output says which one
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Ruling, 2026-08-29: the risk framework is mandatory at onboarding, with a default backup so
|
|
45
45
|
onboarding is never blocked. The command therefore sets a framework on every bundle it writes, and
|
|
46
46
|
prints which one it used.
|
|
47
47
|
|
package/docs/PORTAL.md
CHANGED
|
@@ -19,11 +19,12 @@ enrolment — and none of it is done by installing this repo.
|
|
|
19
19
|
|
|
20
20
|
## The model
|
|
21
21
|
|
|
22
|
-
| Who | Sees |
|
|
22
|
+
| Who | Sees and does |
|
|
23
23
|
|---|---|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
24
|
+
| A person with access to everything | every organisation, company and person, naming a company on scoped routes (never an implicit install-wide default) |
|
|
25
|
+
| A person with access to an organisation or a company | everything below those points: its companies and their runs and reports, and — for an organisation — its Generic |
|
|
26
|
+
| Run clearances · Manage | the two switches each person holds: start and stop clearances; add people, add companies and change settings — both inside the person's access |
|
|
27
|
+
| Anyone else | 403 at the door; probes outside a person's access read as **404** (existence never leaks) |
|
|
27
28
|
|
|
28
29
|
The enrolment substrate IS the grants file (`CLEAROTRON_ACCESS_FILE`, [the operations runbook](architecture/06-operations-runbook.md#access-control-and-instance-isolation)
|
|
29
30
|
for the shape and `examples/grants.example.json` for a runnable one) — portal
|
|
@@ -33,12 +34,12 @@ whichever door this instance runs, then granted here; the model is stated once i
|
|
|
33
34
|
[docs/SECURITY.md](SECURITY.md). On a proxied instance that means
|
|
34
35
|
`MCP_ALLOWED_EMAIL_DOMAINS` and/or `MCP_ALLOWED_EMAILS` (no default — the portal
|
|
35
36
|
refuses to start with neither set, and combines the two as a UNION rather than the verifier's default
|
|
36
|
-
intersection) must admit
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
intersection) must admit every person in the guest list: your own domain in the domain list, and
|
|
38
|
+
individually named outside addresses in `MCP_ALLOWED_EMAILS` rather than their whole domain — admitting a
|
|
39
|
+
consumer domain wholesale to enrol one person would put every address on it through the door, leaving
|
|
40
|
+
the grants file as the only wall. A run with no company tag is shown only to a person with access to
|
|
41
|
+
everything, and a Generic run only inside the organisation it was filed under. A person with several
|
|
42
|
+
companies enters the door normally and picks one (`/portal/api/me` returns the list).
|
|
42
43
|
The one-shot confirmation store is in memory, which bounds the deployment to **one portal process per
|
|
43
44
|
instance**: a second process would not see the first's CONSUMED jtis, so the one-shot guarantee fails
|
|
44
45
|
OPEN rather than closed — the token itself verifies anywhere (both processes hold the same
|
|
@@ -108,7 +109,7 @@ What that command starts, for anyone who needs to drive the portal on its own:
|
|
|
108
109
|
```bash
|
|
109
110
|
# the portal with the LOCAL identity source (no CF Access, loopback only)
|
|
110
111
|
PORTAL_AUTH_MODE=local PORTAL_LOCAL_USER=cli@celta.example \
|
|
111
|
-
|
|
112
|
+
CLEAROTRON_ACCESS_FILE=$HOME/trademark-dev/grants.json \
|
|
112
113
|
PORTAL_SECRET="$(openssl rand -base64 32)" CLEAROTRON_REPORTS_DIR=$HOME/trademark-dev/pool \
|
|
113
114
|
CLEAROTRON_WORK_DIR=$HOME/trademark-dev/workspace node driver/portal-service.mjs
|
|
114
115
|
# → http://127.0.0.1:18802/portal/login
|
|
@@ -121,8 +122,8 @@ and restarting mints a new passphrase.
|
|
|
121
122
|
|
|
122
123
|
What local mode does **not** change is who sees what. It produces an email address and stops;
|
|
123
124
|
`makePrincipal` and the `assertPrincipal` chokepoint judge it exactly as they judge a
|
|
124
|
-
proxy-verified address, so `PORTAL_LOCAL_USER` must ALSO
|
|
125
|
-
|
|
125
|
+
proxy-verified address, so `PORTAL_LOCAL_USER` must ALSO have an entry in the guest list —
|
|
126
|
+
`"everything": true` under `people` for the whole install. Sign in as an address the roster does not know and every
|
|
126
127
|
page refuses it at the door, which is the correct answer and is warned about at boot. The roster itself
|
|
127
128
|
stays mandatory: local mode has a population of one, and one is still a population.
|
|
128
129
|
|
package/docs/SECURITY.md
CHANGED
|
@@ -10,7 +10,7 @@ here corresponds to shipped behavior; when hardening changes, change this file i
|
|
|
10
10
|
| Surface | Trust | Guard |
|
|
11
11
|
|---|---|---|
|
|
12
12
|
| stdio MCP (`mcp-server/server.mjs`) | local/full ("ops") | OS user boundary — run it AS the operator account; it is the only surface on which `what_if_run` EXECUTES (`visibleTools` keeps what-if out of the HTTP listing for ops, but the CallTool chokepoint gates on `authorize()` alone, which admits it for any ops token not `--verbs`-scoped) |
|
|
13
|
-
| Client MCP (`mcp-server/http-server-client.mjs`) | signed-in client / account key | a client account's `what_if_run` ENQUEUES rather than executes (
|
|
13
|
+
| Client MCP (`mcp-server/http-server-client.mjs`) | signed-in client / account key | a client account's `what_if_run` ENQUEUES rather than executes (ruling 2026-08-27) — it never imports the engine, and `driver/whatif-worker.mjs` spawns the sandbox from an OS service process. A confirmation token is unsigned, so the call must ALSO name its `runId`: the account gate keys on it, and `whatIfEnqueue` refuses a token naming a different run. The `model` argument is refused to a client. |
|
|
14
14
|
| HTTP MCP (`mcp-server/http-server.mjs`) | authenticated remote | auth-BEFORE-data; fail-closed construction; inner scoped tokens |
|
|
15
15
|
| Report "Ask your AI" links | external report recipients | run-bound `user` tokens minted at publish; client layer only |
|
|
16
16
|
| Dev portal (`driver/dev-portal.mjs`) | dev only | loopback-only (throws on any other host); never production serving |
|
|
@@ -33,28 +33,27 @@ Three deployment shapes, and they are the whole set:
|
|
|
33
33
|
| **Shared or hosted** | `PORTAL_AUTH_MODE=auth-proxy` — any login system in front that authenticates in the browser and forwards a verifiable JWT per request (`cf-access` is the older word for this and still works) | Admit the address at your login system, **then** grant it in the guest list — both halves, always |
|
|
34
34
|
| **Neither configured** | the service refuses to start | — |
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
and
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
rule
|
|
51
|
-
|
|
52
|
-
**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
nothing and answer the question the install asks.
|
|
36
|
+
A person is admitted by their own entry in the guest list (`CLEAROTRON_ACCESS_FILE`) and by nothing
|
|
37
|
+
else. The guest list gives each person access to points on one tree — the whole install, an
|
|
38
|
+
organisation, or one company — and two switches, **Run clearances** and **Manage**. A person sees
|
|
39
|
+
everything below the points they were given and nothing else; both switches off is a person who can read
|
|
40
|
+
and start nothing. In the portal, `makePrincipal` → `assertPrincipal` is the only path to a decision and
|
|
41
|
+
no identity source may reach past it; the connector resolves a key's person through the same function
|
|
42
|
+
(`resolvePerson`, `shared/scope.mjs`), so the two doors cannot disagree about one address. The file is
|
|
43
|
+
re-read per request, so a change lands without a restart. `npm run grant` edits it, and so does anyone
|
|
44
|
+
with Manage from the People page, inside their own access.
|
|
45
|
+
|
|
46
|
+
**Nothing is admitted by the part of an address after its `@`.** The staff-by-domain rule
|
|
47
|
+
(`PORTAL_STAFF_DOMAINS`) is gone and the setting is ignored. A `*@domain` entry in the guest list still
|
|
48
|
+
names every address at that domain and may hold Run clearances; it may never hold Manage or access to
|
|
49
|
+
everything, and the guest list is refused at load if it tries, because a domain-wide Manage is the staff
|
|
50
|
+
rule by another name.
|
|
51
|
+
|
|
52
|
+
**Two organisations are invisible to each other because they are sibling branches.** A company belongs
|
|
53
|
+
to exactly one organisation, and the guest list is refused at load if a company is listed under two. A
|
|
54
|
+
clearance run with no company — Generic — is filed under the organisation it was started in and is
|
|
55
|
+
visible only inside it. A Generic run filed before organisations existed is visible only to a person
|
|
56
|
+
with access to everything.
|
|
58
57
|
|
|
59
58
|
|
|
60
59
|
- **On the proxy door**, every HTTP request re-validates a JWT from the fronting auth proxy. The
|
|
@@ -81,7 +80,7 @@ nothing and answer the question the install asks.
|
|
|
81
80
|
- Four principal kinds: **ops** (write verbs; automation/operator), **user** (read-only, pinned to
|
|
82
81
|
exactly ONE run — report recipients), **account** (a signed-in client across the accounts their
|
|
83
82
|
identity is granted: the client layer, the evidence layer — `list_evidence` / `list_searches` /
|
|
84
|
-
`get_search_coverage` — the AUDIT CHAIN (
|
|
83
|
+
`get_search_coverage` — the AUDIT CHAIN (ruling 2026-08-27: `read_artifact` over the chain
|
|
85
84
|
artifacts named in `ACCOUNT_ARTIFACTS`, `list_findings` on the raw `kind` path, `get_finding`,
|
|
86
85
|
`get_run`, `trace`, `decision_timeline`), WHAT-IF as a queued sandbox job (`what_if_plan`,
|
|
87
86
|
`what_if_run`, `what_if_result`), and the run lifecycle on their own runs, and nothing else. All of it accountSafe and deliberately NOT clientSafe, because a report link is forwardable
|
|
@@ -173,7 +173,7 @@ timeout shot, warm patch, backoff), the lane-wedge re-dispatch, and the rate-lim
|
|
|
173
173
|
| `CLEAROTRON_AI_BILLING` | `subscription` (default) \| `api-key` | Billing mode for the selected engine. ONE variable for both, and only the LIVE engine's setting is read — it fills each engine's billing knob, and the engine that is not selected is never consulted. **`anthropic-agent`:** subscription **deletes `ANTHROPIC_API_KEY` from the child env** so `claude -p` uses OAuth subscription credentials (a present key would override them); `api-key` keeps the key — the scale setting and standing fallback. **`openai-agent`:** subscription seeds `auth.json` into the per-run `CODEX_HOME` from `CLEAROTRON_OPENAI_AUTH_FILE` (default `~/.codex/auth.json`) and strips API keys; `api-key` keeps `CODEX_API_KEY`. **Fail-loud either way:** `api-key` with no `ANTHROPIC_API_KEY` / `CODEX_API_KEY` throws, and never silently bills the subscription. The resolved mode is stamped on every stage telemetry row (`engine`, `authMode`, `apiBilled`). |
|
|
174
174
|
| `CLEAROTRON_CLAUDE_PATH` · `CLEAROTRON_CODEX_PATH` | `claude` / `codex` on `PATH` | The engine binary to spawn, ONE PER ENGINE: `CLEAROTRON_CLAUDE_PATH` under `anthropic-agent`, `CLEAROTRON_CODEX_PATH` under `openai-agent`. Only the live engine's is read, and a box that runs both sets both — they were one variable until it met a box needing two different paths. Give it an **absolute** path if the binary is not on `PATH` — stage subprocesses run with cwd set to the run directory, so a relative path does not resolve there, and `npx clearotron doctor` refuses one. |
|
|
175
175
|
| `CLEAROTRON_OPENAI_AUTH_FILE` | `~/.codex/auth.json` | The credentials file seeded into the per-run `CODEX_HOME` under subscription billing. Was exempted from the August 2026 rename as an `openai-agent` internal rather than an install-surface name; the owner's 2026-09-04 ruling **reversed that exemption** and renamed the whole namespace, so it carries the house prefix like everything else. One spelling, no exceptions. |
|
|
176
|
-
| `CLEAROTRON_OPENAI_MODEL_JUDGMENT` / `CLEAROTRON_OPENAI_MODEL_SWEEP` / `CLEAROTRON_OPENAI_MODEL_CHEAP` | `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`. | Maps the driver's abstract `opus`/`sonnet`/`haiku` tiers onto the codex ladder, the way the anthropic engine maps onto its own (
|
|
176
|
+
| `CLEAROTRON_OPENAI_MODEL_JUDGMENT` / `CLEAROTRON_OPENAI_MODEL_SWEEP` / `CLEAROTRON_OPENAI_MODEL_CHEAP` | `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`. | Maps the driver's abstract `opus`/`sonnet`/`haiku` tiers onto the codex ladder, the way the anthropic engine maps onto its own (ruling 2026-09-02, superseding the single-model mapping). A tier that cannot hold its stage contract announces itself at that stage — `missing:negative-results`, `no_coverage_status_row`, `named_band_missing` in `_driver/<stage>.jsonl` — not at delivery. |
|
|
177
177
|
| ↳ why they are three, and what the old measurement still says | superseded 2026-09-02, not erased | The three defaulted to `sol` alone on a MEASUREMENT, never a placeholder: on 2026-08-11/12, with `SWEEP=gpt-5.6-terra` / `CHEAP=gpt-5.6-luna`, every codex clearance died on structural-output gates — `missing:negative-results`, `no_coverage_status_row`, `named_band_missing` — over 2 scenarios × 3 stage families, **byte-identically on retry**, so no retry budget rescued it. That measurement stands for the code and the codex CLI **of that date**, and it is why the judgment tier keeps `sol`. Three weeks of stage-contract work and several codex minor versions sit between it and the 2026-09-02 ruling that split the three. **The symptom to match against this cause is unchanged**: those same three gates, as a fail row in`_driver/<stage>.jsonl`, inside the first few dispatches of a clearance and identical on every retry — a model incapable of a contract is not flaky at it. It reads as an engine bug when the only fault is this configuration. A cheap-tier experiment still belongs in the three constants in `driver/engine/openai-agent.mjs` where a reviewer sees it, not in an untraceable env line. |
|
|
178
178
|
| `CLEAROTRON_DATABASE` | **REQUIRED — no default.** `corsearch` \| `clarivate` \| `signa` \| `euipo` \| `uspto-local` \| `free-tier` | Active register provider — one per run, set in the environment the deploy carries, in every environment including production. There is **no default**: unset resolves to `null` and every use of it throws, so a run refuses at start rather than calling a vendor nobody chose ( — the removed`corsearch` fallback named a vendor the deployment did not choose). Three are paid global sweeps; `euipo` (EU) and `uspto-local` (US) are free single-office sources, and `free-tier` composes those two as one register. Choosing a free value makes every territory outside its coverage a disclosed *deferred* row. Unknown ids throw loudly, and the gather layer throws at stage time for any provider without a built MCP server. |
|
|
179
179
|
| `CLEAROTRON_CODEX_SANDBOX_BYPASS` | unset | `1` runs `codex exec` with its own sandbox helper bypassed, for hosts where that helper cannot spawn. It removes a defence: with it set, the run dir is writable by the seat and only the deny-hook stands between a stage and `_driver/`. Set it because the host forces it, never for convenience. |
|
|
@@ -282,9 +282,10 @@ because the rule is about what PRODUCT CODE reads, not about what a run reads.
|
|
|
282
282
|
| Var | Default | Meaning |
|
|
283
283
|
|---|---|---|
|
|
284
284
|
| `DEMO_PORT` | `18900` | Port `npx clearotron demo` serves the replayed report on. `--port` overrides it. |
|
|
285
|
-
| `CLEAROTRON_DEMO` | unset | `1` puts this install in the DEMO posture. **Ordering is real**: the four products are listed and orderable, the form, the plan and the confirmation are the product's own, and the confirmation resolves to a finished report that already exists rather than dispatching — no engine turn, no register call, no queue entry, no run directory (
|
|
285
|
+
| `CLEAROTRON_DEMO` | unset | `1` puts this install in the DEMO posture. **Ordering is real**: the four products are listed and orderable, the form, the plan and the confirmation are the product's own, and the confirmation resolves to a finished report that already exists rather than dispatching — no engine turn, no register call, no queue entry, no run directory (ruling 2026-08-31, superseding the greyed-control ruling of the same day). A product the demo carries no finished report for refuses and names which one. It also re-aims two boot warnings written for an operator of a real deployment at the visitor who is not one, from one place (`driver/demo-posture.mjs`). **Set by `npx clearotron demo`, not by an operator** — it is passed explicitly to the two processes that have a reason to know (the portal and the MCP door; the worker is not told, because a demo never queues anything for it to drain), and those run with `CLEAROTRON_NO_ENV_FILE=1`, so a stray `.env` can neither put a live install into demo mode nor take a demo out of one. Anything but the literal `1` is not a demo. Replaces `PORTAL_DEMO`, which named only one of the processes that has to know. |
|
|
286
286
|
| `CLEAROTRON_TEST_FIXTURE_PROFILES` | unset | `1` makes the profile loader return the three suite fixtures, which are refused from every roster otherwise. Set by `scripts/test-run.mjs`, never by an operator; an explicit `includeTestFixtures` argument beats it. Effect class `harness`; the full contract is its row in `.env.example`. |
|
|
287
|
-
| `CLEAROTRON_DEMO_PROFILES` | unset | `1` makes the profile loader return the bundled demo account, which a fresh install does not resolve (
|
|
287
|
+
| `CLEAROTRON_DEMO_PROFILES` | unset | `1` makes the profile loader return the bundled demo account, which a fresh install does not resolve (ruling 2026-09-08). Set by `clearotron demo`, `start --demo` and the suite runner, never by an operator. The gate is on the bundled layer, so a deployment's own configured store keeps its `demoData` accounts either way. Effect class `harness`; the full contract is its row in `.env.example`. |
|
|
288
|
+
| `CLEAROTRON_ORGANISATION_NAME` | unset | Your organisation's name, written quoted by `npx clearotron install` directly after the sign-in address. The first `clearotron start` files it as the first organisation in the grants file (`CLEAROTRON_ACCESS_FILE`) when that file holds none; from then on the grants file holds the name, renaming is an edit there, and this is not read. Unset ⇒ no organisation is invented. `clearotron start --organisation <name>` supplies it for one start, and a demo never reads it. Effect class `deployment`; the full contract is its row in `.env.example`. |
|
|
288
289
|
| `PORTAL_LOCAL_CREDENTIAL` | `~/.cordillera/portal-local-credential.json` | Where local sign-in keeps its passphrase DIGEST. `npx clearotron demo` points it inside the demo's own base directory, so a demo mints its own passphrase instead of inheriting a digest minted for another address — and removing the demo stays one `rm -rf`. |
|
|
289
290
|
| `PORTAL_LOCAL_PASSPHRASE` | unset | **NEVER set this in a file.** An internal one-shot handoff, not an operator control: on a first FOREGROUND start the supervisor mints the passphrase and hands it to the portal it spawns *at the spawn call*, so the closing summary can print the value beside the address rather than sending a first-time reader back into eleven startup log lines for the one value in this product that cannot be read back. It is deliberately absent from the composed child environments, because that composition is what `--background` writes into the units' env file — a passphrase there would be a permanent plaintext copy on disk and the product's own sentence, "it is stored only as a digest", would stop being true. Setting it in any env file recreates exactly that. Lost passphrase: `clearotron passphrase --reset`. |
|
|
290
291
|
| `PORTAL_URL` | `http://127.0.0.1:18802`, or built from `PORTAL_SERVICE_HOST`/`PORTAL_SERVICE_PORT` | Where the deploy tick's live-surface check expects to reach the portal. |
|
|
@@ -293,7 +294,7 @@ because the rule is about what PRODUCT CODE reads, not about what a run reads.
|
|
|
293
294
|
| `CLEAROTRON_RELEASE_WAIT_MS` | 25 minutes | How long a requested cut waits for the version pull request to merge itself before giving up. **Read only by the release workflow.** A rehearsal sets it to `0` so the wiring is exercised without holding a runner. Giving up is a quiet success by design — the scheduled run underneath catches a cut whose wait expired — so this budget failing shows up as a slow job rather than a red one. |
|
|
294
295
|
| `CLEAROTRON_AGENT_MCP_URL` | unset (⇒ `null`) | The API-key MCP door advertised to a signed-in client. Null until that door is deployed, and the UI keeps its honest empty state rather than inventing a URL. |
|
|
295
296
|
| `CLEAROTRON_AGENTS` | derived | Comma-separated agent ids for `scripts/purge-runs.mjs` to sweep. |
|
|
296
|
-
| `CLEAROTRON_E2E_DIR` | **none — the script refuses without it** | The config repo's `e2e/` directory. There is one suite and it is not in this repo (
|
|
297
|
+
| `CLEAROTRON_E2E_DIR` | **none — the script refuses without it** | The config repo's `e2e/` directory. There is one suite and it is not in this repo (ruling 2026-08-07), so the comparison script names the variable rather than defaulting anywhere. |
|
|
297
298
|
| `CLEAROTRON_E2E_EXPECT_DEMO_ROSTER` | unset | `1` makes the live-surface check REQUIRE the bundled demo roster. For a box that is meant to ship the demos; off elsewhere, so a real deployment is not failed for lacking them. |
|
|
298
299
|
|
|
299
300
|
### Gates that are OFF by default
|
|
@@ -345,7 +346,7 @@ cannot be read as one list.
|
|
|
345
346
|
|
|
346
347
|
### Retired — set these and nothing happens
|
|
347
348
|
|
|
348
|
-
|
|
349
|
+
The settings below were deleted. Nothing in any environment set them, so each became the constant it
|
|
349
350
|
had always resolved to. **They are listed because an operator whose `.env` still carries one needs to
|
|
350
351
|
know it is inert** — an unread setting is indistinguishable from a setting that works.
|
|
351
352
|
|
|
@@ -363,6 +364,12 @@ know it is inert** — an unread setting is indistinguishable from a setting tha
|
|
|
363
364
|
| `CLEAROTRON_SUPPLEMENTAL_PER_CALL` | 12 supplemental queries per call |
|
|
364
365
|
| `CLEAROTRON_WALL_RESCUE_QUIESCE_MS` | 60 seconds waiting for a killed tree to quiesce |
|
|
365
366
|
|
|
367
|
+
**`PORTAL_STAFF_DOMAINS` is retired differently, because deployments did set it.** Everyone at a listed
|
|
368
|
+
email domain used to be staff. Who may use the portal is now each person's own entry in the grants file
|
|
369
|
+
(`CLEAROTRON_ACCESS_FILE`), with `"everything": true` under `people` for anyone who should see every
|
|
370
|
+
company, and no domain admits anyone. A portal that still finds the setting starts, and says at boot that
|
|
371
|
+
it is ignored and what replaced it.
|
|
372
|
+
|
|
366
373
|
## Change management
|
|
367
374
|
|
|
368
375
|
- **Live env flip (no deploy):** caps, backoffs, feature gates, A/B toggles — edit the deployment's
|
|
@@ -69,7 +69,7 @@ Recurring confusion, settled: "we configure the MCP servers at the edge" is true
|
|
|
69
69
|
fills the same three roles, and the product requires none of them by name.
|
|
70
70
|
|
|
71
71
|
1. **Edge auth (T5, dashboard):** the tunnel routes each public hostname to a loopback port, and a
|
|
72
|
-
CF Access app decides *who* may reach it (
|
|
72
|
+
CF Access app decides *who* may reach it (your own domain; outside people per email; dev app).
|
|
73
73
|
2. **Local verification (T4, unit files):** every service *independently re-verifies* the CF Access
|
|
74
74
|
JWT. For that it needs the team + the app's AUD tag — so `CF_ACCESS_TEAM`/`CLEAROTRON_OIDC_AUDIENCE`/
|
|
75
75
|
`MCP_ALLOWED_EMAIL_DOMAINS` appear in unit files as **mirrors of the dashboard**, not a second
|
|
@@ -105,12 +105,12 @@ by name in §5 below and in the upgrade table in INSTALL.md.
|
|
|
105
105
|
who has not read the code. Vendor credentials keep the vendor’s name (`SIGNA_API_KEY`,
|
|
106
106
|
`PERPLEXITY_API_KEY`, `ANTHROPIC_API_KEY`) — that already says who you bought them from.
|
|
107
107
|
|
|
108
|
-
**THERE IS ONE SPELLING.** The compatibility window that read both closed on 2026-08-26 by
|
|
108
|
+
**THERE IS ONE SPELLING.** The compatibility window that read both closed on 2026-08-26 by ruling:
|
|
109
109
|
no migration, no legacy support. A deployment is REBUILT from the install rather than carried across,
|
|
110
110
|
which is also the only thing that proves the install works for a new reader — a migrated box tests a path
|
|
111
111
|
no new user ever walks.
|
|
112
112
|
|
|
113
|
-
**A retired spelling is not checked for either** —
|
|
113
|
+
**A retired spelling is not checked for either** — ruling, same day, asked directly. The premise
|
|
114
114
|
is what makes the absence correct rather than careless: a machine reaches this code through the install,
|
|
115
115
|
the install writes the names in force, and the boxes that predate the rename are rebuilt rather than
|
|
116
116
|
deployed onto. There is no population left holding the old lines.
|
|
@@ -294,7 +294,7 @@ it. Set-but-unreadable, malformed, or below the size floor **throws**: a truncat
|
|
|
294
294
|
smaller blocklist, and a smaller blocklist reads as a cleaner repo. **Whether the real table is required
|
|
295
295
|
is the CALLER's declaration, never the environment's**: `publication-scan.mjs` asks for it in its own
|
|
296
296
|
source and refuses by name before it runs a single check if it cannot reach it. There is no switch —
|
|
297
|
-
`CLEAROTRON_REQUIRE_BLOCKLIST` was deleted by
|
|
297
|
+
`CLEAROTRON_REQUIRE_BLOCKLIST` was deleted by ruling, because a variable that decides whether a scrub
|
|
298
298
|
guard looks for anything real has a silent OFF position, and silent-off is the one state this module
|
|
299
299
|
exists to make impossible: without the table, "matched nothing" and "had nothing to match on" are the
|
|
300
300
|
same green.
|
|
@@ -328,7 +328,7 @@ which collided with a co-hosted warm-MCP block, so keep any unit override that n
|
|
|
328
328
|
`CLIENT_MCP_SESSION_TTL_MS`, `CLIENT_MCP_SESSION_MAX`, `CLIENT_MCP_RATE_PER_MIN`, `CLIENT_MCP_URL`,
|
|
329
329
|
`CLIENT_MCP_TOKEN_ONLY`, `CLIENT_MCP_AUTH_DISABLED` / `CLIENT_MCP_DEV` (dev seams — never set in prod). CF mirrors (T5): `CF_ACCESS_TEAM`, `CLEAROTRON_OIDC_AUDIENCE`,
|
|
330
330
|
`CLEAROTRON_CLIENT_OIDC_AUDIENCE`, `MCP_ALLOWED_EMAIL_DOMAINS`, `MCP_ALLOWED_EMAILS`. Admin services:
|
|
331
|
-
`PORTAL_SERVICE_PORT` (18802) / `PORTAL_SERVICE_HOST`, `
|
|
331
|
+
`PORTAL_SERVICE_PORT` (18802) / `PORTAL_SERVICE_HOST`, `PORTAL_MCP_URL`,
|
|
332
332
|
`PORTAL_RATE_PER_MIN`, `PORTAL_LOCAL_WORKER` (set only by `bin/start.mjs` when it supervises a worker,
|
|
333
333
|
and what licenses the portal to say a queued job is waiting for one — a deployed instance drains via
|
|
334
334
|
systemd, writes no heartbeat, and must keep saying "waiting to start" rather than invent an alarm),
|
|
@@ -363,7 +363,7 @@ catch, so it has to be deliberate. The client door also passes `authHeader` into
|
|
|
363
363
|
it did not before, so its seam would have been settable and inert.
|
|
364
364
|
|
|
365
365
|
**Named in full rather than as a prefix**, because a row that abbreviates a family documents nothing an
|
|
366
|
-
operator can search for — and
|
|
366
|
+
operator can search for — and the rule's own second arm fails exactly that, which is how this table came
|
|
367
367
|
to be written out.
|
|
368
368
|
|
|
369
369
|
| service | issuer | JWKS | identity claim | token header |
|
|
@@ -391,7 +391,7 @@ the test box and production both bind 127.0.0.1 behind a tunnel.
|
|
|
391
391
|
|
|
392
392
|
Local mode adds two values and no third: `PORTAL_LOCAL_USER` is the one email address that signs in
|
|
393
393
|
(mandatory in that mode — the service refuses to start without it, and the address must ALSO be enrolled
|
|
394
|
-
in `CLEAROTRON_ACCESS_FILE
|
|
394
|
+
in `CLEAROTRON_ACCESS_FILE`, because signing in is not being enrolled), and
|
|
395
395
|
`PORTAL_LOCAL_CREDENTIAL` optionally relocates the credential file, which otherwise lives at
|
|
396
396
|
`~/.cordillera/portal-local-credential.json` (mode 0600, never in the repository and never inside the
|
|
397
397
|
pool or the archive). `PORTAL_SECRET` is required in BOTH modes and signs both token families — the
|
|
@@ -283,7 +283,7 @@ run dir and the ledgers. The auth model (four principal kinds; run-bound client
|
|
|
283
283
|
exactly `brief`, `read_artifact` gated to the report, and `list_findings` gated to the curated card
|
|
284
284
|
groups) is documented in [09 — Security and data](09-security-and-data.md).
|
|
285
285
|
|
|
286
|
-
**A signed-in client account reads the audit chain** (
|
|
286
|
+
**A signed-in client account reads the audit chain** (ruling 2026-08-27). The audit trail is
|
|
287
287
|
what makes a clearance defensible, and the person who has to defend the filing is the client's
|
|
288
288
|
lawyer — so `audit`, `narrative`, the record artifacts and a register axis are readable through
|
|
289
289
|
`read_artifact`, the raw `list_findings` path returns the AT#/F#/NR# records, and `get_run`, `trace`
|
|
@@ -255,6 +255,11 @@ wrong one exits `MODULE_NOT_FOUND` whose last line is the node version: piped th
|
|
|
255
255
|
almost exactly like a quiet success, and the ritual's own success test — *"and then `git diff` is
|
|
256
256
|
empty"* — is satisfied **precisely when nothing ran**.
|
|
257
257
|
|
|
258
|
+
**Three of these regen commands live in the test tree, which the published package excludes**, so they
|
|
259
|
+
are runnable from a checkout and not from an install. That is the right boundary — regenerating a
|
|
260
|
+
baseline is a contributor's act — but it is worth saying here rather than leaving a reader to find an
|
|
261
|
+
absent file and diagnose their configuration.
|
|
262
|
+
|
|
258
263
|
| File | Regen | What it records |
|
|
259
264
|
|---|---|---|
|
|
260
265
|
| `driver/contract-e3-baseline.json` | `node driver/test/contract-e3-baseline.mjs --write` | structure-as-text per stage dispatch and per skill file |
|
package/docs/configuration.md
CHANGED
|
@@ -111,6 +111,124 @@ wanted. A framework and its manifest are checked as a pair.
|
|
|
111
111
|
Alongside it, `workedExamplesPath` sets the analysis depth target — worked clearances calibrated
|
|
112
112
|
under that framework. Absent, the generic set applies.
|
|
113
113
|
|
|
114
|
+
### Writing your own, step by step
|
|
115
|
+
|
|
116
|
+
**Nothing here is a code change.** Two files go into your own store; a profile points at one of them.
|
|
117
|
+
|
|
118
|
+
**1. Write the deck.** A markdown document. It is reasoned *with*, so write it the way you would brief a
|
|
119
|
+
colleague: what each band means, what it turns on, what to do about it. Give **every band a heading of
|
|
120
|
+
its own**, spelled exactly as you will spell it in the manifest, and under each heading write the rungs
|
|
121
|
+
as bold-led bullets:
|
|
122
|
+
|
|
123
|
+
```markdown
|
|
124
|
+
## High
|
|
125
|
+
|
|
126
|
+
- **What it is.** A live registration for a similar mark in a class the client will trade in.
|
|
127
|
+
- **What it turns on.** Whether the goods actually overlap, and whether the owner is using the mark.
|
|
128
|
+
- **What to do.** Advise against adoption unless the owner's non-use can be established.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
That shape is not decoration. The profile screen extracts what the bands mean from these headings and
|
|
132
|
+
bullets, and **it is all or nothing**: one band without a heading, or one heading with no bold-led
|
|
133
|
+
bullet, and the box explaining your bands silently does not render at all — while the title and the
|
|
134
|
+
coloured pills still do, so the page looks finished. Frameworks in this repository have shipped in
|
|
135
|
+
exactly that state, which is why there is now a command that tells you before a client sees it.
|
|
136
|
+
|
|
137
|
+
**2. Write the manifest**, beside the deck and named after it: `your-framework.md` needs
|
|
138
|
+
`your-framework.manifest.json`. The path is derived, never configured, so the two cannot drift apart.
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"schema_version": 1,
|
|
143
|
+
"framework_key": "your-firm-2026",
|
|
144
|
+
"title": "Your firm's clearance risk framework",
|
|
145
|
+
"source_deck": "Where this came from, and when it was last reviewed",
|
|
146
|
+
"entity_label": "the company",
|
|
147
|
+
"bands": [
|
|
148
|
+
{ "label": "Very High", "tone": "severe" },
|
|
149
|
+
{ "label": "High", "tone": "high" },
|
|
150
|
+
{ "label": "Moderate", "tone": "medium" },
|
|
151
|
+
{ "label": "Manageable","tone": "low" }
|
|
152
|
+
],
|
|
153
|
+
"structure": { "kind": "bands" }
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Every key above is required and **no other key is allowed** — an unknown one is refused by name rather
|
|
158
|
+
than ignored. `schema_version` is `1`. `framework_key` is lowercase letters, digits and hyphens.
|
|
159
|
+
`bands` runs **most severe first**; that order is the framework's severity order everywhere the engine
|
|
160
|
+
names a risk. A band label may contain letters, spaces, slashes and hyphens, and **no digits** — a band
|
|
161
|
+
called "Level 3" invites arithmetic where judgement is wanted. `tone` is one of `severe`, `high`,
|
|
162
|
+
`medium`, `low`, `minimal`, and it chooses a colour, nothing else. `entity_label` is how your deck names
|
|
163
|
+
the client side in prose. If your deck is a matrix rather than a ladder, say
|
|
164
|
+
`"structure": { "kind": "matrix" }` — the matrix itself lives in the deck prose, never here.
|
|
165
|
+
|
|
166
|
+
**The manifest carries vocabulary and order only.** No threshold, no mapping table, no decision rule.
|
|
167
|
+
Those belong in the deck, where they are read as reasoning rather than applied as arithmetic.
|
|
168
|
+
|
|
169
|
+
**3. Put both files in your own store** and point a profile at the deck with `frameworkPath`. Client
|
|
170
|
+
rubrics deliberately do not live inside a checkout of this product.
|
|
171
|
+
|
|
172
|
+
**4. Check it before it is in force**, with the pre-flight. It opens both files exactly as a run would,
|
|
173
|
+
prints what they declare, and where the deck and the manifest disagree it names the band and says what
|
|
174
|
+
the deck did not do. It creates nothing, rates nothing and contacts nobody.
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
clearotron framework skills/prelim-search/your-framework.md
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
Framework: skills/prelim-search/your-framework.md
|
|
182
|
+
deck /srv/clearotron-config/skills/prelim-search/your-framework.md
|
|
183
|
+
read from the configured store
|
|
184
|
+
manifest /srv/clearotron-config/skills/prelim-search/your-framework.manifest.json
|
|
185
|
+
read from the configured store
|
|
186
|
+
|
|
187
|
+
It declares itself "Your firm's clearance risk framework" (your-firm-2026), a bands-shaped
|
|
188
|
+
framework rating the company.
|
|
189
|
+
|
|
190
|
+
The ladder, highest risk first:
|
|
191
|
+
1. Very High severe
|
|
192
|
+
2. High high
|
|
193
|
+
3. Moderate medium
|
|
194
|
+
4. Manageable low
|
|
195
|
+
|
|
196
|
+
What the deck defines:
|
|
197
|
+
✓ Very High Advise against adoption; the owner is likely to enforce.
|
|
198
|
+
✗ High the section under this band's heading states no rungs — a bands-shaped
|
|
199
|
+
deck writes each rung as a top-level `- **Label.** text` bullet
|
|
200
|
+
|
|
201
|
+
Not ready:
|
|
202
|
+
1 of 4 bands are named in the manifest and not defined in the deck. The profile screen shows
|
|
203
|
+
what the bands mean only when EVERY band is defined, so one miss empties the whole box.
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
It exits 0 when the two agree and 1 when they do not, so it can gate a deployment. `clearotron
|
|
207
|
+
brandowner add --dry-run` prints the same report for the framework it would set.
|
|
208
|
+
|
|
209
|
+
**It also tells you which file answered.** Resolution looks in your store first and falls back to this
|
|
210
|
+
repository, and the repository ships decks under names a customer may well have chosen too. A deck that
|
|
211
|
+
went missing from your store is therefore replaced by ours rather than reported absent — same band
|
|
212
|
+
words, different rubric, nothing raised anywhere. When that happens the report says so, above the
|
|
213
|
+
verdict, and the profile screen writes a line to the log.
|
|
214
|
+
|
|
215
|
+
**Then open the profile screen** for a company using it, and confirm you see the framework's title, your
|
|
216
|
+
band names in your order, and the box explaining what each band means.
|
|
217
|
+
|
|
218
|
+
### What is checked, and what is not
|
|
219
|
+
|
|
220
|
+
| | |
|
|
221
|
+
|---|---|
|
|
222
|
+
| The deck file exists | checked, and a run refuses without it |
|
|
223
|
+
| The manifest parses, and its keys and band labels are legal | checked, by name |
|
|
224
|
+
| The deck's headings and bullets match the manifest's bands | checked, by `clearotron framework` — and by the test suite, for every framework this installation can reach |
|
|
225
|
+
| Which file answered when your store and this repository both have one | reported by `clearotron framework`, and written to the log at view time |
|
|
226
|
+
| Whether the rubric is any good | **not checked, and cannot be** |
|
|
227
|
+
|
|
228
|
+
That last row is the one to hold on to. A framework is reasoned with on every search the company ever
|
|
229
|
+
runs, and nothing reads it for sense. **A framework that is subtly wrong produces confident ratings that
|
|
230
|
+
look exactly like right ones.** Have it read by whoever would sign the advice, before it is pointed at.
|
|
231
|
+
|
|
114
232
|
---
|
|
115
233
|
|
|
116
234
|
## 3. Client profiles
|
|
@@ -26,8 +26,8 @@ at runtime.
|
|
|
26
26
|
This bounds the set at about 35 front doors rather than 100 stubs, and a stub is what rots.
|
|
27
27
|
- **Decisions go in `docs/decisions/`.** A ruling gets a numbered ADR so the next reader finds it settled.
|
|
28
28
|
- **No hand-kept changelogs, at any level.** Git holds the history, and a hand-kept log rots. A
|
|
29
|
-
per-directory one was never written and stays banned. **Amended 2026-08-31 (
|
|
30
|
-
|
|
29
|
+
per-directory one was never written and stays banned. **Amended 2026-08-31 (ruling:
|
|
30
|
+
the landed release-pipeline decision is master, and the old doc is cleaned up to match
|
|
31
31
|
it, never the machinery bent to the doc):** the root `CHANGELOG.md` is the release pipeline's
|
|
32
32
|
MACHINE-COMPILED output — assembled by `scripts/release-version.mjs` from the pending release notes
|
|
33
33
|
when a release is cut, plain-language-gated, and public-facing (the website receives it). It is not
|
|
@@ -19,7 +19,7 @@ clearance, and change the code. Nothing else.** In particular it carries no prod
|
|
|
19
19
|
material, no worked internal mechanics for a lane, and no historical narrative.
|
|
20
20
|
|
|
21
21
|
Four of the five documents above are withheld. They stay in this repository, which is the archive; they do
|
|
22
|
-
not cross at the cut. **`CHANGELOG.md` left this list on 2026-08-31 (
|
|
22
|
+
not cross at the cut. **`CHANGELOG.md` left this list on 2026-08-31 (ruling:
|
|
23
23
|
the landed release-pipeline decision is master).** The 803-word hand-written file this record reviewed was
|
|
24
24
|
already absent; what carries the name now is the release pipeline's machine-compiled, plain-language,
|
|
25
25
|
public-facing changelog — release history a reader of the public repository is meant to have, which the
|
|
@@ -38,7 +38,7 @@ neither is on disk, and neither was ever added or deleted in either repository's
|
|
|
38
38
|
record that names enforcement which was never written is worse than one that names none, because a
|
|
39
39
|
reader stops looking. What follows is what actually holds the line.
|
|
40
40
|
|
|
41
|
-
**The drop list itself does not ship.** It stays with the archive by
|
|
41
|
+
**The drop list itself does not ship.** It stays with the archive by ruling, 2026-08-31, so the
|
|
42
42
|
published tree carries no copy — and neither, today, does any other tree. `CUT_RECORD_PRESENT` is false
|
|
43
43
|
everywhere the code runs.
|
|
44
44
|
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.0-beta.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f75266d: New: People, in the sidebar for anyone with Manage, lists who can use the installation, and adds a person.
|
|
8
|
+
|
|
9
|
+
Enter their email address, choose what they may do and what they can see, and save. A sentence under the form says what they will and will not see, before you save. They sign in the same way you do; Clearotron issues no passwords.
|
|
10
|
+
|
|
11
|
+
An installation that signs one person in on its own machine cannot hold a second. People says so, and links to how to put a login system in front of it.
|
|
12
|
+
- f75266d: New: Each person now has two permissions, Run clearances and Manage, instead of a staff or client role.
|
|
13
|
+
|
|
14
|
+
A person sees everything below the points they were given: the whole installation, an organisation, or a single company. Someone without Run has no New clearance. Someone without Manage has no People page and cannot add companies.
|
|
15
|
+
|
|
16
|
+
The company menu groups companies by organisation when you can see more than one. Each organisation has its own Generic, listed first and marked Default. The top bar names your organisation when you can see exactly one.
|
|
17
|
+
- f75266d: New: Clearances with no company set up now have a daily allowance per organisation, 20 unless the Generic profile sets another.
|
|
18
|
+
|
|
19
|
+
Anyone with Run clearances on a whole organisation can start them. One organisation's clearances never use up another's, and a person with access to the whole installation is not limited.
|
|
20
|
+
- c4d2768: New: You can now choose which company a clearance is for on the page itself, and set up new companies in the browser.
|
|
21
|
+
|
|
22
|
+
Those screens used to refuse to render until a company was chosen. They told you to pick one at the top left. That is not where the control is when the sidebar is collapsed. They now show you the companies instead. Each one says what it sells, how many marketplaces it covers, and which territories it defaults to.
|
|
23
|
+
|
|
24
|
+
Setting up a company is a page, not a document. It needs a name. Everything else has a default, and the screen says what that default is. It refuses before writing anything, and says why. A name it cannot make a key from, a key already in use, or an email address another company claims. A company you create can run its first search straight away, with no restart, and stop or cancel that search like any other.
|
|
25
|
+
|
|
26
|
+
The product now says company throughout. It used to say brand owner, account, client and customer for the same thing. The firm running the installation is named separately, in the top bar.
|
|
27
|
+
|
|
28
|
+
Companies created through the settings page were saved without a risk framework. Their matters were then rated under the house default, with nothing on screen saying so. Every company created now carries one, and says which.
|
|
29
|
+
- f75266d: New: The setup wizard now asks for the organisation's name after the sign-in address, and the person who installs starts with access to everything.
|
|
30
|
+
|
|
31
|
+
`clearotron grant add` sets a person's two permissions with `--run` and `--manage`; with neither, the person can look and start nothing.
|
|
32
|
+
- f75266d: For operators: Who may sign in is now decided by each person's own entry in the guest list, and nothing else. On a local installation, the next `clearotron start` gives whoever signs in access to everything if the guest list has no `people` section.
|
|
33
|
+
|
|
34
|
+
- For operators: Any other installation with people on it needs one edit to its guest list (`CLEAROTRON_ACCESS_FILE`) before upgrading.
|
|
35
|
+
- For operators: Anyone admitted because of their email domain needs an entry under a new `people` section: `"everything": true`, `"run": true`, `"manage": true`.
|
|
36
|
+
- For operators: Each person who starts clearances needs `"run": true`, and each person who adds people or companies needs `"manage": true`. A person with no entry can see what their access covers, and start nothing.
|
|
37
|
+
- For operators: An organisation whose `accounts` is `"*"` needs the list of companies it holds instead. Until then the portal refuses to start, and names the entry. A company may be listed under one organisation only.
|
|
38
|
+
- For operators: `PORTAL_STAFF_DOMAINS` is ignored from this version on, and the portal says so at startup.
|
|
39
|
+
- For operators: After upgrading, sign in to check, and run `clearotron connect` again for each person whose assistant uses a key.
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- 8ff52d4: Fixed: Installed under a folder named with `#` or `%`, setup's register check now reads the provider's cost instead of calling it unknown. It built the address of the provider's own declaration by hand, and those characters broke it. It now uses the address Node builds, which is also what Windows needs.
|
|
44
|
+
- 350e0ed: New: `clearotron framework <your-framework.md>` reads a risk framework and its manifest, and reports what they declare.
|
|
45
|
+
|
|
46
|
+
Run it before either rates a matter. It prints the ladder in the framework's own order, the company the deck names, and the shape it is. Where the deck and the manifest disagree, it names the band and says what the deck did not do. It creates nothing, rates nothing and contacts nobody, and it exits non-zero when the pair is not ready. `clearotron brandowner add --dry-run` prints the same report.
|
|
47
|
+
|
|
48
|
+
Getting the deck's shape wrong used to fail quietly. The profile screen showed the framework's title and your band colours, and silently omitted the box saying what the bands mean. The new command answers that question directly, using the screen's own read of the deck.
|
|
49
|
+
|
|
50
|
+
Fixed: a framework served from the product's own files, while you have a configuration store set, now says so.
|
|
51
|
+
|
|
52
|
+
Your store is looked in first, and the product's files answer when it is silent. The product ships decks under names you may also have chosen. So a deck that went missing from your store was replaced by ours rather than reported absent. Same band words, different rubric, nothing raised anywhere. The profile screen now writes one line naming what happened, and the new command reports it.
|
|
53
|
+
|
|
54
|
+
Fixed: the house triage ladder's profile page explains its bands again.
|
|
55
|
+
|
|
56
|
+
Its band sections stated their meanings as plain paragraphs, which the screen does not read. Every company without a framework of its own saw band colours and no explanation. The wording is unchanged.
|
|
57
|
+
- cfb9a9f: Fixed: `clearotron doctor` now says which companies your portal's trigger key can start.
|
|
58
|
+
|
|
59
|
+
The key carries a list of the companies it may start runs for. A company added after the key was minted is outside it. Doctor reported the key's expiry and never its coverage. So the one command whose job is to tell you what a machine is configured for said nothing about it.
|
|
60
|
+
|
|
61
|
+
It reads the roster the services read, not the one a command-line process resolves, and it says which. Those two can disagree, and when they do the difference is the whole answer.
|
|
62
|
+
|
|
63
|
+
The line is a note, not a failure. Your portal takes a fresh credential at the start of every call, so a company outside the key is not normally refused. It is refused when the portal cannot take a fresh one, and the line says so and gives you the command to widen the key.
|
|
64
|
+
- ba2899b: Fixed: On reports searched through Signa, each register finding now links to the office's own page for that record, where the office publishes one. Singapore publishes no such page, so its registrations are cited by number, and the report says why. A number that an office's page cannot take is cited the same way.
|
|
65
|
+
- c4d2768: New: The configuration guide now explains how to write your own risk framework, step by step.
|
|
66
|
+
|
|
67
|
+
It gives the manifest in full. It says which fields are required, and what each one may contain. It shows the shape a deck needs for the profile screen to explain your bands.
|
|
68
|
+
|
|
69
|
+
That shape was undocumented, and getting it wrong fails quietly. The screen still shows the framework's title and your band colours. The box saying what the bands mean does not appear at all.
|
|
70
|
+
|
|
71
|
+
The guide also says what is checked and what is not. Nothing reads your rubric for sense. A framework that is subtly wrong produces confident ratings that look exactly like right ones.
|
|
72
|
+
|
|
3
73
|
## 0.2.4
|
|
4
74
|
|
|
5
75
|
### Patch Changes
|