clearotron 0.4.0 → 0.4.1-beta.0
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/THIRD-PARTY-NOTICES.md +2 -2
- package/build-info.json +2 -2
- package/docs/SECURITY-OWASP.md +21 -13
- package/docs/SECURITY.md +20 -14
- package/docs/architecture/04-configuration-reference.md +3 -2
- package/docs/architecture/05-config-governance.md +1 -1
- package/docs/architecture/06-operations-runbook.md +1 -1
- package/docs/architecture/09-security-and-data.md +16 -11
- package/driver/CHANGELOG.md +18 -0
- package/driver/binding-layers.mjs +28 -6
- package/driver/citation-census.json +3 -3
- package/driver/common-law-coverage-status.mjs +52 -0
- package/driver/common-law-receipts.mjs +1 -1
- package/driver/connotation-search.mjs +5 -5
- package/driver/contract-dictation-registry.mjs +1 -1
- package/driver/contract-e3-backlog.mjs +1 -1
- package/driver/contract-vocabulary.mjs +18 -18
- package/driver/degraded-parts.mjs +10 -3
- package/driver/dispatch-record.mjs +19 -0
- package/driver/doubt-closure-tool.mjs +2 -2
- package/driver/driver.config.mjs +39 -6
- package/driver/engagement-receipt.mjs +1 -1
- package/driver/engine/CONTRACT.md +41 -1
- package/driver/engine/anthropic-agent.mjs +56 -4
- package/driver/engine/mcp/codex-config.mjs +1 -1
- package/driver/engine/mcp/coverage-server.mjs +1 -1
- package/driver/engine/mcp/gather-config.mjs +1 -1
- package/driver/engine/mcp/recording-server.mjs +31 -4
- package/driver/engine/mcp/stdio-server.mjs +45 -7
- package/driver/engine/openai-agent.mjs +100 -17
- package/driver/engine/session-record.mjs +223 -0
- package/driver/enqueue-schema.mjs +2 -2
- package/driver/findings-model.mjs +3 -3
- package/driver/form-neighbourhood.mjs +7 -7
- package/driver/frame-diff-model.mjs +3 -3
- package/driver/framework.mjs +22 -0
- package/driver/gateway.mjs +94 -12
- package/driver/named-band.mjs +2 -0
- package/driver/package.json +1 -1
- package/driver/phase0.mjs +13 -2
- package/driver/pipeline-knockout.mjs +1 -1
- package/driver/pipeline.mjs +46 -33
- package/driver/portal-families.mjs +3 -3
- package/driver/portal-service.mjs +1 -1
- package/driver/predelivery-lint.mjs +19 -19
- package/driver/publish/index.mjs +1 -1
- package/driver/publish/knockout.mjs +5 -4
- package/driver/publish/office-record-links.mjs +17 -2
- package/driver/publish/publish-inputs.mjs +1 -1
- package/driver/publish/render-knockout.mjs +17 -5
- package/driver/publish/render.mjs +4 -2
- package/driver/record-fold.mjs +177 -0
- package/driver/register-count.mjs +18 -1
- package/driver/register-plan.mjs +71 -17
- package/driver/register-records.mjs +7 -0
- package/driver/registry-fidelity.mjs +2 -2
- package/driver/repairs.mjs +1 -1
- package/driver/report-card-record.mjs +4 -4
- package/driver/scope-facts.mjs +11 -3
- package/driver/search-policy.mjs +20 -1
- package/driver/skills/clearance-common-law/SKILL.md +22 -22
- package/driver/skills/clearance-register/digest.md +3 -3
- package/driver/skills/clearance-register/register-recipes.md +11 -11
- package/driver/skills/clearance-register/status-rules.md +1 -1
- package/driver/skills/clearance-register/unit.md +1 -1
- package/driver/skills/clearance-search/firm-wide-reasoning.md +3 -2
- package/driver/skills/clearance-search/phase2-execution.md +6 -5
- package/driver/skills/clearance-search/synthesis-rules.md +4 -4
- package/driver/skills/clearance-search/worked-examples.md +39 -39
- package/driver/skills/clearance-variants/SKILL.md +16 -16
- package/driver/skills/frame-diff/SKILL.md +2 -2
- package/driver/stage-context.mjs +27 -1
- package/driver/stages.mjs +12 -12
- package/driver/suite-census.json +175 -31
- package/driver/synthesis-record.mjs +5 -0
- package/driver/unit-inventory.mjs +17 -28
- package/driver/verify.mjs +6 -6
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/lib/evidence.mjs +8 -8
- package/mcp-server/lib/knockout.mjs +1 -1
- package/mcp-server/package.json +2 -2
- package/package.json +2 -2
- package/portal-ui/package.json +1 -1
- package/providers/_shared/count.mjs +4 -1
- package/providers/_shared/enumerate.mjs +61 -22
- package/providers/_shared/execute-plan.mjs +2 -0
- package/providers/corsearch/src/capabilities.js +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +2 -2
- package/providers/signa/src/capabilities.js +17 -2
- package/providers/signa/src/core.js +234 -37
- package/scripts/band-shape-probe.mjs +2 -2
- package/scripts/deprecate-below.mjs +28 -4
- package/scripts/e2e-first-time.mjs +16 -1
- package/scripts/freeze-example-run.mjs +2 -2
- package/shared/scope.mjs +18 -2
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -110,7 +110,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
110
110
|
SOFTWARE.
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
## @modelcontextprotocol/sdk@1.
|
|
113
|
+
## @modelcontextprotocol/sdk@1.31.0
|
|
114
114
|
|
|
115
115
|
- **Licence declared:** `MIT`
|
|
116
116
|
- **Repository:** https://github.com/modelcontextprotocol/typescript-sdk
|
|
@@ -4896,7 +4896,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
4896
4896
|
SOFTWARE.**
|
|
4897
4897
|
```
|
|
4898
4898
|
|
|
4899
|
-
## proxy-addr@2.0.
|
|
4899
|
+
## proxy-addr@2.0.8
|
|
4900
4900
|
|
|
4901
4901
|
- **Licence declared:** `MIT`
|
|
4902
4902
|
- **Repository:** jshttp/proxy-addr
|
package/build-info.json
CHANGED
package/docs/SECURITY-OWASP.md
CHANGED
|
@@ -45,10 +45,11 @@ shows the engine's internals to a company's people.
|
|
|
45
45
|
**What Clearotron does.** Every read passes one authorization check, and a person sees only what their
|
|
46
46
|
grant names. A key bound to one run reads that run and writes nothing. The copy of a report a company
|
|
47
47
|
receives drops the staff-only notes. No real company's data is in this repository; run data lives
|
|
48
|
-
in folders the operator owns. CI scans the tree and the built bundle for secrets.
|
|
48
|
+
in folders the operator owns. CI scans the tree and the built bundle for secrets. A report published
|
|
49
|
+
from 0.4.0 on carries its fonts inside itself, and opening it contacts no other server.
|
|
49
50
|
|
|
50
51
|
**Where.** [SECURITY.md](SECURITY.md) (the access model), `shared/scope.mjs`, `driver/portal-report.mjs`,
|
|
51
|
-
`.gitleaks.toml`, `.github/workflows/ci.yml`.
|
|
52
|
+
`.gitleaks.toml`, `.github/workflows/ci.yml`, `scripts/report-offline-render-check.mjs`.
|
|
52
53
|
|
|
53
54
|
**Not covered.** A company's clearances are kept until the operator deletes them: Clearotron sets no
|
|
54
55
|
retention period, because how long they are held is the operator's policy.
|
|
@@ -57,14 +58,15 @@ retention period, because how long they are held is the operator's policy.
|
|
|
57
58
|
|
|
58
59
|
**Here.** A compromised npm package, GitHub Action or engine program.
|
|
59
60
|
|
|
60
|
-
**What Clearotron does.** Dependabot updates the npm packages and the GitHub Actions.
|
|
61
|
-
published to npm through trusted publishing, with provenance that ties the package
|
|
62
|
-
build that produced it.
|
|
61
|
+
**What Clearotron does.** Dependabot updates the npm packages and the GitHub Actions. Every GitHub Action is pinned to a commit
|
|
62
|
+
hash. Each release is published to npm through trusted publishing, with provenance that ties the package
|
|
63
|
+
to the commit and the build that produced it. Each release carries a software bill of materials. On an install from a git
|
|
64
|
+
checkout, `clearotron update` installs with install scripts off. A stable release is not published while a code-scanning alert is open. Setup
|
|
63
65
|
installs, and `clearotron doctor` accepts, only an engine program at or above the version this release
|
|
64
66
|
needs.
|
|
65
67
|
|
|
66
|
-
**Where.** `.github/dependabot.yml`, `.github/workflows/release.yml`,
|
|
67
|
-
`scripts/release-code-scanning-check.mjs`, `driver/driver.config.mjs` (`ENGINE_BINARIES`, each program's
|
|
68
|
+
**Where.** `.github/dependabot.yml`, `.github/workflows/`, `.github/workflows/release.yml`,
|
|
69
|
+
`scripts/release-sbom.mjs`, `bin/update.mjs`, `scripts/release-code-scanning-check.mjs`, `driver/driver.config.mjs` (`ENGINE_BINARIES`, each program's
|
|
68
70
|
`floor`).
|
|
69
71
|
|
|
70
72
|
### LLM04:2025 Data and Model Poisoning
|
|
@@ -99,12 +101,14 @@ The common-law search asks the research service to run a search program, on that
|
|
|
99
101
|
|
|
100
102
|
**What Clearotron does.** Tools are granted per stage, by name. The case-law stage is the exception: it is granted every tool its two case-law services offer. A Claude
|
|
101
103
|
stage is offered no command tool.
|
|
102
|
-
Register searches beyond the fixed plan go through a proposal that code checks and runs. A key issued for
|
|
103
|
-
automation
|
|
104
|
+
Register searches beyond the fixed plan go through a proposal that code checks and runs. A new key issued for
|
|
105
|
+
automation must name the actions it may take; keys issued before that rule keep working, and each use of
|
|
106
|
+
one is recorded. No key for automation reaches a what-if over the network. A what-if requested by a company's people is queued for a
|
|
104
107
|
separate process rather than run by the door that received it.
|
|
105
108
|
|
|
106
109
|
**Where.** `driver/engine/mcp/gather-config.mjs`, `driver/engine/anthropic-agent.mjs`,
|
|
107
|
-
`driver/engine/mcp/supplemental.mjs`, `shared/scope.mjs`, `driver/whatif-worker.mjs
|
|
110
|
+
`driver/engine/mcp/supplemental.mjs`, `shared/scope.mjs`, `driver/whatif-worker.mjs`,
|
|
111
|
+
`mcp-server/mint-token.mjs`, `mcp-server/server.mjs`.
|
|
108
112
|
|
|
109
113
|
**Not covered.** As ASI02: the Claude program also offers a stage built-in tools that act without asking.
|
|
110
114
|
|
|
@@ -189,7 +193,7 @@ remove them.
|
|
|
189
193
|
**What Clearotron does.** The AI program starts with a named list of settings, and the key that signs
|
|
190
194
|
access keys is not on it. The worker that starts the AI program does not hold that key either, whether
|
|
191
195
|
the install runs in a terminal or under systemd. Access keys are scoped to a run, a company or named
|
|
192
|
-
actions, and one check enforces the scope. On Claude, a stage's file tools can read nothing outside its run folder, its
|
|
196
|
+
actions, and one check enforces the scope. A new key for automation must name its actions. On Claude, a stage's file tools can read nothing outside its run folder, its
|
|
193
197
|
instruction folders and any folder the machine's own Claude settings add. On Codex with its sandbox on, a stage's commands can read nothing outside its run folder, its instruction folders, the temporary
|
|
194
198
|
folders, and the system and program files a command needs to run.
|
|
195
199
|
|
|
@@ -276,10 +280,12 @@ could not run is listed in the report.
|
|
|
276
280
|
|
|
277
281
|
**What Clearotron does.** No agent outlives its stage. Each stage is one process under a watchdog and a
|
|
278
282
|
hard time limit, and the driver stops its whole process group. The driver records every program it
|
|
279
|
-
starts. On the Claude engine a stage cannot write into the instructions it runs from.
|
|
283
|
+
starts. On the Claude engine a stage cannot write into the instructions it runs from. The systemd units
|
|
284
|
+
an install can run in the background start with `NoNewPrivileges`, so nothing a stage starts there can
|
|
285
|
+
gain more than the install's account holds.
|
|
280
286
|
|
|
281
287
|
**Where.** `driver/engine/anthropic-agent.mjs`, `driver/engine/common.mjs`,
|
|
282
|
-
`driver/engine/child-record.mjs`, `driver/engine/deny-authority-write.mjs
|
|
288
|
+
`driver/engine/child-record.mjs`, `driver/engine/deny-authority-write.mjs`, `driver/systemd/`.
|
|
283
289
|
|
|
284
290
|
**Not covered.** As LLM04: on Codex, no check refuses a stage's write inside its run folder.
|
|
285
291
|
|
|
@@ -296,3 +302,5 @@ Measured on 2026-09-23 with the GitHub API.
|
|
|
296
302
|
| Private vulnerability reporting | On |
|
|
297
303
|
| npm provenance | Every release, through trusted publishing |
|
|
298
304
|
| Required checks on `main` | Six; force pushes and branch deletion refused |
|
|
305
|
+
| Actions pinned to a commit hash | All 42, read from the workflow files on 2026-09-24 |
|
|
306
|
+
| Software bill of materials | CycloneDX, attached to every release, read from the release workflow on 2026-09-24 |
|
package/docs/SECURITY.md
CHANGED
|
@@ -12,7 +12,7 @@ Each risk on OWASP's two lists for AI systems, and what Clearotron does about it
|
|
|
12
12
|
|
|
13
13
|
| Surface | Trust | Guard |
|
|
14
14
|
|---|---|---|
|
|
15
|
-
| 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,
|
|
15
|
+
| 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, and the CallTool chokepoint refuses any tool the listing hides, so no ops token reaches it over HTTP) |
|
|
16
16
|
| Client MCP (`mcp-server/http-server-client.mjs`) | a company's signed-in person / their access key | `what_if_run` from an `account` principal 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 grant check keys on it, and `whatIfEnqueue` refuses a token naming a different run. The `model` argument is refused on this face. |
|
|
17
17
|
| HTTP MCP (`mcp-server/http-server.mjs`) | authenticated remote | auth-BEFORE-data; fail-closed construction; inner scoped tokens |
|
|
18
18
|
| Run-bound keys | a report recipient an operator issues one to | a `user` key from `mint-token.mjs`, read-only and bound to one run; the plain-language report tools (`clientSafe`) only |
|
|
@@ -98,9 +98,9 @@ with access to everything.
|
|
|
98
98
|
on (`get_coverage`, `search`, `diff_artifact`, `run_changes`, what-if), which are denied by the
|
|
99
99
|
default that an undecided tool is denied.
|
|
100
100
|
- HMAC-signed tokens (`v1.<payload>.<sig>`, node:crypto only). `authorize()` is the single
|
|
101
|
-
enforcement point for every tool call; `visibleTools()`
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
enforcement point for every tool call; `visibleTools()` decides what a session is offered, and a
|
|
102
|
+
tool it hides is refused by name at the call, after `authorize()`. `authorize()` never sees `local`,
|
|
103
|
+
and `visibleTools()` is the only per-call gate that uses it.
|
|
104
104
|
- **User tokens cannot**: enumerate runs, cross to another run (explicit mismatching runId refused;
|
|
105
105
|
omitted runId pinned), reach any write/spend tool, or read internal artifacts — `read_artifact`
|
|
106
106
|
is name-gated to the report alone (`USER_ARTIFACTS`), and `list_findings` to the curated
|
|
@@ -110,13 +110,13 @@ with access to everything.
|
|
|
110
110
|
rides inside a delivered PDF and can be forwarded to anyone, and the ruling was about people the
|
|
111
111
|
operator enrolled. Both sets are gated at the read_artifact tool AND at the Resources surface
|
|
112
112
|
(`resources/list` / `resources/read`), kind for kind — two doors to the same bytes, one rule.
|
|
113
|
-
- **Ops tokens are least-privilege**:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
- **Ops tokens are least-privilege**: a new ops token must name its `verbs[]`, the write tools it may
|
|
114
|
+
call (an intake connector physically cannot `stop_run`), and `mint-token` refuses one without
|
|
115
|
+
`--verbs`. Tokens issued before that rule keep working, and the audit line marks every call they
|
|
116
|
+
make. `what_if_plan` and `what_if_run` are left out of the HTTP tool listing for every ops scope
|
|
117
|
+
(`visibleTools`, keyed on `local`), and a tool the listing hides is refused at the call, so no ops
|
|
118
|
+
token plans or runs a what-if over HTTP. An `account` principal returns above that branch: its
|
|
119
|
+
what-if is queued for a separate process, never run by the door.
|
|
120
120
|
|
|
121
121
|
## Token lifecycle
|
|
122
122
|
|
|
@@ -127,15 +127,21 @@ what the mechanism guarantees.*
|
|
|
127
127
|
principal in every audit line; the `jti` printed at mint time is the revocation handle). Three other callers share the same `mintToken`: `clearotron start` mints the portal's verb-scoped,
|
|
128
128
|
company-capped ops token in memory at every start, and neither prints nor stores it; `clearotron connect`
|
|
129
129
|
and the portal's connect screen each mint a person's key for their own assistant.
|
|
130
|
-
- **Revocation**: denylist file checked on every verification
|
|
131
|
-
|
|
130
|
+
- **Revocation**: a denylist file is checked on every verification, always. With
|
|
131
|
+
`TRADEMARK_MCP_TOKEN_DENYLIST` unset it is `~/.config/clearotron/token-denylist`, the file every revoker
|
|
132
|
+
writes in that case, and that file being absent means nothing has been revoked. A list named in the
|
|
133
|
+
setting that is missing or unreadable, or a default list that exists but cannot be read, refuses every
|
|
134
|
+
key. **Rotation**: two-secret window, flag-day-free.
|
|
132
135
|
- **Rate limits**: per-identity bucket on every request plus a separate lower per-principal bucket
|
|
133
136
|
for ops sessions.
|
|
134
137
|
|
|
135
138
|
## Audit
|
|
136
139
|
|
|
137
140
|
Every HTTP tool call appends one line to an append-only JSONL (`TRADEMARK_MCP_AUDIT_LOG`) when the call
|
|
138
|
-
finishes: `{ts, email, sub, method, tool, runId, status, door}`, where `status` is the outcome
|
|
141
|
+
finishes: `{ts, email, sub, kind, method, tool, runId, status, door}`, where `status` is the outcome, plus `query` (its
|
|
142
|
+
first 120 characters) on a search, `uri` on a resource read and `transport` on the local route, and for an
|
|
143
|
+
ops token `namesVerbs`, which says whether the token names its tools (never which tools, and never the
|
|
144
|
+
token). It is
|
|
139
145
|
written after scope resolution, so it names the principal. A request refused before any tool runs is
|
|
140
146
|
written too, with the status `refused`. Writing it is best-effort and never blocks a request.
|
|
141
147
|
|
|
@@ -156,7 +156,8 @@ the attempt row records the id the program reports it served.
|
|
|
156
156
|
**The CLI must be new enough for the model.** Each release carries its own list of accepted models,
|
|
157
157
|
and one that predates a model refuses it outright while the tier alias goes on serving the previous
|
|
158
158
|
generation — a 400 at the first turn, or a run that quietly used an older model. Setup installs
|
|
159
|
-
`2.1.
|
|
159
|
+
`2.1.293` or newer for this reason, the first release whose `haiku` reaches Claude Haiku 5.5 (`opus` and
|
|
160
|
+
`sonnet` reached 5.5 from `2.1.284`); a copy already on the machine is used as it is, so `doctor`'s
|
|
160
161
|
version is the one to read before assuming which model a tier reaches.
|
|
161
162
|
|
|
162
163
|
**Anything else throws.** There is no regex fall-through to sonnet and no cross-provider
|
|
@@ -190,7 +191,7 @@ timeout shot, warm patch, backoff), the lane-wedge re-dispatch, and the rate-lim
|
|
|
190
191
|
| `CLEAROTRON_AI_BILLING` | `subscription` (default) \| `api-key` \| `cloud` | 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; **`cloud`** bills Claude per use through the reader's own cloud account, named by the Claude program's own switch — `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY` or `CLAUDE_CODE_USE_BEDROCK`, or `ANTHROPIC_BASE_URL` alone for a gateway (see the credentials table below) — and deletes `ANTHROPIC_API_KEY` as subscription does. **`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`; `cloud` is refused. **Fail-loud, never a guess, never the subscription in its place:** `api-key` with no `ANTHROPIC_API_KEY` / `CODEX_API_KEY` throws; on `anthropic-agent`, `cloud` with two switches on, or with none and no `ANTHROPIC_BASE_URL`, throws, and so does a switch left on under `subscription` or `api-key`, because the program bills that cloud whatever this says; a value that is none of the three throws on both engines. `clearotron start` and `clearotron doctor` report each refusal against the settings the services read, naming a value that is not a billing mode by its setting and never quoting it, and the runner refuses the search when it is ordered, before anything is spent. The resolved mode is stamped on every stage telemetry row (`engine`, `authMode`, `apiBilled`, and `cloud`, null when no cloud bills), and the attempt row also carries `providerReported`, the program's own word for who served the turn. |
|
|
191
192
|
| `CLEAROTRON_CLAUDE_PATH` · `CLEAROTRON_CODEX_PATH` | `claude` / `codex` on `PATH`, then the copy Clearotron installed | 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 machine that runs both sets both — they were one variable until it met a machine needing two different paths. A path set here must be **absolute**: stage subprocesses run with cwd set to the run directory, so a relative path does not resolve there, and `clearotron doctor` refuses one. **Unset**, or set to the bare word `claude` / `codex` (which means the same), the engine uses the program on `PATH` when the machine has one, and otherwise the copy Clearotron installed in `~/.local/share/clearotron/engines`: `clearotron install` offers to put the chosen engine's program there (`@anthropic-ai/claude-code` or `@openai/codex`, this platform's build only), and `clearotron update` refreshes it. Nothing is bundled into the package. A value naming anything else is used as given and never falls back. `clearotron doctor` says which copy it found, and its version when npm installed that copy. |
|
|
192
193
|
| `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; a decision of 2026-09-04 **reversed that exemption** and renamed the whole namespace, so it carries the house prefix like everything else. One spelling, no exceptions. |
|
|
193
|
-
| `CLEAROTRON_OPENAI_MODEL_JUDGMENT` / `CLEAROTRON_OPENAI_MODEL_SWEEP` / `CLEAROTRON_OPENAI_MODEL_CHEAP` | `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna
|
|
194
|
+
| `CLEAROTRON_OPENAI_MODEL_JUDGMENT` / `CLEAROTRON_OPENAI_MODEL_SWEEP` / `CLEAROTRON_OPENAI_MODEL_CHEAP` | `gpt-6.1-sol` / `gpt-6.1-sol` / `gpt-6-luna` (from 2026-10-09; before that `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`). Codex `0.159.0` or newer is required on a ChatGPT sign-in: older releases refuse `gpt-6.1-sol` with a 400. | 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. |
|
|
194
195
|
| ↳ 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. |
|
|
195
196
|
| `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. |
|
|
196
197
|
| `CLEAROTRON_CODEX_SANDBOX_BYPASS` | unset | `1` runs `codex exec` with its own sandbox bypassed, for hosts where that sandbox cannot run (setup and `doctor --probe-engine` report it). It removes a defence: with it set, a stage's commands run with every permission of the account Clearotron runs as, not only in the run folder. Codex has no guard against a stage writing into the run's `_driver/` folder, with the sandbox or without it. Set it because the host forces it, never for convenience. |
|
|
@@ -161,7 +161,7 @@ structural, or dev seam); [dev] = dev/test seam, never set in prod.
|
|
|
161
161
|
`CLEAROTRON_AI` (anthropic-agent | openai-agent; default anthropic-agent), `CLEAROTRON_AI_BILLING`
|
|
162
162
|
(subscription|api-key|cloud; cloud is Claude only), `CLEAROTRON_CODEX_PATH`,
|
|
163
163
|
`CLEAROTRON_OPENAI_AUTH_FILE`, `CLEAROTRON_OPENAI_MODEL_JUDGMENT` / `CLEAROTRON_OPENAI_MODEL_SWEEP` /
|
|
164
|
-
`CLEAROTRON_OPENAI_MODEL_CHEAP` (
|
|
164
|
+
`CLEAROTRON_OPENAI_MODEL_CHEAP` (gpt-6.1-sol / gpt-6.1-sol / gpt-6-luna),
|
|
165
165
|
`CLEAROTRON_CLAUDE_PATH` (claude on PATH, then the copy Clearotron installed),
|
|
166
166
|
`CLEAROTRON_SYNTHESIS_MODEL` (opus), `CLEAROTRON_KNOCKOUT_MODEL` (opus),
|
|
167
167
|
`CLEAROTRON_MAX_BUDGET_USD` (unset).
|
|
@@ -294,7 +294,7 @@ node mcp-server/mint-token.mjs --scope ops --sub <principal-name> \
|
|
|
294
294
|
|
|
295
295
|
- `--sub` names the principal, and it rides into every audit line. Two integrations holding ops
|
|
296
296
|
tokens are then distinguishable in the log; one shared token makes them permanently indistinguishable.
|
|
297
|
-
- `--verbs` is
|
|
297
|
+
- `--verbs` is required for an ops token: it lists the write tools the token may call. A connector minted without `stop_run`
|
|
298
298
|
cannot call it — the check sits at the one chokepoint every tool call passes, not in each tool.
|
|
299
299
|
- `--accounts` caps the token to a set of company keys, so a trial integration can start demo
|
|
300
300
|
searches and never a real company's.
|
|
@@ -105,9 +105,12 @@ are no root units; everything is `systemd --user`.
|
|
|
105
105
|
The API-key door is a fourth process (loopback :18812) with **no Access in front** — the trade is
|
|
106
106
|
explicit: a mandatory key replaces the browser sign-in, and the mode refuses to start if anything
|
|
107
107
|
that would weaken that (the dev auth-disable knob, a missing signing secret) is also set.
|
|
108
|
-
- The staff unit is systemd-hardened (`NoNewPrivileges`,
|
|
109
|
-
audit-log path writable, `PrivateTmp`)
|
|
110
|
-
|
|
108
|
+
- The remote deployment's staff unit (`mcp-server/remote/`) is systemd-hardened (`NoNewPrivileges`,
|
|
109
|
+
`ProtectSystem=strict` with only the audit-log path writable, `PrivateTmp`). The portal, worker and two
|
|
110
|
+
assistant-door units an install runs (`driver/systemd/`) set `NoNewPrivileges`, `RestrictSUIDSGID`,
|
|
111
|
+
`LockPersonality` and `UMask=0007`, and nothing that limits where they write: they can write wherever
|
|
112
|
+
the install's account can. Both HTTP faces enforce host allowlists (DNS-rebinding protection),
|
|
113
|
+
per-identity rate limits, body caps, and auth-before-body-read.
|
|
111
114
|
- **Outbound** traffic is enumerated in [What leaves the machine](#what-leaves-the-machine) below.
|
|
112
115
|
No outbound service is given filesystem access: register, research, and case-law calls carry query
|
|
113
116
|
arguments only, and the pool and run dirs are never exposed to them.
|
|
@@ -165,17 +168,17 @@ CLI's built-in `WebFetch` (`engine/mcp/gather-config.mjs`), or the driver's stan
|
|
|
165
168
|
moment of the call, so no list here can be complete, and a deployment that needs one must enforce it at
|
|
166
169
|
the network layer.
|
|
167
170
|
|
|
168
|
-
**A
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
**A report carries its own fonts.** A report published from 0.4.0 on holds Plus Jakarta Sans and Fira
|
|
172
|
+
Code inside itself (`shared/fonts/`), and opening it contacts no other server, from disk, from an email
|
|
173
|
+
or in the portal. CI records every request a rendered report makes and fails on one that leaves
|
|
174
|
+
(`scripts/report-offline-render-check.mjs`). A report published before 0.4.0 still links its two font
|
|
175
|
+
services when opened from disk. The request carries no matter data. Registry links in the report resolve only when a reader
|
|
173
176
|
clicks one.
|
|
174
177
|
|
|
175
178
|
**The demo contacts nothing.** `bin/example.mjs` re-publishes artifacts already on disk: no `fetch` in its
|
|
176
179
|
module graph, no MCP server, no engine binary. Its two subprocesses are a `git rev-parse` build stamp
|
|
177
|
-
and the browser opener, and the portal binds `127.0.0.1`. The report it opens loads
|
|
178
|
-
|
|
180
|
+
and the browser opener, and the portal binds `127.0.0.1`. The report it opens loads nothing from
|
|
181
|
+
another server.
|
|
179
182
|
|
|
180
183
|
## Secrets
|
|
181
184
|
|
|
@@ -228,7 +231,9 @@ today — flag for the buyer's compliance review.
|
|
|
228
231
|
and the engine-local gather MCP servers add none: each is ~130 lines over a shared 120-line stdio
|
|
229
232
|
scaffolding (`driver/engine/mcp/stdio-server.mjs`) with no MCP SDK. The artifacts read layer does
|
|
230
233
|
use the SDK (`@modelcontextprotocol/sdk`, `jose`). Small surface by design.
|
|
231
|
-
-
|
|
234
|
+
- On an install from a git checkout, `clearotron update` installs with `npm ci --ignore-scripts`, so no
|
|
235
|
+
package's install script runs on the host. Development packages are kept there, because the portal is
|
|
236
|
+
rebuilt from them. If your deploy then rebuilds a native addon,
|
|
232
237
|
make that a named, reviewed step: it is the only place a lifecycle script should run on the host.
|
|
233
238
|
- Run a skill scanner over the skills tree at deploy time and abort on high/critical findings. Two
|
|
234
239
|
things to check on any new host: that the scanner is actually installed (a scanner that is absent
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.4.1-beta.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed: A connection library is updated to close a reported weakness in how sign-in details could be sent.
|
|
8
|
+
- Fixed: A clearance started from a connected assistant under the Generic default now appears on Clearances when you choose Generic. Before, it showed only under All companies.
|
|
9
|
+
- Fixed: A register count or record that could not be fetched no longer shows the program's error message to a client. This covers the knockout workbook's count notes, the knockout report and a clearance finding card. Each shows the line the report already uses for something left open.
|
|
10
|
+
- Fixed: A knockout's completion message and email now give its rating, or the highest rating when several names were screened. They no longer use the term 'worst band'.
|
|
11
|
+
- Fixed: A client whose mark is written in another script, such as Greek, Cyrillic or Chinese, now receives the link to the report. Before, the email arrived without one.
|
|
12
|
+
- Fixed: In rare cases, a report repaired just before delivery listed one conflict as two findings. Its last finding also lost its full write-up. The conflict now stays one finding, and every finding keeps its write-up.
|
|
13
|
+
- Fixed: Reports no longer say a search "did not finish and must be re-run" when a name's non-Latin form was in fact searched. They no longer make a conclusion depend on reviewing records the search had already read in full. Knockout reports now list every territory you ordered.
|
|
14
|
+
- Fixed: The audit workbook no longer warns that the depth rules were applied to nothing on a clearance whose narrative is written in sections. Those rules govern per-finding write-ups, and such a narrative has none.
|
|
15
|
+
- Fixed: An order for the European Union now also searches each member state's own trade mark register, at no extra search cost. A national registration — in France, for example — is now found by an EU search.
|
|
16
|
+
- Before you upgrade: if you installed Codex yourself, update it to 0.159.0 or newer first. Older copies cannot use GPT-6.1 Sol, and searches on the OpenAI engine would stop at their first step.
|
|
17
|
+
- New: Searches now use the latest models: Claude Haiku 5.5, Sonnet 5.5 and Opus 5.5, and OpenAI's GPT-6.1 Sol and GPT-6 Luna. Installing or updating Clearotron brings the Claude and Codex programs up to versions that can use them.
|
|
18
|
+
- Fixed: Two different names written in Greek, Cyrillic or Chinese characters, or differing only in an accented letter, can now be screened in one knockout. Before, they were refused as duplicates.
|
|
19
|
+
- Fixed: Findings rated medium risk or higher are now checked for the owner's actual use in the same way on every company's risk ladder. That same search looks for the owner's record of oppositions and disputes, so "Likely to enforce" carries a rating instead of "not checked".
|
|
20
|
+
|
|
3
21
|
## 0.4.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -100,19 +100,39 @@ export function bindingLayersFor(territory) {
|
|
|
100
100
|
layers.push({ layer: "national", office: BX_OFFICE,
|
|
101
101
|
why: `${t} has no separate national register — Benelux (BX) is the national register for it` });
|
|
102
102
|
} else if (t === WO_OFFICE) {
|
|
103
|
-
// The international register ORDERED AS a territory.
|
|
104
|
-
//
|
|
103
|
+
// The international register ORDERED AS a territory. There is no national layer to miss, because
|
|
104
|
+
// the Madrid register IS the order. Without this a matter listing
|
|
105
105
|
// "International" (territory-codes.mjs normalizes it to WO) discloses a missing national register
|
|
106
106
|
// that does not exist — a limitation on a register the plan searched, which is the kind a reader
|
|
107
107
|
// learns to skim.
|
|
108
108
|
layers.push({ layer: "international", office: WO_OFFICE, why: "the international register, ordered directly" });
|
|
109
109
|
return Object.freeze(layers.map(Object.freeze));
|
|
110
|
-
} else if (t === EU_OFFICE
|
|
111
|
-
// The EU
|
|
112
|
-
|
|
110
|
+
} else if (t === EU_OFFICE) {
|
|
111
|
+
// The EU-wide register named by its OFFICE code, which is how a plan that is already resolved
|
|
112
|
+
// carries it. It names that register, not the territory: resolving a France plan's ["FR", "EM",
|
|
113
|
+
// "WO"] again must not turn it into a search of every member state. An EU ORDER arrives as "EU"
|
|
114
|
+
// (normalizeTerritory folds "European Union", EUTM and EUIPO there) and takes the branch below.
|
|
115
|
+
layers.push({ layer: "regional", office: EU_OFFICE, why: "the EU-wide register, named by its office code" });
|
|
113
116
|
layers.push({ layer: "international", office: WO_OFFICE,
|
|
114
117
|
why: "international registrations designating the EU bind it without a separate EU filing" });
|
|
115
118
|
return Object.freeze(layers.map(Object.freeze));
|
|
119
|
+
} else if (t === "EU") {
|
|
120
|
+
// The EU ordered AS a territory: the EU-wide register, EVERY member state's national register, and
|
|
121
|
+
// international registrations designating the EU or a member. A national right blocks use in its own
|
|
122
|
+
// member state without appearing in the EU-wide register, so an EU order that searched only EUIPO and
|
|
123
|
+
// Madrid cleared none of the countries inside it — measured 2026-10-09 on both live providers: an
|
|
124
|
+
// EU-scoped search returns no member state's national filings, and listing the members in the same
|
|
125
|
+
// request reaches them. The owner's ruling of the same day: an EU order searches the national
|
|
126
|
+
// registers too, on every provider. Benelux is one national register for three members, listed once.
|
|
127
|
+
layers.push({ layer: "regional", office: EU_OFFICE, why: "the EU-wide register, ordered directly" });
|
|
128
|
+
for (const office of new Set(EU_MEMBERS.map((m) => (BENELUX_MEMBERS.includes(m) ? BX_OFFICE : m)))) {
|
|
129
|
+
layers.push({ layer: "national", office, why: office === BX_OFFICE
|
|
130
|
+
? "Benelux (BX) is the national register of three EU members"
|
|
131
|
+
: `a national right in ${office} blocks use there without appearing in the EU-wide register` });
|
|
132
|
+
}
|
|
133
|
+
layers.push({ layer: "international", office: WO_OFFICE,
|
|
134
|
+
why: "an international registration designating the EU or a member state binds it without a separate filing" });
|
|
135
|
+
return Object.freeze(layers.map(Object.freeze));
|
|
116
136
|
} else {
|
|
117
137
|
layers.push({ layer: "national", office: t, why: `the national register of ${t}` });
|
|
118
138
|
}
|
|
@@ -215,7 +235,9 @@ export function unsearchedLayerReason(report) {
|
|
|
215
235
|
regional: "EU-wide rights",
|
|
216
236
|
international: "international registrations designating it",
|
|
217
237
|
};
|
|
218
|
-
|
|
238
|
+
// One name per KIND of register: an EU order carries a national layer per member state, and a provider
|
|
239
|
+
// that reaches only some of them would otherwise list "the national register" once per member missed.
|
|
240
|
+
const missing = [...new Set(report.unsearched.map((u) => names[u.layer] ?? u.layer))];
|
|
219
241
|
const list = missing.length === 1 ? missing[0]
|
|
220
242
|
: `${missing.slice(0, -1).join(", ")} and ${missing[missing.length - 1]}`;
|
|
221
243
|
return `Not a complete clearance for ${report.territory}: ${list} ${missing.length === 1 ? "was" : "were"} `
|
|
@@ -111,3 +111,55 @@ export function recordCoverageStatus(spec, received, { now = () => new Date().to
|
|
|
111
111
|
if (refused.length) lines.push("Refused — send these again, corrected:", ...refused.map((x) => `- ${x}`));
|
|
112
112
|
return { ok: refused.length === 0, text: lines.join("\n") };
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
/** The status code records for a ledger row whose findings gave none of the three words. */
|
|
116
|
+
export const NOT_STATED = "not stated";
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* THE STATUSES A FINDINGS FILE STATES, RECORDED BY CODE (Krzys, 2026-10-01). A seat whose findings carry
|
|
120
|
+
* a coverage ledger but made no `record_coverage_status` call is accepted, not re-run: a re-run for this
|
|
121
|
+
* was a full stage re-run, and nothing is added to what the model reads. Each ledger row's status is read
|
|
122
|
+
* from the row as the findings wrote it — one of the three words, else "not stated" — and recorded in the
|
|
123
|
+
* same file the tool writes, marked `source: "findings"`. A record the seat made through the tool is never
|
|
124
|
+
* overwritten; a record code made earlier is replaced, so it follows the latest findings after a re-run.
|
|
125
|
+
* Returns the record written, or null when nothing was written. Never throws.
|
|
126
|
+
*/
|
|
127
|
+
export function recordStatusesFromFindings(findingsPath, { half = null, now = () => new Date().toISOString() } = {}) {
|
|
128
|
+
if (!findingsPath) return null;
|
|
129
|
+
const file = coverageStatusPath(findingsPath);
|
|
130
|
+
let existing = null;
|
|
131
|
+
try { existing = JSON.parse(readFileSync(file, "utf8")); } catch { existing = null; }
|
|
132
|
+
if (existing && existing.source !== "findings") return null; // the seat recorded its own statuses
|
|
133
|
+
let text;
|
|
134
|
+
try { text = readFileSync(String(findingsPath), "utf8"); } catch { return null; }
|
|
135
|
+
const record = { coverage_status: ledgerStatuses(text), half, source: "findings", recorded_at: now() };
|
|
136
|
+
try {
|
|
137
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
138
|
+
writeFileSync(file, JSON.stringify(record, null, 2) + "\n");
|
|
139
|
+
} catch { return null; }
|
|
140
|
+
return record;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Each coverage-ledger row's unit and status, as the findings file wrote them: the first table under the
|
|
145
|
+
* Coverage ledger heading, its header and separator rows skipped. A row with none of the three words
|
|
146
|
+
* reads "not stated"; a ledger with no rows reads as one unit, "coverage ledger", not stated. PURE.
|
|
147
|
+
*/
|
|
148
|
+
export function ledgerStatuses(text) {
|
|
149
|
+
const lines = String(text ?? "").split("\n");
|
|
150
|
+
const at = lines.findIndex((l) => /^#{1,4}\s+[^\n]*coverage[\s-]ledger/i.test(l));
|
|
151
|
+
const isSeparator = (l) => /^\|?(\s*:?-{2,}:?\s*\|)+\s*:?-{0,}:?\s*\|?$/.test(String(l ?? "").trim());
|
|
152
|
+
const out = {};
|
|
153
|
+
if (at >= 0) {
|
|
154
|
+
for (let i = at + 1; i < lines.length; i++) {
|
|
155
|
+
const l = lines[i].trim();
|
|
156
|
+
if (/^#{1,6}\s/.test(l)) break;
|
|
157
|
+
if (!l.startsWith("|")) { if (Object.keys(out).length) break; continue; }
|
|
158
|
+
if (isSeparator(l) || isSeparator(lines[i + 1])) continue; // the separator, and the header above it
|
|
159
|
+
const unit = l.replace(/^\|/, "").split("|")[0].trim();
|
|
160
|
+
if (!unit) continue;
|
|
161
|
+
out[unit] = COMMON_LAW_COVERAGE_STATUSES.find((s) => new RegExp(`(^|[^a-z-])${s}([^a-z-]|$)`, "i").test(l)) ?? NOT_STATED;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return Object.keys(out).length ? out : { "coverage ledger": NOT_STATED };
|
|
165
|
+
}
|
|
@@ -166,7 +166,7 @@ export function countMatrixCells(findingsContent) {
|
|
|
166
166
|
/**
|
|
167
167
|
* The shared shortfall ladder over a `cellsOf(term) → count` accessor. A variant is satisfied by:
|
|
168
168
|
* its own floor; " / "-packed ALTERNATES each meeting the floor (one manifest cell, two renderings —
|
|
169
|
-
* "
|
|
169
|
+
* "缎与钢 / 萨汀": each alternate is its own search term with its own grid — the copper-conduit worker
|
|
170
170
|
* keyed the split forms with full receipts while the validator demanded the compound key, 2026-06-12);
|
|
171
171
|
* or WORD-BOUNDARY substring coverage (a fully-receipted variant covers a LONGER variant containing it
|
|
172
172
|
* as a whole-word phrase — "Foxglade Legends II: Oak & Ember" ⊃ "Oak & Ember"; a marketplace phrase
|
|
@@ -300,14 +300,14 @@ export const normText = (s) => foldPunct(String(s ?? "").toLowerCase()).replace(
|
|
|
300
300
|
|
|
301
301
|
// (2026-08-04, the terminal R2 run of that round) — LANGUAGE EDITIONS OF ONE DOCUMENT ARE ONE RECEIPT.
|
|
302
302
|
// The recurrence floor demanded the model cite the SAME dictionary lemma twice, under two language editions.
|
|
303
|
-
// Attempts 2, 6 and 8 died on https://fr.wiktionary.org/wiki
|
|
304
|
-
// 9 died on https://en.wiktionary.org/wiki
|
|
303
|
+
// Attempts 2, 6 and 8 died on https://fr.wiktionary.org/wiki/βελτρίν; the model then cited it verbatim; attempt
|
|
304
|
+
// 9 died on https://en.wiktionary.org/wiki/βελτρίν and the run went terminal — ~37 minutes of model work, no
|
|
305
305
|
// artifact. Both editions cleared the floor independently (en 8 owning queries, fr 5, floor 4) and their
|
|
306
306
|
// titles diverge before TITLE_PREFIX ("Wiktionary, the free dictionary" / "Wiktionnaire, le dictionnaire
|
|
307
307
|
// libre"), so citing one PROVABLY could not satisfy the other and no attempt could have converged.
|
|
308
308
|
// Folding the leading language-edition host label weakens the compare's contract not at all: the contract is
|
|
309
|
-
// "the model quoted something it could only have got by opening the receipt", and fr.wiktionary.org/wiki
|
|
310
|
-
// proves exactly that for en.wiktionary.org/wiki
|
|
309
|
+
// "the model quoted something it could only have got by opening the receipt", and fr.wiktionary.org/wiki/βελτρίν
|
|
310
|
+
// proves exactly that for en.wiktionary.org/wiki/βελτρίν. BOUNDED two ways — only a leading 2-3 letter (or
|
|
311
311
|
// xx-yy) label, and only when a PATH follows, because folding a BARE host would let any URL on the domain
|
|
312
312
|
// satisfy a demand for one specific page. Measured on the evidence receipts: 274 distinct results → 266,
|
|
313
313
|
// and the load-bearing-by-recurrence set is the same 7, only re-keyed.
|
|
@@ -438,7 +438,7 @@ const QUOTE_MIN = 24;
|
|
|
438
438
|
// that it could not have been produced without reading the snippet. 24 Latin characters buy that. 24
|
|
439
439
|
// HANZI buy several times as much, because a Chinese character carries a morpheme where a Latin
|
|
440
440
|
// character carries a letter — so this constant asked the CJK lane for a passage far longer than the
|
|
441
|
-
// rule requires, and a
|
|
441
|
+
// rule requires, and a 维尔特林 row failed on exactly that.
|
|
442
442
|
//
|
|
443
443
|
// IT CUT BOTH WAYS, which is why both sites move together or neither does. The same constant decides
|
|
444
444
|
// `usableSnippet` — which rows are ELIGIBLE to be quote-required at all — so a CJK snippet under 24
|
|
@@ -131,7 +131,7 @@ export const TOOL_ORDER_BACKLOG = [
|
|
|
131
131
|
closes: "#865 — same shared file, same split grant." },
|
|
132
132
|
{ stage: "register-digest", tool: "register_batch_screen", site: "driver/skills/clearance-register/digest.md",
|
|
133
133
|
closes: "#865 — the digest's OWN doctrine, not a shared file: this one is the stage being told to "
|
|
134
|
-
+ "batch-screen with a tool its grant does not carry. gather-config.mjs:196 already records that "
|
|
134
|
+
+ "batch-screen with a tool its grant does not carry. gather-config.mjs:196 RECORDING already records that "
|
|
135
135
|
+ "this stage's prompt once ordered live register checks; the order outlived the note." },
|
|
136
136
|
{ stage: "register-unit", tool: "band_record", site: "driver/skills/clearance-register/SKILL.md",
|
|
137
137
|
closes: "#865 — same shared file, same split grant." },
|
|
@@ -211,7 +211,7 @@ export const E3_BACKLOG = [
|
|
|
211
211
|
kind: "literal-json-skeleton",
|
|
212
212
|
where: "driver/skills/clearance-common-law/SKILL.md:123-170",
|
|
213
213
|
surface: "skill-file",
|
|
214
|
-
evidence: "### Format\\n```markdown\\n# Common-law findings —
|
|
214
|
+
evidence: "### Format\\n```markdown\\n# Common-law findings — Dusk: Tales of Thornmantle (2026-05-11)\\n\\n## Summary\\n\\n- Perplexity calls executed: 4 …",
|
|
215
215
|
reparsedBy: "driver/publish/audit-from-spine.mjs:14 parseTables → parseSpineFindingBlocks (audit-from-spine.mjs:123) re-parses the common-law finding tables into audit.md",
|
|
216
216
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
217
217
|
},
|