clearotron 0.3.2-beta.12 → 0.3.2-beta.13
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/CONTRIBUTING.md +6 -5
- package/INSTALL.md +3 -4
- package/README.md +2 -1
- package/bin/example.mjs +12 -1
- package/bin/onboard.mjs +16 -2
- package/bin/start.mjs +6 -3
- package/build-info.json +2 -2
- package/docs/CLIENT-MCP.md +6 -6
- package/docs/DELIVERY.md +3 -3
- package/docs/ONBOARDING.md +1 -1
- package/docs/PORTAL.md +3 -3
- package/docs/RELEASES.md +1 -1
- package/docs/SECURITY.md +1 -1
- package/docs/architecture/04-configuration-reference.md +4 -4
- package/docs/architecture/05-config-governance.md +10 -10
- package/docs/architecture/06-operations-runbook.md +3 -3
- package/docs/architecture/07-quality-and-audit.md +1 -1
- package/docs/architecture/08-development-guide.md +2 -2
- package/docs/architecture/09-security-and-data.md +1 -1
- package/docs/decisions/0002-no-dark-functionality.md +1 -1
- package/docs/decisions/0006-what-the-public-repository-carries.md +3 -3
- package/driver/CHANGELOG.md +13 -0
- package/driver/ask-ledger.mjs +1 -1
- package/driver/band-shape.mjs +1 -1
- package/driver/bundled-demos.mjs +2 -2
- package/driver/card-budget.mjs +2 -2
- package/driver/case-law-ledger.mjs +2 -2
- package/driver/connotation-search.mjs +5 -5
- package/driver/contract-e3-backlog.mjs +13 -13
- package/driver/coverage-form.mjs +2 -2
- package/driver/demo-container.mjs +26 -2
- package/driver/disposition-tool.mjs +2 -2
- package/driver/engine/CONTRACT.md +3 -3
- package/driver/engine/mcp/gather-config.mjs +1 -1
- package/driver/engine/openai-agent.mjs +2 -2
- package/driver/findings-model.mjs +21 -6
- package/driver/gateway.mjs +1 -1
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +1 -1
- package/driver/pipeline.mjs +4 -4
- package/driver/placement-union.mjs +1 -1
- package/driver/portal-mcp-client.mjs +1 -1
- package/driver/portal-report.mjs +4 -1
- package/driver/portal-service.mjs +17 -5
- package/driver/predelivery-lint.mjs +9 -4
- package/driver/progress.mjs +37 -1
- package/driver/publish/render.mjs +6 -6
- package/driver/record-discard.mjs +1 -1
- package/driver/register-count.mjs +1 -1
- package/driver/register-digest-record.mjs +1 -1
- package/driver/report-card-record.mjs +2 -2
- package/driver/roster-verdict.mjs +2 -2
- package/driver/search-policy.mjs +1 -1
- package/driver/skeptic-record.mjs +1 -1
- package/driver/stages.mjs +1 -1
- package/driver/suite-census.json +27 -9
- package/driver/systemd/README.md +1 -1
- package/driver/unit-inventory.mjs +2 -2
- package/driver/verify.mjs +1 -1
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/CONNECT.md +8 -8
- package/mcp-server/lib/runs.mjs +1 -1
- package/mcp-server/package.json +1 -1
- package/mcp-server/serve.mjs +27 -0
- package/package.json +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/free-tier/src/capabilities.js +2 -2
- package/providers/jx/src/core.js +3 -3
- package/providers/jx/src/turn-envelope.mjs +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/perplexity/README.md +1 -1
- package/providers/signa/src/capabilities.js +2 -2
- package/providers/signa/src/core.js +2 -2
- package/providers/uspto-local/src/core.js +1 -1
- package/providers/uspto-local/src/sync.js +1 -1
- package/scripts/README.md +2 -6
- package/scripts/citation-anchor-report.mjs +1 -1
- package/scripts/citation-line-check.mjs +3 -3
- package/scripts/e2e.mjs +1 -1
- package/scripts/env-audit.mjs +1 -1
- package/scripts/env-classify.mjs +1 -1
- package/scripts/pack-publishable.mjs +1 -1
- package/scripts/release-artifact-seal.mjs +2 -2
- package/scripts/settings-render-check.mjs +5 -1
- package/scripts/strip-tracker-citations.mjs +4 -4
- package/scripts/test-run.mjs +46 -3
- package/shared/browser-temp-root.mjs +10 -3
- package/shared/client-door.mjs +18 -6
- package/shared/connect-clients.mjs +2 -0
- package/shared/identifier-scan.mjs +2 -2
- package/shared/invocation.mjs +1 -1
- package/shared/reference-guard-classes.mjs +5 -3
- package/shared/stdio-connect.mjs +39 -18
- package/shared/writing-standard-classes.mjs +2 -3
package/CONTRIBUTING.md
CHANGED
|
@@ -60,9 +60,10 @@ Four more things run for free:
|
|
|
60
60
|
| `npx clearotron demo` | Replays `demo/` — a real run on a fictional mark — through the real publisher into `~/trademark-demo/pool` and serves it. No keys, no model, no engine. |
|
|
61
61
|
|
|
62
62
|
**About `npx clearotron demo`.** `npx clearotron demo --no-open --once` publishes the sample and exits without
|
|
63
|
-
touching a browser. `
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
touching a browser. Without `--once` it serves the portal until it is stopped: `--no-open` keeps it from
|
|
64
|
+
opening a browser, `--port <n>` moves its three doors to `n`, `n+1` and `n+2`, and `--keep` leaves its folder
|
|
65
|
+
behind when it stops. `npx clearotron demo --pool <dir>` puts the pool somewhere else, and
|
|
66
|
+
`npx clearotron demo --run-dir <dir>` replays a finished run of your own instead of the shipped example.
|
|
66
67
|
|
|
67
68
|
Fix a bug, add a jurisdiction, add a doctrine test case: all of it is reachable from here.
|
|
68
69
|
|
|
@@ -90,7 +91,7 @@ this tree does not carry has to be declared with its reason, and the publication
|
|
|
90
91
|
fails an undeclared one.
|
|
91
92
|
|
|
92
93
|
**`driver/skills/**` is engine input, not documentation.** Those Markdown files are the prompt payload
|
|
93
|
-
served to the model at run time — `synthesis-rules.md` is a
|
|
94
|
+
served to the model at run time — `synthesis-rules.md` is a 12,000-word program. Editing them for
|
|
94
95
|
brevity, tone or tidiness changes what a clearance concludes. Nothing in this section, and nothing in
|
|
95
96
|
any writing pass over the documentation, applies to them.
|
|
96
97
|
|
|
@@ -192,7 +193,7 @@ with no runner assigned and no steps recorded, and every job that gates on it is
|
|
|
192
193
|
this repository: two runs on one SHA, thirty minutes apart, the push run green on every job and the
|
|
193
194
|
scheduled one red having run nothing.
|
|
194
195
|
|
|
195
|
-
That is **could
|
|
196
|
+
That is **a check that could not look**, not a fault, and this tool says so rather than making you open the run and
|
|
196
197
|
read timings to find out. It does not block on it — nothing ran, so nothing can have regressed.
|
|
197
198
|
|
|
198
199
|
**It also does not call it green.** A run that never ran told you nothing about `main`, and the last
|
package/INSTALL.md
CHANGED
|
@@ -242,16 +242,15 @@ refuses for every real user.
|
|
|
242
242
|
|
|
243
243
|
Two further constraints, both of which stop a package being cut from just anywhere:
|
|
244
244
|
|
|
245
|
-
- **The de-identification scan needs a full clone.** It refuses a shallow one (exit 2 — could
|
|
246
|
-
not a pass) because it walks history it cannot see.
|
|
245
|
+
- **The de-identification scan needs a full clone.** It refuses a shallow one (exit 2 — a check that could
|
|
246
|
+
not look, not a pass) because it walks history it cannot see.
|
|
247
247
|
- **It also needs its table**, passed with `--blocklist` (or `CLEAROTRON_IDENTIFIER_BLOCKLIST`), and
|
|
248
248
|
that table lives in the private config store rather than in this repository. Without it the scan
|
|
249
249
|
exits 2 rather than arming a weaker rule set quietly.
|
|
250
250
|
|
|
251
251
|
Both refusals are correct and neither is a workaround to route around.
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
stylistic choice.** This repository *is* the `clearotron` package, and npm links a package's `bin` into
|
|
253
|
+
This repository *is* the `clearotron` package, and npm links a package's `bin` into
|
|
255
254
|
`node_modules/.bin` only for its **dependencies** — never for the package itself. So after `npm install`
|
|
256
255
|
succeeds there is no `clearotron` on your `PATH` and none in `node_modules/.bin`; a bare `clearotron`
|
|
257
256
|
would be `command not found` with nothing having failed.
|
package/README.md
CHANGED
|
@@ -33,7 +33,8 @@ the address in your browser. No sign-up, no credentials, no network calls to us.
|
|
|
33
33
|
The demo runs for as long as that window stays open, and removes everything it made when you close it —
|
|
34
34
|
nothing of it is left on the machine, and running it again later starts clean. If you want to keep the
|
|
35
35
|
sample reports after closing the window, run `npx clearotron demo --keep`; it prints the one command
|
|
36
|
-
that removes the folder when you are done with it.
|
|
36
|
+
that removes the folder when you are done with it. `npx clearotron demo --once` publishes the reports and
|
|
37
|
+
exits, and keeps the folder the same way.
|
|
37
38
|
|
|
38
39
|
**Then install it.**
|
|
39
40
|
|
package/bin/example.mjs
CHANGED
|
@@ -47,7 +47,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
47
47
|
import { spawn } from "node:child_process";
|
|
48
48
|
import { BRAND } from "../shared/brand.mjs"; // — the installer's own name, from the tenant seam
|
|
49
49
|
import { envFrom } from "../shared/env-aliases.mjs"; // — resolves EITHER spelling; names the retired one because that is the live-writable half
|
|
50
|
-
import { isFrozen, demoChildren, demoInventory, prepareSample } from "../driver/demo-container.mjs"; // — one definition of what a frozen demo is, for the player AND the gate
|
|
50
|
+
import { isFrozen, demoChildren, demoInventory, prepareSample, releaseDemoCopies } from "../driver/demo-container.mjs"; // — one definition of what a frozen demo is, for the player AND the gate
|
|
51
51
|
import { ensureDemoProgram, demoProgramEnv } from "../shared/permanent-install.mjs";
|
|
52
52
|
|
|
53
53
|
const REPO = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -168,6 +168,14 @@ if (!sampleDirs.length || !isFrozen(sampleDir)) {
|
|
|
168
168
|
// rest are published. An unreadable file inside one sample used to throw out of the copy here and take
|
|
169
169
|
// every demo down with a stack trace (measured on a published beta, 2026-09-11).
|
|
170
170
|
const failures = ALL ? inventory.unusable.map((u) => ({ name: u.name, why: u.why })) : [];
|
|
171
|
+
// A STOP WHILE THE SAMPLES ARE COPIED OR PUBLISHED still takes the copies with it. The copies go on exit
|
|
172
|
+
// (driver/demo-container.mjs), and a signal's default ends the process without one. Taken off again once
|
|
173
|
+
// the copies are gone, before the portal's own handlers are installed.
|
|
174
|
+
const stopEarly = [["SIGINT", 2], ["SIGTERM", 15], ["SIGHUP", 1]].map(([sig, no]) => {
|
|
175
|
+
const h = () => process.exit(128 + no);
|
|
176
|
+
process.once(sig, h);
|
|
177
|
+
return [sig, h];
|
|
178
|
+
});
|
|
171
179
|
const samples = [];
|
|
172
180
|
for (const dir of sampleDirs) {
|
|
173
181
|
const r = prepareSample(dir, { repoRoot: REPO });
|
|
@@ -277,6 +285,9 @@ for (const s0 of samples) {
|
|
|
277
285
|
failures.push({ name: s0.name, why: String(e?.message ?? e) });
|
|
278
286
|
}
|
|
279
287
|
}
|
|
288
|
+
// PUBLISHED, SO THE COPIES THEY WERE PUBLISHED FROM GO NOW, whatever comes next.
|
|
289
|
+
releaseDemoCopies();
|
|
290
|
+
for (const [sig, h] of stopEarly) process.off(sig, h);
|
|
280
291
|
if (!results.length) {
|
|
281
292
|
die(`demo: no demo could be replayed.`, "", ...failures.map((f) => ` ${f.name}: ${f.why}`));
|
|
282
293
|
}
|
package/bin/onboard.mjs
CHANGED
|
@@ -3344,8 +3344,22 @@ export async function runCheck() {
|
|
|
3344
3344
|
sock.once("error", (e) => done(e.code === "ECONNREFUSED" ? false : null));
|
|
3345
3345
|
});
|
|
3346
3346
|
} catch { /* stays null — nobody could ask */ }
|
|
3347
|
+
// WHOSE LISTENER. Only this install's own evidence counts: its unit running (which binds this very
|
|
3348
|
+
// port), or a live start of this install whose record names the port. Anything else on the port is
|
|
3349
|
+
// somebody's process, and on a shared box usually another account's door.
|
|
3350
|
+
let doorOwnListener = null;
|
|
3351
|
+
if (doorListening === true) {
|
|
3352
|
+
if (doorActive === true) doorOwnListener = true;
|
|
3353
|
+
else {
|
|
3354
|
+
try {
|
|
3355
|
+
const { clientDoorPort: portOf } = await import(pathToFileURL(join(REPO, "shared", "client-door.mjs")).href);
|
|
3356
|
+
const { readRunning } = await import(pathToFileURL(join(REPO, "shared", "running-start.mjs")).href);
|
|
3357
|
+
if (readRunning().some((r) => r?.ports?.client === portOf(doorEnv))) doorOwnListener = true;
|
|
3358
|
+
} catch { /* stays null — an unread record is not a claim either way */ }
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3347
3361
|
const door = doorState({ env: doorEnv, unitDir, exists: existsSync, active: doorActive,
|
|
3348
|
-
listening: doorListening, activeState: doorActiveState, subState: doorSubState });
|
|
3362
|
+
listening: doorListening, ownListener: doorOwnListener, activeState: doorActiveState, subState: doorSubState });
|
|
3349
3363
|
// EVERY COMMAND THROUGH `invoke`. Doctor's own guard runs every command doctor
|
|
3350
3364
|
// prints from a directory that is not the install; a literal `clearotron start` in that text is
|
|
3351
3365
|
// `command not found` for a reader with no shim, and the guard caught exactly that the moment this
|
|
@@ -4272,7 +4286,7 @@ try {
|
|
|
4272
4286
|
// Enter yield the empty string, `present("")` is false, and askValue loops with "A value is needed
|
|
4273
4287
|
// here." — so a reader taking the header at its word ("Enter takes the default in brackets") on the
|
|
4274
4288
|
// one prompt that advertises empty as an answer could not leave it. Driven on the merged tree:
|
|
4275
|
-
// 60 enters, killed at the cap, every cycle this prompt. Same defect class as
|
|
4289
|
+
// 60 enters, killed at the cap, every cycle this prompt. Same defect class as the
|
|
4276
4290
|
// engine-menu loop, arriving one commit after it was fixed, in a prompt this branch's own sibling
|
|
4277
4291
|
// added.
|
|
4278
4292
|
//
|
package/bin/start.mjs
CHANGED
|
@@ -1610,9 +1610,12 @@ if (isMain) {
|
|
|
1610
1610
|
// — `clearotron demo` hands over to this — so fixing the player alone left the defect where it was.
|
|
1611
1611
|
// ONE SAMPLE AT A TIME: one whose files cannot be read is left out and named below, and the others
|
|
1612
1612
|
// seed. Copied in one call, a single unreadable file emptied the whole archive.
|
|
1613
|
-
const { publishContainer, seedDemoRuns } = await import("../driver/demo-container.mjs");
|
|
1613
|
+
const { publishContainer, seedDemoRuns, releaseDemoCopies } = await import("../driver/demo-container.mjs");
|
|
1614
1614
|
const container = publishContainer(join(REPO, "demo"), { repoRoot: REPO });
|
|
1615
|
-
|
|
1615
|
+
let seed;
|
|
1616
|
+
// THE COPY GOES AS SOON AS THE POOL IS SEEDED FROM IT, and on the way out if seeding throws.
|
|
1617
|
+
try { seed = await seedPool({ pool: paths.pool, examplesDir: container.dir, republish: republishRun }); }
|
|
1618
|
+
finally { releaseDemoCopies(); }
|
|
1616
1619
|
// AND AS RUNS, so the assistant this demo's connect line wires has them to list, brief and open. Under
|
|
1617
1620
|
// the demo's own workspace only: nothing of it reaches an install started afterwards. Their report
|
|
1618
1621
|
// links are stamped with this portal's address, the one the Open line prints.
|
|
@@ -2565,7 +2568,7 @@ if (isMain) {
|
|
|
2565
2568
|
// THE HEADINGS COME WITH THE PAIR, from the composer. Nothing is written here: the page prints these
|
|
2566
2569
|
// same two words above these same two commands, and a second author is how the two surfaces drift.
|
|
2567
2570
|
if (connect.variants) {
|
|
2568
|
-
for (const v of connect.variants) { say(` ${v.heading}`); say(` ${v.text}`); say(""); }
|
|
2571
|
+
for (const v of connect.variants) { say(` ${v.heading}`); say(` ${v.text}`); if (v.hint) say(` ${v.hint}`); say(""); }
|
|
2569
2572
|
} else {
|
|
2570
2573
|
say(` ${connect.command}`);
|
|
2571
2574
|
say("");
|
package/build-info.json
CHANGED
package/docs/CLIENT-MCP.md
CHANGED
|
@@ -46,10 +46,10 @@ uses). Enrolment is therefore the portal's: no second credential to mint, rotate
|
|
|
46
46
|
access revokes this with it. **Off unless `CLIENT_MCP_ACCOUNT_ACCESS=1`.**
|
|
47
47
|
|
|
48
48
|
**Who turns that on. The installer, since 2026-09-03** — ruling, settled
|
|
49
|
-
point 2. `render-units.mjs --apply` and `
|
|
49
|
+
point 2. `render-units.mjs --apply` and `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
|
|
52
|
-
`
|
|
52
|
+
`clearotron connect` calls.
|
|
53
53
|
|
|
54
54
|
**This supersedes the 2026-08-31 ruling** *"On demand is fine"*, under which nothing at install and no
|
|
55
55
|
rebuild's enable list could start this unit, because starting it WAS the consent that opened
|
|
@@ -57,10 +57,10 @@ access for each company's people. The owner changed the posture knowingly: **the
|
|
|
57
57
|
gate, not whether a process runs.** A door with no key issued refuses everything, which is the same
|
|
58
58
|
protection by a mechanism that does not depend on a reader finding a verb.
|
|
59
59
|
|
|
60
|
-
**`
|
|
60
|
+
**`clearotron disconnect` therefore revokes a person, not a service** (Q3). It writes the caller's key
|
|
61
61
|
ids to the denylist and strikes them from the record; it does not stop the unit and does not touch
|
|
62
62
|
`CLIENT_MCP_ACCOUNT_ACCESS`, which is the whole install's setting. Cutting everyone off is
|
|
63
|
-
`
|
|
63
|
+
`clearotron disconnect --everyone`, which states how many keys and how many people that is before
|
|
64
64
|
acting — and does not stop the service either.
|
|
65
65
|
|
|
66
66
|
An `account` principal reaches **eighteen** tools, for its own companies only — everything carrying
|
|
@@ -117,8 +117,8 @@ Four things about it are worth knowing before you offer it:
|
|
|
117
117
|
- **The caller cannot choose the model.** The tier is cost and method both, and it is the one argument on
|
|
118
118
|
the one tool that spends. Express the change with `instructions`.
|
|
119
119
|
- **Nothing bounds the spend, by ruling.** `start_run` is stamped `clientPrincipal: true` at the
|
|
120
|
-
chokepoint so `runCaps.dailyRuns` bites it; a what-if job carries no such stamp, because
|
|
121
|
-
|
|
120
|
+
chokepoint so `runCaps.dailyRuns` bites it; a what-if job carries no such stamp, because spend controls
|
|
121
|
+
were deliberately left out of it. Every experiment records what it spent; no door
|
|
122
122
|
refuses the next one. Concurrency IS bounded — `CLEAROTRON_WHATIF_MAX_CONCURRENT`, default 1 — because
|
|
123
123
|
letting a free experiment occupy the box while a paid clearance waits is a different question from
|
|
124
124
|
spend, and the ruling did not touch it.
|
package/docs/DELIVERY.md
CHANGED
|
@@ -284,12 +284,12 @@ So the checks that run are exactly those whose whole input is model-authored tex
|
|
|
284
284
|
**permission-prose**, **scope-numbers-in-prose**, **counting-consistency**,
|
|
285
285
|
**wipo-designation-language**, **prescription-prose**. Every other clearance check is listed in the
|
|
286
286
|
receipt under `notApplicable` with the reason its input does not exist here
|
|
287
|
-
(`KNOCKOUT_ABSENT_BY_DESIGN` in `driver/predelivery-lint.mjs`). That is a third state
|
|
287
|
+
(`KNOCKOUT_ABSENT_BY_DESIGN` in `driver/predelivery-lint.mjs`). That is a third state,
|
|
288
288
|
and it is the point: a check that cannot apply is recorded as absent, never as a silent pass, and
|
|
289
289
|
never as a `pass:false` that would project to the lawyer as a defect.
|
|
290
290
|
|
|
291
|
-
**Why the lane cannot simply skip the lint.** The lint receipt IS the workbook's QC record
|
|
292
|
-
|
|
291
|
+
**Why the lane cannot simply skip the lint.** The lint receipt IS the workbook's QC record,
|
|
292
|
+
so a lane that writes no`_driver/predelivery-lint.json` produces an *empty* QC record
|
|
293
293
|
rather than a deliberately-absent one — and an empty record reads as "not evaluated", never
|
|
294
294
|
"passed". And since recorded defects reach the reviewing lawyer only through the projected
|
|
295
295
|
machine-check sheet on the audit workbook, a lane with nothing to project is a lane whose
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -21,7 +21,7 @@ installer.
|
|
|
21
21
|
## The command
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
|
|
24
|
+
clearotron brandowner add <key> --name "<legal name>" [options]
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
`<key>` is the bundle's filename and its identity everywhere else. It is validated on the way in, so a
|
package/docs/PORTAL.md
CHANGED
|
@@ -99,7 +99,7 @@ There were two ways in and only one of them proved anything: the auth-proxy edge
|
|
|
99
99
|
and handed every caller the same synthetic address. **Both switches are deleted**, along with
|
|
100
100
|
`PORTAL_DEV_EMAIL`. Local mode replaces them with a real sign-in.
|
|
101
101
|
|
|
102
|
-
**Use `
|
|
102
|
+
**Use `clearotron start`.** It is the documented way to run this product on one machine
|
|
103
103
|
([INSTALL.md](../INSTALL.md) §6) and it assembles everything below for you: the portal, the MCP face the Start button
|
|
104
104
|
calls, a minted ops key, the grants file, the saved-search store and the data-plane paths — one command,
|
|
105
105
|
one URL, `Ctrl-C` stops both processes. Nothing here needs an `*_AUTH_DISABLED` variable, and the
|
|
@@ -143,7 +143,7 @@ its dev bypass to provide that**:`TRADEMARK_MCP_AUTH_MODE=token` runs it with a
|
|
|
143
143
|
access key and no auth proxy — loopback only, and refused outright alongside
|
|
144
144
|
`TRADEMARK_MCP_AUTH_DISABLED`, which authenticates nobody. Mint the key with
|
|
145
145
|
`mint-token.mjs --scope ops --sub portal --verbs start_run,stop_run --accounts foxglade`, or let
|
|
146
|
-
`
|
|
146
|
+
`clearotron start` mint one in memory at every start and never write it down.
|
|
147
147
|
|
|
148
148
|
## Putting your own login provider in front
|
|
149
149
|
|
|
@@ -177,7 +177,7 @@ the model.
|
|
|
177
177
|
1. Put the proxy in front of the portal's port and make it require sign-in. The portal must not be
|
|
178
178
|
reachable except through it — an origin anyone can reach directly is an origin with no door.
|
|
179
179
|
2. Set `PORTAL_AUTH_MODE=auth-proxy` and the four values above in `.env`.
|
|
180
|
-
3. `
|
|
180
|
+
3. `clearotron doctor` — the **Portal door** section reports which door is configured and which of
|
|
181
181
|
the four values are present, by name. It never prints their values.
|
|
182
182
|
4. Sign in through the proxy once and confirm the portal shows your address rather than a passphrase
|
|
183
183
|
box.
|
package/docs/RELEASES.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install -g clearotron@beta # newest — cut when there is something wort
|
|
|
16
16
|
| | `latest` (stable) | `beta` |
|
|
17
17
|
|---|---|---|
|
|
18
18
|
| **Version looks like** | `0.2.0` | `0.2.1-beta.4` |
|
|
19
|
-
| **Cut when** | a beta has passed a full clearance run and a from-scratch install by somebody who has never seen the product, and the
|
|
19
|
+
| **Cut when** | a beta has passed a full clearance run and a from-scratch install by somebody who has never seen the product, and the maintainers decide to ship it | when a change lands that is worth testing, or while a stable is being prepared |
|
|
20
20
|
| **Promises** | it installed and ran a real clearance end to end before it was published | it built, and the automated suite passed |
|
|
21
21
|
| **Use it if** | you are running this for real work | you want a fix that landed today, or you are helping test |
|
|
22
22
|
|
package/docs/SECURITY.md
CHANGED
|
@@ -123,7 +123,7 @@ what the mechanism guarantees.*
|
|
|
123
123
|
- One OPERATOR issuance path: `mint-token.mjs` (prints once, stores nothing; `sub` names the
|
|
124
124
|
principal in every audit line; the `jti` printed at mint time is the revocation handle). Two
|
|
125
125
|
automatic minters sit beside it on the same `mintToken`: the clearance publisher mints the report
|
|
126
|
-
link's run-bound `user` token at publish, and `
|
|
126
|
+
link's run-bound `user` token at publish, and `clearotron start` mints the portal's verb-scoped,
|
|
127
127
|
company-capped ops token in memory at every start. Neither prints, and neither is written down.
|
|
128
128
|
- **Revocation**: denylist file checked on every verification; missing file = nothing revoked (the
|
|
129
129
|
denylist can never take all auth down). **Rotation**: two-secret window, flag-day-free.
|
|
@@ -176,11 +176,11 @@ timeout shot, warm patch, backoff), the lane-wedge re-dispatch, and the rate-lim
|
|
|
176
176
|
|---|---|---|
|
|
177
177
|
| `CLEAROTRON_AI` | `anthropic-agent` (default) \| `openai-agent` | Compute engine (a registered provider adapter). `anthropic-agent` = `claude -p`; `openai-agent` = `codex exec` (both off-gateway, same normalized contract). An **unregistered** value — a typo, or the gateway-runtime adapter removed in the extraction — **fails loud**: no silent wrong-provider run. Production default is unchanged. |
|
|
178
178
|
| `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. |
|
|
179
|
-
| `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 `
|
|
180
|
-
| `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;
|
|
179
|
+
| `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. |
|
|
180
|
+
| `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. |
|
|
181
181
|
| `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. |
|
|
182
182
|
| ↳ 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. |
|
|
183
|
-
| `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 (
|
|
183
|
+
| `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. |
|
|
184
184
|
| `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. |
|
|
185
185
|
|
|
186
186
|
## Environment variable reference
|
|
@@ -225,7 +225,7 @@ deployment may override (verify live values per deployment).
|
|
|
225
225
|
| `CLEAROTRON_RUN_LOCK_POLL_MS` | 15000 | Run-slot acquire poll cadence. |
|
|
226
226
|
| `CLEAROTRON_ADMISSION_BUDGET_MS` | 7200000 (2 h) | Runner stops claiming new jobs after this per activation; leftovers re-trigger a fresh activation. |
|
|
227
227
|
| `CLEAROTRON_QUEUE_SCAN_MS` | 10000 (min 1000) | Mid-drain re-scan for newly arrived jobs. |
|
|
228
|
-
| `CLEAROTRON_WHATIF_MAX_CONCURRENT` | 1 (min 1) | How many queued what-ifs the runner drains at once. Deliberately NOT a run-slot: an experiment that took one from `CLEAROTRON_MAX_CONCURRENT_RUNS` could block an admitted paid clearance rather than merely share the box with it. This is a concurrency bound and not a spend control —
|
|
228
|
+
| `CLEAROTRON_WHATIF_MAX_CONCURRENT` | 1 (min 1) | How many queued what-ifs the runner drains at once. Deliberately NOT a run-slot: an experiment that took one from `CLEAROTRON_MAX_CONCURRENT_RUNS` could block an admitted paid clearance rather than merely share the box with it. This is a concurrency bound and not a spend control — spend controls were deliberately left out when what-if opened to each company's people (2026-08-27), and nothing here refuses the next experiment. |
|
|
229
229
|
| `CLEAROTRON_STOP_GRACE_MS` | 60000 | Grace after first SIGTERM before exit(1). |
|
|
230
230
|
| `CLEAROTRON_MAX_CLAIM_AGE_MS` | 172800000 (48 h; 0 disables) | Hard ceiling on a claim's age (from the `.pid` sidecar mtime) — beyond it, re-claim regardless of liveness. |
|
|
231
231
|
| `CLEAROTRON_KNOCKOUT_VARIANT_CAP` | unset (⇒ the lane's own cap) | Ceiling on variants a knockout screens per name. Set only to bound an unusually wide batch; absent means the lane decides. |
|
|
@@ -97,7 +97,7 @@ product doc.
|
|
|
97
97
|
| **Allowlist** (`{version, grants:[{email, customer}]}`) | T2 | git + PR on the `CLIENT_ACCESS_MAP` file | see §2 row 4 | LIVE, file-only; surfaced read-only at `admin.access` |
|
|
98
98
|
| **Ops tokens** (scope ops/user, verbs, companies, TTL) | T4 | `mint-token.mjs` CLI; jti denylist file | operator-held tokens | LIVE, CLI |
|
|
99
99
|
|
|
100
|
-
## 4b. The install surface names
|
|
100
|
+
## 4b. The install surface names
|
|
101
101
|
|
|
102
102
|
The variables a **user or installer** ever types carry the product’s own prefix. They are listed
|
|
103
103
|
by name in §5 below and in the upgrade table in INSTALL.md.
|
|
@@ -123,14 +123,14 @@ has taken it.
|
|
|
123
123
|
|
|
124
124
|
**`CLEAROTRON_JX_LANES` was held back from the August 2026 rename** — it was RETIRED 2026-07-27
|
|
125
125
|
(`pipeline.mjs` `RETIRED_ENV`, `jx-units.mjs`) and nothing reads it, so renaming a dead name looked like
|
|
126
|
-
handing an operator a name that warns about nothing.
|
|
126
|
+
handing an operator a name that warns about nothing. A decision of 2026-09-04 reversed that: the
|
|
127
127
|
whole namespace carries one prefix, dead names included, because a tree spelled two ways costs more
|
|
128
128
|
than a retired row spelled consistently. The per-lane `CLEAROTRON_NATIVE_LANGUAGE_<code>` switch in the same row **is** live and is
|
|
129
|
-
fail-OPEN: unset means ON (§5.5
|
|
129
|
+
fail-OPEN: unset means ON (§5.5).
|
|
130
130
|
|
|
131
131
|
Variables outside the install surface were left alone by the August rename — `CLEAROTRON_ENGINE_MAX_BUFFER`
|
|
132
|
-
and its siblings were never in that window. **That separate decision was taken on 2026-09-04**: the
|
|
133
|
-
|
|
132
|
+
and its siblings were never in that window. **That separate decision was taken on 2026-09-04**: the rename
|
|
133
|
+
became global and pre-cut, so the internals carry the house prefix too and the public tree never
|
|
134
134
|
shows the old namespace. No compatibility layer, no alias reading, no migration — greenfield, and our own
|
|
135
135
|
boxes rebuild.
|
|
136
136
|
|
|
@@ -207,10 +207,10 @@ move once the same window has been read across more runs.
|
|
|
207
207
|
`CLEAROTRON_BAND_TRUTH_GATE` (**never disable in prod — restores the fabrication**),
|
|
208
208
|
`CLEAROTRON_FRAME_REOPEN` (+`CLEAROTRON_FRAME_REOPEN_MAX`=1, `CLEAROTRON_REOPEN_MAX_FETCH`=150),
|
|
209
209
|
`CLEAROTRON_REGISTER_GAP_CLAMP`, `CLEAROTRON_RECALL_PROBES`, `CLEAROTRON_RECALL_TRIPWIRE`, `CLEAROTRON_WARM_RETRY`,
|
|
210
|
-
`CLEAROTRON_MODEL_WIRE_CHECK` (
|
|
210
|
+
`CLEAROTRON_MODEL_WIRE_CHECK` (fails a turn whose
|
|
211
211
|
provider reports a different model FAMILY than the driver asked for; disarming it silences the refusal
|
|
212
212
|
and never the record: `modelActual`/`modelMismatch` keep landing on every dispatch row), `CLEAROTRON_FORM_REPAIR`
|
|
213
|
-
(
|
|
213
|
+
(repairs a form-class stage failure inside the dispatch, up to twice; disarmed, the defect
|
|
214
214
|
falls through to the retry ladder exactly as it did before, visibly, and is never swallowed as
|
|
215
215
|
"validated fine"). Policy knob: `CLEAROTRON_UNREACHABLE_SENIOR`
|
|
216
216
|
(open-item|clamp). Enumerate: `CLEAROTRON_ENUMERATE_CEILING` (600/OR-stack),
|
|
@@ -359,7 +359,7 @@ systemd, writes no heartbeat, and must keep saying "waiting to start" rather tha
|
|
|
359
359
|
repo** — `git grep process.env.CLIENT_ACCESS` here returns nothing, so they are governed here and
|
|
360
360
|
never appear in the audit).
|
|
361
361
|
|
|
362
|
-
**Which identity source the portal runs
|
|
362
|
+
**Which identity source the portal runs — T4, and it is chosen by name, never inferred.**
|
|
363
363
|
`PORTAL_AUTH_MODE` selects the door: unset or `auth-proxy` (the default for a hosted deployment) means
|
|
364
364
|
any login system in front that authenticates in the browser and forwards a verifiable JWT per request.
|
|
365
365
|
**Any OIDC or JWT proxy is a choice per deployment** — for example Cloudflare Access, which is not a
|
|
@@ -367,7 +367,7 @@ special case in the code; `local` means one address and one passphrase on loopba
|
|
|
367
367
|
exactly the same thing** — normalised where the mode is read rather than by an alias row, because
|
|
368
368
|
`shared/env-aliases.mjs` maps variable NAMES and there is no value-alias mechanism.
|
|
369
369
|
|
|
370
|
-
**Bringing your own login provider
|
|
370
|
+
**Bringing your own login provider — T4.**`PORTAL_OIDC_ISSUER`,
|
|
371
371
|
`PORTAL_JWKS_URL`, `PORTAL_EMAIL_CLAIM` and `PORTAL_AUTH_HEADER` are the portal-side spelling of the four
|
|
372
372
|
values the staff MCP face already reads as `TRADEMARK_MCP_OIDC_ISSUER`, `TRADEMARK_MCP_JWKS_URL`,
|
|
373
373
|
`TRADEMARK_MCP_EMAIL_CLAIM` and `TRADEMARK_MCP_AUTH_HEADER`. `makeAccessVerifier` has always accepted them; the portal
|
|
@@ -432,7 +432,7 @@ synthetic identity would answer before the mandatory key ever ran. The mode is l
|
|
|
432
432
|
travels in a header or the query string), requires `TRADEMARK_MCP_ALLOWED_HOSTS` exactly as the
|
|
433
433
|
authenticated door does, and mirrors its mandatory `CLEAROTRON_ACCESS_FILE`.
|
|
434
434
|
|
|
435
|
-
**The local install sets all of the above itself.**`
|
|
435
|
+
**The local install sets all of the above itself.**`clearotron start` (`bin/start.mjs`) is a supervisor:
|
|
436
436
|
it resolves one set of ports, derives `PORTAL_MCP_URL` and `TRADEMARK_MCP_ALLOWED_HOSTS` from them, mints
|
|
437
437
|
the ops key in memory, and hands each child an explicit environment carrying `CLEAROTRON_NO_ENV_FILE=1`. So
|
|
438
438
|
exactly one process in that tree reads `<repo>/.env` — the supervisor — and nothing a laptop runs needs
|
|
@@ -73,7 +73,7 @@ properties below, each of which was learned the expensive way:
|
|
|
73
73
|
mid-run feeds one expensive run two different skill versions. **Call
|
|
74
74
|
check that nothing is in flight and abort if anything is** — read every queue this
|
|
75
75
|
deployment would drain plus the run-slot locks, and refuse on any of them. `--override
|
|
76
|
-
"<reason>"` is the
|
|
76
|
+
"<reason>"` is the operator's deliberate exception; the reason is printed into the deploy output, and a
|
|
77
77
|
blank one exits 2 rather than passing. This step used to be a line of prose asking a human to
|
|
78
78
|
check, which is not a guard on the night it matters. Then **stop the four trigger units** for
|
|
79
79
|
the deploy window and install an **EXIT trap that restarts them on every exit path**, success or
|
|
@@ -184,7 +184,7 @@ rather than stage compute. That gateway is no longer part of the product, so the
|
|
|
184
184
|
to probe and the file went with them. Nothing invoked it: not `package.json`, not `bin/`, not
|
|
185
185
|
`scripts/`, not a unit, not CI. It was run by hand, and it spent real money when it was.
|
|
186
186
|
|
|
187
|
-
What replaces each half: the free path check is `
|
|
187
|
+
What replaces each half: the free path check is `clearotron doctor` plus the runner's own preflights
|
|
188
188
|
(`preflightEngineBinary`, `preflightCredentials`, `preflightDeploymentUrls`), each of which refuses by
|
|
189
189
|
name before a run dir exists. The billable half has no replacement and needs none — stage compute is
|
|
190
190
|
exercised by real runs and the A/B harness, which is what its own note already said.
|
|
@@ -289,7 +289,7 @@ Copy it, point `CLEAROTRON_ACCESS_FILE` at your copy, sign in as one of its addr
|
|
|
289
289
|
exactly that organisation's companies. Then delete it and write your own — it names nobody real, which also
|
|
290
290
|
means it grants nothing you have.
|
|
291
291
|
|
|
292
|
-
`
|
|
292
|
+
`clearotron start` (§6) writes an empty roster (`{"tenants": {}}`) into its state directory: your own staff
|
|
293
293
|
address is admitted, nobody else is enrolled, and enforcement is already on.
|
|
294
294
|
|
|
295
295
|
### Ops tokens — a credential for the verbs that spend
|
|
@@ -269,7 +269,7 @@ write nothing and 7 that do:
|
|
|
269
269
|
`what_if_run` → one sandboxed stage; `what_if_result` → collect a queued one) — **executing** is
|
|
270
270
|
local stdio only, never remote, for any principal. The confirmation token is a deliberate-action
|
|
271
271
|
handshake, not a crypto boundary; the security boundary is ops scope + local-only execution.
|
|
272
|
-
Since
|
|
272
|
+
Since 2026-08-27 an `account` principal reaches all three, and `what_if_run` on that
|
|
273
273
|
path ENQUEUES into `<runDir>/_experiments/_queue/` rather than shelling — `driver/whatif-worker.mjs`,
|
|
274
274
|
drained by the runner, is what spawns the sandbox. Because the token is unsigned, a call on that path must
|
|
275
275
|
also name its `runId` so the grant check fires, and the enqueue refuses a token naming another run.
|
|
@@ -88,7 +88,7 @@ These are the things a well-meaning refactor breaks. Each is enforced somewhere;
|
|
|
88
88
|
|
|
89
89
|
Mechanically a config change ([04](04-configuration-reference.md#model-tiers-and-resolution)); two
|
|
90
90
|
traps and one law. Traps: an alias not registered in the engine's model map now **refuses the
|
|
91
|
-
dispatch by name** (
|
|
91
|
+
dispatch by name** (it used to run sonnet silently and log the alias you asked for, which is
|
|
92
92
|
the `fable` lesson turned into an error), and `CLEAROTRON_SYNTHESIS_MODEL` is read at module load (fine
|
|
93
93
|
for the oneshot service, stale in long-lived processes). The law: any grade-moving
|
|
94
94
|
change — family, effort, tier remap — ships only through the paid A/B against the reference
|
|
@@ -221,7 +221,7 @@ Four realities to respect:
|
|
|
221
221
|
|
|
222
222
|
- **Exclusion is by filename convention only.** Anything named `*.test.mjs` runs in CI; billable or
|
|
223
223
|
manual harnesses must not match the glob (historical one-off proofs with hard-coded dev paths are
|
|
224
|
-
not kept; the last billable hand-run harness, `selftest.mjs`, was deleted
|
|
224
|
+
not kept; the last billable hand-run harness, `selftest.mjs`, was deleted).
|
|
225
225
|
- **The `||=` env guards leak**: a shell exporting a real register credential or
|
|
226
226
|
`CLEAROTRON_PLAN_DISPATCH=on` is *not* overridden by the harness — run the suite in a clean env.
|
|
227
227
|
(CI is safe.)
|
|
@@ -83,7 +83,7 @@ are no root units; everything is `systemd --user`.
|
|
|
83
83
|
are stripped), and **`account`** (a signed-in person on the client face — the runs of the
|
|
84
84
|
companies they are granted, reached either by the CF sign-in with no token, or by a per-person API
|
|
85
85
|
key, `scope: account`, whose companies are re-read from the grants file on every request rather than
|
|
86
|
-
baked into it. Wider *reach* than a report link and, since
|
|
86
|
+
baked into it. Wider *reach* than a report link and, since a decision of 2026-08-27, more
|
|
87
87
|
*depth* too: the audit chain — the audit trail, the reasoning narrative, the record artifacts, a
|
|
88
88
|
register axis, and the `get_run` / `trace` / `decision_timeline` decision walk. Model identity
|
|
89
89
|
and billed counts stay sealed (`get_telemetry`, `get_provider_usage`), as does the reviewers'
|
|
@@ -60,7 +60,7 @@ Every `CLEAROTRON_*` name read by shipping code declares an effect class, and an
|
|
|
60
60
|
whatever the declaration test enforces. Do not re-derive it by hand; that is how 172, 159 and 352 all
|
|
61
61
|
came to describe the same repository.
|
|
62
62
|
|
|
63
|
-
## Addendum — 2026-08-20, on implementation
|
|
63
|
+
## Addendum — 2026-08-20, on implementation
|
|
64
64
|
|
|
65
65
|
All four switches are deleted, as ruled. One consequence above is narrowed by what the code turned out
|
|
66
66
|
to be, and it is recorded here rather than in a pull request body, because this document is what the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0006 — What the public repository carries
|
|
2
2
|
|
|
3
|
-
**Accepted 2026-08-17
|
|
3
|
+
**Accepted 2026-08-17.**
|
|
4
4
|
|
|
5
5
|
## Context
|
|
6
6
|
|
|
@@ -74,7 +74,7 @@ announces which mode it is in, once, so a reader of any run can tell which colum
|
|
|
74
74
|
"change the code" is the clause it answers to and this repository's working practice is that agents change
|
|
75
75
|
the code. `CLAUDE.md` is not carried: it was only ever a one-line pointer at `AGENTS.md`, a second copy of
|
|
76
76
|
one subject is a future contradiction ([ADR-0004](0004-documentation-structure.md)), and the de-identified
|
|
77
|
-
public cut names that file specifically.
|
|
77
|
+
public cut names that file specifically. Decided 2026-08-19, on the question raised against the
|
|
78
78
|
recovery — where the two files had been held back because the revert that stripped them recorded no
|
|
79
79
|
decision either way, and `shared/withheld-paths.mjs` did not cover them.
|
|
80
80
|
|
|
@@ -89,7 +89,7 @@ vendor-branded duplicate of it does not, however small.
|
|
|
89
89
|
citations across code, comments, env examples and docs; all but six were rewritten, and the six declared
|
|
90
90
|
are this record's own, where naming what is dropped is the content.
|
|
91
91
|
- **A binding to a withheld document is retired.** `driver/doc-constants.mjs` pinned four prose figures in
|
|
92
|
-
`docs/KNOCKOUT.md` and `docs/REGISTER-HIT-COUNTS.md`; those rows are gone, and the test pinning
|
|
92
|
+
`docs/KNOCKOUT.md` and `docs/REGISTER-HIT-COUNTS.md`; those rows are gone, and the test pinning the
|
|
93
93
|
one-file-states-it-twice case now anchors to the shape rather than to a named document.
|
|
94
94
|
- **Withholding is reversible**, which is why this is a list rather than a `git rm`. This repository keeps
|
|
95
95
|
every word.
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.2-beta.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed: A conditional verdict whose conditions are all kept in the run record no longer shows an empty "conditional on" line. The rating word stands alone.
|
|
8
|
+
- Fixed: A running clearance's card and row show the stage it is in now, including a step back during a correction pass.
|
|
9
|
+
- Fixed: The portal's health check no longer includes an internal error message when the instructions store cannot be read. The message goes to the service log instead.
|
|
10
|
+
- Fixed: `doctor` no longer describes another program on the client door's port as this install's door. It says only that a process holds the port.
|
|
11
|
+
- Fixed: The connect lines name the exact Node your install runs on, and an older Node gets one plain line instead of a crash.
|
|
12
|
+
- Fixed: The demo no longer leaves copies of its samples in your temp directory, including when it is stopped mid-way.
|
|
13
|
+
- Fixed: Running the test suite no longer leaves demo copies in the machine's temporary directory.
|
|
14
|
+
- Fixed: On WSL, the Windows connect line names your Linux distribution, or says plainly to fill it in, instead of leaving it out.
|
|
15
|
+
|
|
3
16
|
## 0.3.2-beta.12
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/driver/ask-ledger.mjs
CHANGED
|
@@ -170,7 +170,7 @@ function endingForQids(qids, join, ts) {
|
|
|
170
170
|
return null;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
/** The {ending, handoff} pair for one cross-check directive. Only `recall` is subject to
|
|
173
|
+
/** The {ending, handoff} pair for one cross-check directive. Only `recall` is subject to the
|
|
174
174
|
* discharge rule; every other net keeps the ending it always had. PURE. */
|
|
175
175
|
function recallEnding(name, qid, join, ts) {
|
|
176
176
|
const ending = endingForQids([qid], join, ts);
|
package/driver/band-shape.mjs
CHANGED
|
@@ -769,7 +769,7 @@ export function buildBandShape(band, { targets = [], inScopeClasses = [], crowdC
|
|
|
769
769
|
}
|
|
770
770
|
|
|
771
771
|
const fmtN = (n) => Number(n ?? 0).toLocaleString("en-US");
|
|
772
|
-
const cell = (v) => String(v ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim() || "—";
|
|
772
|
+
const cell = (v) => String(v ?? "").replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim() || "—";
|
|
773
773
|
|
|
774
774
|
// Bounded aggregate table: top rows by count, one honest remainder line — never for the floors.
|
|
775
775
|
function topTable(md, title, cols, entries, cap = 30) {
|
package/driver/bundled-demos.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// bundled-demos.mjs — WHICH client bundles this repo ships, read from the directory that ships them.
|
|
4
4
|
//
|
|
5
5
|
// This was a hand-maintained triple. The directory grew a fourth bundle, the triple did not, and the
|
|
6
|
-
// set-equality it feeds could no longer match anything — so the
|
|
7
|
-
// notice a door that has silently fallen back to the bundled roster, returned PASS on
|
|
6
|
+
// set-equality it feeds could no longer match anything — so the roster-fallback detector, whose whole job is to
|
|
7
|
+
// notice a door that has silently fallen back to the bundled roster, returned PASS on exactly that
|
|
8
8
|
// condition. A guard that cannot match reports that it found nothing wrong.
|
|
9
9
|
//
|
|
10
10
|
// The same shape had already produced two FALSE REFUSALS on this check, both from an exact match
|
package/driver/card-budget.mjs
CHANGED
|
@@ -74,13 +74,13 @@ function insertBulletAtSectionHead(text, headingRe, headingLine, bullet, { paren
|
|
|
74
74
|
const m = text.match(headingRe);
|
|
75
75
|
if (m) {
|
|
76
76
|
const at = m.index + m[0].length;
|
|
77
|
-
return `${text.slice(0, at)}\n${bullet}\n${text.slice(at)
|
|
77
|
+
return `${text.slice(0, at)}\n${bullet}\n${text.slice(at)}`;
|
|
78
78
|
}
|
|
79
79
|
if (parentHeadingRe) {
|
|
80
80
|
const p = text.match(parentHeadingRe);
|
|
81
81
|
if (p) {
|
|
82
82
|
const at = p.index + p[0].length;
|
|
83
|
-
return `${text.slice(0, at)}\n${headingLine}\n\n${bullet}\n${text.slice(at)
|
|
83
|
+
return `${text.slice(0, at)}\n${headingLine}\n\n${bullet}\n${text.slice(at)}`;
|
|
84
84
|
}
|
|
85
85
|
return `${text.replace(/\s*$/, "")}\n\n${parentHeadingLine}\n\n${headingLine}\n\n${bullet}\n`;
|
|
86
86
|
}
|
|
@@ -114,8 +114,8 @@ export function parseCaseLawLedger(raw) {
|
|
|
114
114
|
/**
|
|
115
115
|
* The census a reader (and a scenario) asks the ledger for. PURE, and it never judges.
|
|
116
116
|
*
|
|
117
|
-
* `readByTerritory` is the one that answers the depth-dive condition: a dive names ONE territory
|
|
118
|
-
*
|
|
117
|
+
* `readByTerritory` is the one that answers the depth-dive condition: a dive names ONE territory,
|
|
118
|
+
* so "did this dive read anything in its own territory" is a lookup, not an inference.
|
|
119
119
|
*/
|
|
120
120
|
export function caseLawRetrievalCensus(ledger) {
|
|
121
121
|
const queries = ledger?.queries ?? [];
|