clearotron 0.3.1-beta.0 → 0.3.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +17 -0
- package/INSTALL.md +1 -1
- package/README.md +6 -1
- package/THIRD-PARTY-NOTICES.md +4 -4
- package/bin/connect.mjs +2 -2
- package/bin/onboard.mjs +96 -11
- package/bin/start.mjs +129 -23
- package/build-info.json +2 -2
- package/docs/architecture/05-config-governance.md +14 -0
- package/driver/CHANGELOG.md +52 -0
- package/driver/citation-census.json +2 -1
- package/driver/common-law-receipts.mjs +78 -1
- package/driver/contract-vocabulary.mjs +6 -6
- package/driver/engine/anthropic-agent.mjs +75 -0
- package/driver/form-neighbourhood.mjs +44 -2
- package/driver/matter-frame-record.mjs +68 -1
- package/driver/named-band.mjs +1 -1
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +13 -6
- package/driver/pipeline.mjs +48 -34
- package/driver/portal-config-view.mjs +6 -0
- package/driver/portal-mcp-client.mjs +54 -9
- package/driver/portal-service.mjs +69 -6
- package/driver/profile-page.html +34 -4
- package/driver/profiles.mjs +5 -1
- package/driver/register-availability.mjs +2 -2
- package/driver/register-plan.mjs +22 -0
- package/driver/scope-ledger.mjs +33 -0
- package/driver/skills/prelim-common-law/SKILL.md +3 -1
- package/driver/skills/prelim-search/synthesis-rules.md +3 -1
- package/driver/stages.mjs +10 -1
- package/driver/suite-census.json +82 -58
- package/mcp-server/CHANGELOG.md +16 -0
- package/mcp-server/http-server.mjs +27 -4
- package/mcp-server/lib/cf-access.mjs +16 -2
- package/mcp-server/lib/http-handler.mjs +37 -1
- package/mcp-server/lib/ops.mjs +22 -9
- package/mcp-server/lib/options.mjs +1 -1
- package/mcp-server/lib/plan.mjs +47 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +29 -4
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-Cv-E_agg.css → index-D5WAoLZI.css} +20 -6
- package/portal-ui/dist/assets/{index-CwPAS0we.js → index-D8ITW-aD.js} +2151 -744
- package/portal-ui/dist/index.html +2 -2
- package/portal-ui/package.json +6 -6
- package/providers/_shared/term-shape.mjs +6 -0
- package/providers/_shared/territory-codes.mjs +43 -0
- package/providers/clarivate/src/capabilities.js +13 -1
- package/providers/clarivate/src/core.js +21 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/scripts/citation-line-check.mjs +67 -21
- package/scripts/e2e.mjs +4 -2
- package/scripts/import-cycle-check.mjs +34 -3
- package/scripts/live-surface-check.mjs +2 -2
- package/scripts/mint-names-in-force.mjs +21 -2
- package/scripts/purge-runs.mjs +114 -3
- package/scripts/revisit-render-check.mjs +77 -44
- package/scripts/test-run.mjs +7 -0
- package/scripts/travelling-predicates.mjs +1 -1
- package/shared/connect-clients.mjs +95 -16
- package/shared/invocation.mjs +25 -0
- package/shared/mcp-challenge.mjs +32 -0
- package/shared/names-in-force.mjs +2 -0
- package/shared/notice-owed.mjs +62 -0
- package/shared/scope.mjs +66 -5
- package/shared/stdio-connect.mjs +92 -24
- package/shared/trigger-lane.mjs +19 -0
- package/shared/wsl.mjs +15 -0
- package/skills/clearotron-account/SKILL.md +27 -0
- package/skills/clearotron-ops/COURIER.md +8 -1
- package/skills/clearotron-ops/SKILL.md +18 -2
package/.env.example
CHANGED
|
@@ -480,6 +480,23 @@ CLEAROTRON_CUT_REF=
|
|
|
480
480
|
# effect: tuning
|
|
481
481
|
CLEAROTRON_RELEASE_WAIT_MS=
|
|
482
482
|
|
|
483
|
+
# The trickle floor: the fewest output tokens per second of ACTIVE time (elapsed minus tool wait) a
|
|
484
|
+
# model turn may produce before it is stopped as a stall. Unset uses 1. A stage streaming a token every
|
|
485
|
+
# few seconds holds off both other clocks — the stall clock resets on any byte, and the no-progress
|
|
486
|
+
# ceiling counts token movement as progress deliberately — so before this the only thing that stopped
|
|
487
|
+
# one was its wall, which throws the whole attempt away. Set 0 to disable the floor; that is read as "no
|
|
488
|
+
# floor", never as a floor of zero. A negative or unreadable value falls back to the default rather than
|
|
489
|
+
# switching the instrument off. Read by driver/engine/anthropic-agent.mjs.
|
|
490
|
+
# effect: tuning
|
|
491
|
+
CLEAROTRON_MIN_TOKENS_PER_SEC=
|
|
492
|
+
|
|
493
|
+
# How much ACTIVE time a turn gets before the floor above may fire at all, in whole milliseconds. Unset
|
|
494
|
+
# uses 300000 (five minutes). It exists because a rate measured over a few seconds is noise, and a turn
|
|
495
|
+
# that thinks before it writes would fail an immediate floor while working perfectly. A non-positive or
|
|
496
|
+
# unreadable value falls back to the default. Read by driver/engine/anthropic-agent.mjs.
|
|
497
|
+
# effect: tuning
|
|
498
|
+
CLEAROTRON_MIN_TOKENS_WARMUP_MS=
|
|
499
|
+
|
|
483
500
|
# ── The local key door's socket ───────────────────────────────
|
|
484
501
|
# Same shape as the two below: read by product code, in neither this file nor the governance table
|
|
485
502
|
# until a private environment-contract ratchet caught it.
|
package/INSTALL.md
CHANGED
|
@@ -1326,7 +1326,7 @@ answer on any of them:
|
|
|
1326
1326
|
by that vendor's terms. AGPL-3.0 grants you nothing over it, and this repository redistributes no
|
|
1327
1327
|
part of it.
|
|
1328
1328
|
- **Register and research providers are your own agreements.** EUIPO, the USPTO bulk product,
|
|
1329
|
-
`PERPLEXITY_API_KEY`, CourtListener, and the subscription registers (
|
|
1329
|
+
`PERPLEXITY_API_KEY`, CourtListener, and the subscription registers (Clarivate, Signa, Corsearch)
|
|
1330
1330
|
each sit on terms you accept directly with that provider. The adapters in `providers/` are ours and
|
|
1331
1331
|
are licensed with the rest of the code; what they connect to is not.
|
|
1332
1332
|
- **npm dependencies carry their own licences.** `package-lock.json` is the resolved set, and each
|
package/README.md
CHANGED
|
@@ -29,6 +29,11 @@ That fetches the published package — it will ask once before downloading — t
|
|
|
29
29
|
clearances into a local portal and prints the portal's address and the passphrase to sign in with. Open
|
|
30
30
|
the address in your browser. No account, no credentials, no network calls to us.
|
|
31
31
|
|
|
32
|
+
The demo runs for as long as that window stays open, and removes everything it made when you close it —
|
|
33
|
+
nothing of it is left on the machine, and running it again later starts clean. If you want to keep the
|
|
34
|
+
sample reports after closing the window, run `npx clearotron demo --keep`; it prints the one command
|
|
35
|
+
that removes the folder when you are done with it.
|
|
36
|
+
|
|
32
37
|
**Then install it.**
|
|
33
38
|
|
|
34
39
|
```bash
|
|
@@ -102,7 +107,7 @@ clearotron run --job my-job.json
|
|
|
102
107
|
## How it fits together
|
|
103
108
|
|
|
104
109
|
- **A reasoning CLI does the thinking.** Every stage runs as a headless turn of the [Claude CLI](https://claude.com/claude-code) (`claude`) or the Codex CLI (`codex`), which must be installed. `CLEAROTRON_AI_BILLING` chooses what pays for the turn: your signed-in subscription, or an API key. Either way the CLI is what runs — there is no path that calls the model directly.
|
|
105
|
-
- **One register credential sets coverage and cost.** `CLEAROTRON_DATABASE` has no default — a run refuses rather than picking a vendor for you. EUIPO and a local USPTO index cost nothing;
|
|
110
|
+
- **One register credential sets coverage and cost.** `CLEAROTRON_DATABASE` has no default — a run refuses rather than picking a vendor for you. EUIPO and a local USPTO index cost nothing; Clarivate, Signa and Corsearch are subscriptions. [The six, and what each reaches](providers/README.md).
|
|
106
111
|
- **One research key.** `PERPLEXITY_API_KEY` covers the open web and the marketplaces. A clearance refuses without it at the door, before a register stage has spent.
|
|
107
112
|
- **A run takes hours, and survives interruption.** Every finished stage stays on disk; a resume re-runs only what is missing, and a run parked on a provider cap continues by itself.
|
|
108
113
|
- **A finished run is queryable.** An MCP server lets Claude, ChatGPT or your editor read and question
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -5028,7 +5028,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
5028
5028
|
THE SOFTWARE.
|
|
5029
5029
|
```
|
|
5030
5030
|
|
|
5031
|
-
## react@19.
|
|
5031
|
+
## react@19.3.0
|
|
5032
5032
|
|
|
5033
5033
|
- **Licence declared:** `MIT`
|
|
5034
5034
|
- **Repository:** https://github.com/react/react
|
|
@@ -5058,7 +5058,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
5058
5058
|
SOFTWARE.
|
|
5059
5059
|
```
|
|
5060
5060
|
|
|
5061
|
-
## react-dom@19.
|
|
5061
|
+
## react-dom@19.3.0
|
|
5062
5062
|
|
|
5063
5063
|
- **Licence declared:** `MIT`
|
|
5064
5064
|
- **Repository:** https://github.com/react/react
|
|
@@ -5592,10 +5592,10 @@ SOFTWARE.
|
|
|
5592
5592
|
- **Repository:** https://github.com/lddubeau/saxes
|
|
5593
5593
|
- **Licence file:** none shipped
|
|
5594
5594
|
|
|
5595
|
-
## scheduler@0.
|
|
5595
|
+
## scheduler@0.28.0
|
|
5596
5596
|
|
|
5597
5597
|
- **Licence declared:** `MIT`
|
|
5598
|
-
- **Repository:** https://github.com/
|
|
5598
|
+
- **Repository:** https://github.com/react/react
|
|
5599
5599
|
- **Licence file:** `LICENSE`
|
|
5600
5600
|
|
|
5601
5601
|
```
|
package/bin/connect.mjs
CHANGED
|
@@ -47,7 +47,7 @@ import { execFileSync } from "node:child_process";
|
|
|
47
47
|
import { createServer } from "node:net";
|
|
48
48
|
import { CONNECT_CLIENTS, WHERE_FLAG, clientById, leadRouteFor, plainStep, whatItNeeds } from "../shared/connect-clients.mjs";
|
|
49
49
|
import { stdioConnectFor, STDIO_SHAPES } from "../shared/stdio-connect.mjs";
|
|
50
|
-
import { isWsl } from "../shared/wsl.mjs";
|
|
50
|
+
import { isWsl, wslTarget } from "../shared/wsl.mjs"; // — and which distribution a row should start the server in
|
|
51
51
|
import { defaultDenylistPath, clientDoorAddress, clientDoorPort, clientDoorState, enablePlan, applyEnablePlan, describeChange, recordConnectKey, CLIENT_DOOR_UNIT } from "../shared/client-door.mjs";
|
|
52
52
|
import { mintToken, tokenId, resolvePerson, loadGrants } from "../shared/scope.mjs";
|
|
53
53
|
import { envFrom } from "../shared/env-aliases.mjs";
|
|
@@ -206,7 +206,7 @@ function deploymentHas(env = process.env) {
|
|
|
206
206
|
return {
|
|
207
207
|
// EVERY SHAPE, RESOLVED ONCE. A row picks its own; nothing here knows a client's name.
|
|
208
208
|
stdioRoutes: Object.fromEntries(Object.keys(STDIO_SHAPES).map((shape) =>
|
|
209
|
-
[shape, stdioConnectFor(shape, { workDir: env.CLEAROTRON_WORK_DIR || null, reportsDir: env.CLEAROTRON_REPORTS_DIR || null })])),
|
|
209
|
+
[shape, stdioConnectFor(shape, { workDir: env.CLEAROTRON_WORK_DIR || null, reportsDir: env.CLEAROTRON_REPORTS_DIR || null, wsl: wslTarget({ env }) })])),
|
|
210
210
|
wsl: isWsl({ env }),
|
|
211
211
|
// WHERE THE DOOR BINDS — not an address handed to any assistant. It is the loopback address the
|
|
212
212
|
// unit listens on, and `enablePlan` needs it to write the unit. It used to be passed to the
|
package/bin/onboard.mjs
CHANGED
|
@@ -66,6 +66,12 @@ import { homedir, userInfo } from "node:os";
|
|
|
66
66
|
import { invocationPrefix, installRoute, reachableCommand } from "../shared/invocation.mjs"; // — one rule for how the reader invokes us
|
|
67
67
|
import { nodeFloorVerdict } from "../shared/node-floor.mjs"; // — the floor is package.json engines, not a constant here
|
|
68
68
|
import { invocationForm } from "../shared/invocation.mjs"; // — and WHY that form
|
|
69
|
+
// One sentence about a page that is not ours, from the module that owns how a reader invokes us — NOT
|
|
70
|
+
// from bin/start.mjs, which is where it used to live. A command importing a command closes a loop that
|
|
71
|
+
// hangs at run time rather than failing at build: this file has a top-level await, so start.mjs asking
|
|
72
|
+
// for it back mid-evaluation never resolves and the wizard installs nothing. The import-cycle guard
|
|
73
|
+
// names that pair; the sentence now lives where both commands can read it.
|
|
74
|
+
import { foreignPageHint } from "../shared/invocation.mjs";
|
|
69
75
|
import { standFrom } from "../shared/invocation.mjs"; // is this tree one npm replaces?
|
|
70
76
|
import { installShim } from "../shared/verb-shim.mjs"; // — the verb goes on PATH
|
|
71
77
|
import { relocationPlan } from "../shared/permanent-install.mjs"; // — and the program out of npx's cache
|
|
@@ -118,6 +124,7 @@ import { usageBlock } from "../shared/usage-block.mjs";
|
|
|
118
124
|
import { invoke } from "../shared/invocation.mjs"; // — name a command the reader can actually type
|
|
119
125
|
import { parseEnvFile } from "../shared/env-file-merge.mjs"; // — ONE KEY=value reader, taken from a leaf: the unit renderer re-exports this same function, and it is a COMMAND, so importing it from here would put a command in this binary's graph
|
|
120
126
|
import { unitEnvironment, unitValue, couldNotDetermine } from "../driver/unit-environment.mjs"; // — F34: claim about the UNITS only from the units' own environment
|
|
127
|
+
import { DEPLOYMENT_BOXES } from "../shared/deployment-box.mjs"; // the allowlist, imported — never a second copy that drifts
|
|
121
128
|
|
|
122
129
|
/**
|
|
123
130
|
* A file's text, or null when it is not there or cannot be read.
|
|
@@ -1051,6 +1058,29 @@ export async function runCheck() {
|
|
|
1051
1058
|
say("\n Trademark clearance engine — configuration check\n");
|
|
1052
1059
|
say(` repo: ${REPO}`);
|
|
1053
1060
|
|
|
1061
|
+
// ── ONE READING OF THE UNITS' ENVIRONMENT, FOR EVERY SECTION THAT CLAIMS ANYTHING ABOUT THEM ─────
|
|
1062
|
+
//
|
|
1063
|
+
// F34: two sections asserted facts about the units while reading the operator's shell, and both of
|
|
1064
|
+
// their reported problems were false on a correctly-running install. Resolving this once is
|
|
1065
|
+
// deliberate — a second reader would drift from this one exactly as the composer and the checker did
|
|
1066
|
+
// in F41, and the drift is invisible because both sides keep passing their own arms.
|
|
1067
|
+
//
|
|
1068
|
+
// HOISTED TO THE TOP OF runCheck, from just above the Engine section, so that the FIRST section —
|
|
1069
|
+
// Deployment — can name the box from the file the units load. A box line sourced from this command's
|
|
1070
|
+
// own shell would be the defect it is there to catch: the shell and the units' file can disagree,
|
|
1071
|
+
// and it is the units' value that decides what the running service thinks it is.
|
|
1072
|
+
const fileEnv = readEnvFile(READ_ENV_PATH());
|
|
1073
|
+
const unitDir = join(homedir(), ".config", "systemd", "user");
|
|
1074
|
+
const { BACKGROUND_UNITS, startPaths } = await import(pathToFileURL(join(REPO, "bin", "start.mjs")).href);
|
|
1075
|
+
const hosted = BACKGROUND_UNITS.some((u) => existsSync(join(unitDir, u)));
|
|
1076
|
+
const unitEnv = hosted
|
|
1077
|
+
? unitEnvironment({
|
|
1078
|
+
units: BACKGROUND_UNITS.map((u) => ({ name: u, text: readIfPresent(join(unitDir, u)) })),
|
|
1079
|
+
readEnvFile: readIfPresent,
|
|
1080
|
+
// These are USER units under ~/.config/systemd/user, so systemd's %h is this home.
|
|
1081
|
+
home: homedir() })
|
|
1082
|
+
: null;
|
|
1083
|
+
|
|
1054
1084
|
// — CURRENT, not just configured. Printed before anything else because a stale deployment
|
|
1055
1085
|
// makes every line below it a report about the wrong build.
|
|
1056
1086
|
say("\n Deployment");
|
|
@@ -1080,6 +1110,56 @@ export async function runCheck() {
|
|
|
1080
1110
|
info("this does not fetch — the count is against the last fetch, not against the remote right now");
|
|
1081
1111
|
}
|
|
1082
1112
|
|
|
1113
|
+
// ── WHICH BOX THIS IS, NAMED OUT LOUD ───────────────────────────────────────────────────────────
|
|
1114
|
+
//
|
|
1115
|
+
// `CLEAROTRON_BOX` is how a deployment names itself; `shared/deployment-box.mjs` says it is never
|
|
1116
|
+
// inferred from the account, the checkout or the port, because all three correlate with the answer
|
|
1117
|
+
// and none of them IS the answer. But nothing here printed it, so an unset or misspelled value left
|
|
1118
|
+
// doctor exiting 0 and ending "Nothing wrong with what is configured" — the one check that would
|
|
1119
|
+
// catch a deployment lying about itself was the one check that did not report.
|
|
1120
|
+
//
|
|
1121
|
+
// FOUR STATES, NOT THREE. Set, unset and unrecognised are the three the requirement names. The
|
|
1122
|
+
// fourth is a hosted box whose unit environment COULD NOT BE READ, and calling that "unset" would
|
|
1123
|
+
// claim the file says nothing when it was never read — absence reported as a finding, in the section
|
|
1124
|
+
// whose entire job is to make the box visible. `couldNotDetermine` already exists for exactly this
|
|
1125
|
+
// and is what the token and URL checks below use.
|
|
1126
|
+
//
|
|
1127
|
+
// THE VALUE COMES FROM THE FILE THE UNITS LOAD, never from this command's shell, and the two can
|
|
1128
|
+
// disagree: an operator with CLEAROTRON_BOX exported in their own environment would otherwise read
|
|
1129
|
+
// their own shell back and call it the deployment's identity. The shell is reported only when it
|
|
1130
|
+
// CONTRADICTS the file, because that disagreement is itself a finding.
|
|
1131
|
+
//
|
|
1132
|
+
// The allowlist is imported rather than re-typed. Two inline copies agree on the day they are
|
|
1133
|
+
// written and drift afterwards, which is the reason that module exists at all.
|
|
1134
|
+
const boxSource = hosted ? "the units' environment" : "your environment file";
|
|
1135
|
+
const boxFileEnv = hosted ? (unitEnv?.known === true ? unitEnv.env : null) : fileEnv;
|
|
1136
|
+
if (hosted && unitEnv?.known !== true) {
|
|
1137
|
+
warn(couldNotDetermine("CLEAROTRON_BOX", unitEnv));
|
|
1138
|
+
} else {
|
|
1139
|
+
const raw = (boxFileEnv?.CLEAROTRON_BOX ?? "").trim();
|
|
1140
|
+
if (!raw) {
|
|
1141
|
+
// AN ABSENCE IS NOT A MISCONFIGURATION, and this command already separates the two — a machine
|
|
1142
|
+
// nobody has configured yet has no box, legitimately, and `--check` on one exits 0 by contract.
|
|
1143
|
+
// Reported at `!` so it is visible rather than silent, which is the whole requirement; raising it
|
|
1144
|
+
// to `✗` made every unconfigured machine fail its own acceptance arm, and said "fault" about a
|
|
1145
|
+
// state that is the normal beginning of an install.
|
|
1146
|
+
warn(`no CLEAROTRON_BOX in ${boxSource} — this deployment does not name itself, so nothing below `
|
|
1147
|
+
+ `can be read as a statement about a particular box. Set it to one of: ${DEPLOYMENT_BOXES.join(", ")}`);
|
|
1148
|
+
} else if (!DEPLOYMENT_BOXES.includes(raw)) {
|
|
1149
|
+
problem(`CLEAROTRON_BOX is "${raw}" in ${boxSource}, which is not a box this build knows `
|
|
1150
|
+
+ `(${DEPLOYMENT_BOXES.join(", ")}). An unrecognised name reads as NO box everywhere it is `
|
|
1151
|
+
+ "consulted, which is quieter than a wrong one and just as wrong");
|
|
1152
|
+
} else {
|
|
1153
|
+
ok(`this box names itself "${raw}", from ${boxSource}`);
|
|
1154
|
+
const shell = (process.env.CLEAROTRON_BOX ?? "").trim();
|
|
1155
|
+
if (shell && shell !== raw) {
|
|
1156
|
+
warn(`your shell says CLEAROTRON_BOX="${shell}", which disagrees with ${boxSource}. The running `
|
|
1157
|
+
+ "service goes by the file; your own commands go by the shell, so the two would report "
|
|
1158
|
+
+ "different boxes for the same machine");
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1083
1163
|
// ── THE TWO FILES MAY DISAGREE ABOUT THE DOOR ────────────────────────────────
|
|
1084
1164
|
//
|
|
1085
1165
|
// They are disjoint by design and are SUPPOSED to differ about paths, ports and secrets. What must
|
|
@@ -1163,7 +1243,10 @@ export async function runCheck() {
|
|
|
1163
1243
|
// Read the file up here rather than at the `.env` heading below: the engine section is the first that
|
|
1164
1244
|
// needs `effective()`, and which ENGINE is configured decides which binary variable to check. Reading
|
|
1165
1245
|
// is not applying — see readEnvFile's header.
|
|
1166
|
-
|
|
1246
|
+
// `fileEnv` is read at the top of runCheck now — the Deployment section needs it to name the box on a
|
|
1247
|
+
// box that runs no units, and a `const` used above its declaration is a dead-zone crash, not a
|
|
1248
|
+
// fallback. It reached here as one on the first draft of that section: harmless on a hosted box,
|
|
1249
|
+
// where the ternary never evaluates this branch, and fatal on every install without units.
|
|
1167
1250
|
// Environment wins over the file (the loader contract), so report the effective value and say which
|
|
1168
1251
|
// source it came from — a value read from the wrong place is the whole class of bug here.
|
|
1169
1252
|
// ── CHECK 1 — THE CHECKER READS EVERY SPELLING THE ENGINE ACCEPTS ─────────────────────────
|
|
@@ -1210,16 +1293,9 @@ export async function runCheck() {
|
|
|
1210
1293
|
// their reported problems were false on a correctly-running install. Resolving this once, here, is
|
|
1211
1294
|
// deliberate — a second reader would drift from this one exactly as the composer and the checker did
|
|
1212
1295
|
// in F41, and the drift is invisible because both sides keep passing their own arms.
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
const unitEnv = hosted
|
|
1217
|
-
? unitEnvironment({
|
|
1218
|
-
units: BACKGROUND_UNITS.map((u) => ({ name: u, text: readIfPresent(join(unitDir, u)) })),
|
|
1219
|
-
readEnvFile: readIfPresent,
|
|
1220
|
-
// These are USER units under ~/.config/systemd/user, so systemd's %h is this home.
|
|
1221
|
-
home: homedir() })
|
|
1222
|
-
: null;
|
|
1296
|
+
// The reading itself was hoisted AGAIN, to the top of runCheck, so the Deployment section can name
|
|
1297
|
+
// the box from the file the units load. `unitDir`, `hosted`, `unitEnv` and `startPaths` are all in
|
|
1298
|
+
// scope from there; nothing between here and there consumed them, which is why the move is safe.
|
|
1223
1299
|
|
|
1224
1300
|
// ── AND THE DOOR SECTION IS THE THIRD SUCH SECTION ──────────────────────────────────────────────
|
|
1225
1301
|
//
|
|
@@ -3987,6 +4063,15 @@ try {
|
|
|
3987
4063
|
say(` ${invocationPrefix()}clearotron start\n`);
|
|
3988
4064
|
say(" Starts the portal and the engine door and prints one address to open in your browser. That");
|
|
3989
4065
|
say(" address is the product: you order a clearance from it and read the report there.\n");
|
|
4066
|
+
// THE ONE SENTENCE A READER NEEDS BEFORE THEY OPEN THAT ADDRESS, and setup was the only one of the
|
|
4067
|
+
// three commands that never said it. A port can be free where the product runs and taken where the
|
|
4068
|
+
// BROWSER runs — a forwarding listener outside this environment answers 127.0.0.1 first — and the
|
|
4069
|
+
// page that opens is then somebody else's, with nothing on screen saying so. It is printed here
|
|
4070
|
+
// rather than beside an address because setup starts nothing and has no address to print: the reader
|
|
4071
|
+
// meets it moments before the command that does. Composed by the same function `start` and `demo`
|
|
4072
|
+
// use, with no number, so the three commands cannot drift into three answers.
|
|
4073
|
+
for (const line of foreignPageHint("start")) say(` ${line}`);
|
|
4074
|
+
say("");
|
|
3990
4075
|
say(` ${style.dim(`Also: \`${invocationPrefix()}clearotron demo\` replays a finished report with no keys and no model calls;`)}`);
|
|
3991
4076
|
say(` ${style.dim(`\`${invocationPrefix()}clearotron run --job ${/\s/.test(EXAMPLE_JOB) ? `"${EXAMPLE_JOB}"` : EXAMPLE_JOB}\` runs a first real clearance on the EU register.`)}`);
|
|
3992
4077
|
// THE OLD WAY IS A CHECKOUT'S. A package has no npm scripts where its reader stands.
|
package/bin/start.mjs
CHANGED
|
@@ -121,9 +121,13 @@ import { SERVER_INSTALL_SET, unitsToRestartOnRefresh, unitHealthVerdict } from "
|
|
|
121
121
|
import { defaultDenylistPath, denylistPathFor, denylistFor, ensureDenylistFile, CLIENT_DOOR_UNIT, enablePlan, clientDoorPort, demoTokenSecret, demoTokenSecretPath, keyIssueCommand } from "../shared/client-door.mjs"; // — one owner for the revocation list's path
|
|
122
122
|
import { createServer } from "node:net";
|
|
123
123
|
import { listenErrorMessage, nextFreePort } from "../shared/listen.mjs";
|
|
124
|
-
import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
124
|
+
import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
125
125
|
import { randomBytes } from "node:crypto";
|
|
126
|
-
import { invocationPrefix, invoke, reachableCommand } from "../shared/invocation.mjs"; // — the banner names the verb
|
|
126
|
+
import { invocationPrefix, invoke, reachableCommand, foreignPageHint } from "../shared/invocation.mjs"; // — the banner names the verb
|
|
127
|
+
// RE-EXPORTED, NOT RE-DECLARED. The sentence moved to a module that imports nothing of ours, because
|
|
128
|
+
// setup imports it and setup has a top-level await: a command importing a command closes a loop that
|
|
129
|
+
// hangs at run time rather than failing at build. Its readers keep this spelling.
|
|
130
|
+
export { foreignPageHint };
|
|
127
131
|
import { unitEnvPath, activeEnvPath } from "../shared/env-local.mjs"; // — the file the units read, named once
|
|
128
132
|
import { parseEnvFile } from "../shared/env-file-merge.mjs"; // ONE KEY=value reader, in a leaf: render-units is a COMMAND, and importing it from here closed a cycle
|
|
129
133
|
import { homedir, userInfo } from "node:os";
|
|
@@ -303,21 +307,6 @@ export function resolvePorts(env = {}) {
|
|
|
303
307
|
client: one("CLIENT_MCP_HTTP_PORT", clientDoorPort({})) };
|
|
304
308
|
}
|
|
305
309
|
|
|
306
|
-
/**
|
|
307
|
-
* WHAT TO DO WHEN THE PAGE THAT OPENS IS NOT OURS. Printed under every "Open" line.
|
|
308
|
-
*
|
|
309
|
-
* A port can be free where this runs and taken where the browser runs: on WSL, a Windows-side listener
|
|
310
|
-
* (VS Code's Remote-SSH forwarding is the one measured, 2026-09-11) answers 127.0.0.1 before WSL does. The
|
|
311
|
-
* doors bind cleanly, the in-use detection has nothing to see, and the browser shows somebody else's page
|
|
312
|
-
* with nothing on this screen saying so. `--port` already moves all three doors; the reader has to be told
|
|
313
|
-
* about it at the moment the address is handed over, which is here.
|
|
314
|
-
*/
|
|
315
|
-
export function foreignPageHint(verb) {
|
|
316
|
-
return [
|
|
317
|
-
"If the page that opens is not this install's sign-in, another program on this machine holds that",
|
|
318
|
-
`port from outside this environment. Run \`${invoke(verb)} --port 28802\` (or any free number) instead.`,
|
|
319
|
-
];
|
|
320
|
-
}
|
|
321
310
|
|
|
322
311
|
/**
|
|
323
312
|
* Apply `--port <n>` to the three doors.
|
|
@@ -349,6 +338,37 @@ export function portsForFlag(portFlag, ports, env = {}) {
|
|
|
349
338
|
};
|
|
350
339
|
}
|
|
351
340
|
|
|
341
|
+
/**
|
|
342
|
+
* THE DEMO'S OWN DOORS — the numbers it opens when the reader has asked for none.
|
|
343
|
+
*
|
|
344
|
+
* A demo used to start on the install's default portal port. On WSL that put the browser's address in
|
|
345
|
+
* the hands of whatever holds that number on the WINDOWS side: a Remote-SSH forward answered 127.0.0.1
|
|
346
|
+
* first, and the reader opened a production portal's "not signed in" page believing it was the demo.
|
|
347
|
+
* A local free-port walk cannot help there — the port IS free inside WSL, which is why the bind
|
|
348
|
+
* succeeded — so the fix is not to look harder but to stop using the number anybody else would.
|
|
349
|
+
*
|
|
350
|
+
* 18860 has no meaning: it is unusual, it is clear of the three an install opens by default (18802,
|
|
351
|
+
* 18790, 18811) and of the range a moved door walks into, and a stranger's machine is unlikely to be
|
|
352
|
+
* running something there. The two doors that follow take the next two numbers, which is the same
|
|
353
|
+
* arithmetic `--port` already uses, so a reader who moves the demo and a reader who does not meet one
|
|
354
|
+
* layout rather than two.
|
|
355
|
+
*
|
|
356
|
+
* AN EXPLICIT VARIABLE STILL WINS, exactly as it does for the flag: somebody who set a port chose that
|
|
357
|
+
* number, and a demo is a convenience over the defaults rather than an override of a decision. So is
|
|
358
|
+
* `--port`, which is applied after this and replaces it. PURE.
|
|
359
|
+
*/
|
|
360
|
+
export const DEMO_PORT_BASE = 18860;
|
|
361
|
+
|
|
362
|
+
export function demoPortDefaults(ports, env = {}) {
|
|
363
|
+
const explicit = (name) => String(env[name] ?? "").trim() !== "";
|
|
364
|
+
return {
|
|
365
|
+
...ports,
|
|
366
|
+
portal: explicit("PORTAL_SERVICE_PORT") ? ports.portal : DEMO_PORT_BASE,
|
|
367
|
+
mcp: explicit("TRADEMARK_MCP_HTTP_PORT") ? ports.mcp : DEMO_PORT_BASE + 1,
|
|
368
|
+
client: explicit("CLIENT_MCP_HTTP_PORT") ? ports.client : DEMO_PORT_BASE + 2,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
352
372
|
/**
|
|
353
373
|
* Where the grants file is, for a command that is NOT the supervisor.
|
|
354
374
|
*
|
|
@@ -911,6 +931,14 @@ if (isMain) {
|
|
|
911
931
|
// Decided before any path is, because in a demo every path below is the demo's own. The posture
|
|
912
932
|
// itself is described at the DEMO block further down.
|
|
913
933
|
const DEMO = argv.includes("--demo");
|
|
934
|
+
// THE DEMO CLEANS UP AFTER ITSELF (owner ruling, 2026-09-14). A reader who tries the product once
|
|
935
|
+
// should not be left with a folder they did not choose, a guest list and a signing secret in their
|
|
936
|
+
// home — and the next demo on that machine should not have to reason about the last one's leftovers.
|
|
937
|
+
// `--keep` is for somebody who wants the reports to outlive the window, and it prints the one command
|
|
938
|
+
// that removes them.
|
|
939
|
+
const DEMO_KEEP = argv.includes("--keep");
|
|
940
|
+
// Whether the reader chose this base, which decides whether leftovers in it are the demo's to reset.
|
|
941
|
+
const BASE_GIVEN = argv.includes("--base");
|
|
914
942
|
// A DEMO HAS NO BACKGROUND FORM, and asking for one is refused before anything is written. The
|
|
915
943
|
// background path installs units that run the reader's own install, so `--demo --background` would set
|
|
916
944
|
// up an empty install in their home and call it the demo.
|
|
@@ -944,13 +972,36 @@ if (isMain) {
|
|
|
944
972
|
// AN INSTALL NOBODY CONFIGURED still answers. `start --base <dir>` writes a guest list on every start
|
|
945
973
|
// and no settings at all, so an install somewhere of its own passes every check above. A guest list
|
|
946
974
|
// with no demo secret beside it is somebody's install; a demo's own base has both.
|
|
947
|
-
|
|
975
|
+
//
|
|
976
|
+
// ONLY WHERE THE READER CHOSE THE BASE. On the demo's own default directory this check refused the
|
|
977
|
+
// demo's OWN leftovers — a 0.3.0-era demo wrote a guest list there, and the next `demo` with no
|
|
978
|
+
// flag at all was told to "run the demo without --base", which is the command it had just run. The
|
|
979
|
+
// default directory is the demo's; what is in it is the demo's to reset.
|
|
980
|
+
if (BASE_GIVEN && !found.length && existsSync(paths.grants) && !existsSync(demoTokenSecretPath(paths.base)))
|
|
948
981
|
found.push(`it holds a guest list, ${paths.grants}, and no demo of its own`);
|
|
949
982
|
if (found.length)
|
|
950
983
|
fatal(`--demo cannot run in ${paths.base}: ${found.join("; ")}.\n`
|
|
951
984
|
+ " The demo keeps its own data, and its own signing secret, in its base. Leaving those in an\n"
|
|
952
985
|
+ " install's directory would make keys issued for that install refuse at its door.\n"
|
|
953
|
-
+
|
|
986
|
+
+ ` Give the demo a directory of its own, or remove ${paths.base} if nothing in it is wanted.`);
|
|
987
|
+
|
|
988
|
+
// ── AN OLDER DEMO'S BASE IS RESET, SILENTLY, AND ONLY WHEN IT IS THE DEMO'S OWN ─────────────────
|
|
989
|
+
//
|
|
990
|
+
// Demos before this one kept their base, so a machine that met the product once carries one. The
|
|
991
|
+
// next demo must neither refuse it nor start half inside it: a base holding one version's pool and
|
|
992
|
+
// another's program is a state nobody designed and nobody can read.
|
|
993
|
+
//
|
|
994
|
+
// THE RAILS ARE WHAT MAKE THIS SAFE TO WRITE AT ALL, because this line removes a directory:
|
|
995
|
+
// · a demo, never an ordinary start;
|
|
996
|
+
// · a base the reader did NOT name — the demo's own default, not a directory they pointed at;
|
|
997
|
+
// · every install signal above already checked and none found;
|
|
998
|
+
// · the path still compared against the default before the call, so a future edit that changes
|
|
999
|
+
// how the default is computed cannot silently widen what this removes.
|
|
1000
|
+
const reset = demoBaseResetTarget({ baseGiven: BASE_GIVEN, base, demoDefault: resolve(join(homedir(), "trademark-demo")) });
|
|
1001
|
+
if (reset && existsSync(reset)) {
|
|
1002
|
+
try { rmSync(reset, { recursive: true, force: true }); }
|
|
1003
|
+
catch (e) { fatal(`the demo could not reset its own folder ${reset} (${e.code ?? e.message}). Remove it and run the demo again.`); }
|
|
1004
|
+
}
|
|
954
1005
|
}
|
|
955
1006
|
// ── THIS INSTALL'S FIRST START, read before this start writes either file that answers it ────────────
|
|
956
1007
|
//
|
|
@@ -1020,6 +1071,10 @@ if (isMain) {
|
|
|
1020
1071
|
// the flag is a convenience over the defaults, not an override of a decision. Driven in the issue:
|
|
1021
1072
|
// with all three exported, the demo already came up correctly — that path must not change.
|
|
1022
1073
|
const portFlag = flag("--port");
|
|
1074
|
+
// THE DEMO'S DEFAULTS, BEFORE THE FLAG AND AFTER THE ENVIRONMENT. Applied only when the reader asked
|
|
1075
|
+
// for no port at all: `--port` below replaces whatever this chose, and a variable somebody set is
|
|
1076
|
+
// preserved inside the function itself. See demoPortDefaults for why the demo may not share 18802.
|
|
1077
|
+
if (DEMO && !portFlag) Object.assign(ports, demoPortDefaults(ports, process.env));
|
|
1023
1078
|
if (portFlag) {
|
|
1024
1079
|
let moved;
|
|
1025
1080
|
try { moved = portsForFlag(portFlag, ports, process.env); }
|
|
@@ -1969,7 +2024,7 @@ if (isMain) {
|
|
|
1969
2024
|
}
|
|
1970
2025
|
say("");
|
|
1971
2026
|
say(` Open: ${envs.url}`);
|
|
1972
|
-
for (const line of foreignPageHint(DEMO ? "demo" : "start")) say(` ${line}`);
|
|
2027
|
+
for (const line of foreignPageHint(DEMO ? "demo" : "start", ports.portal)) say(` ${line}`);
|
|
1973
2028
|
say(" This SURVIVES the terminal — close the window, the product keeps running.");
|
|
1974
2029
|
say(` Stop it: ${invoke("stop")} (stops and removes the units; issued connect keys survive — \`${invoke("disconnect")}\` revokes those)`);
|
|
1975
2030
|
say(` Is it up? ${invoke("status")}`);
|
|
@@ -2070,6 +2125,26 @@ if (isMain) {
|
|
|
2070
2125
|
}
|
|
2071
2126
|
await new Promise((r) => setTimeout(r, 200));
|
|
2072
2127
|
}
|
|
2128
|
+
// ── THE DEMO TAKES ITS FOLDER WITH IT ───────────────────────────────────────────────────────────
|
|
2129
|
+
//
|
|
2130
|
+
// AFTER the children are down, not before: the runner and the two services write into this base
|
|
2131
|
+
// while they live, and removing it under them would turn an orderly stop into a page of ENOENT.
|
|
2132
|
+
//
|
|
2133
|
+
// The same rails as the reset at the top of the run, plus one more: the demo only removes a base it
|
|
2134
|
+
// MADE this run. A reader who asked for a directory of their own keeps it — they named it, so it is
|
|
2135
|
+
// theirs — and `--keep` is for the reader who wants the reports to outlive the window.
|
|
2136
|
+
if (DEMO && !BASE_GIVEN && !DEMO_KEEP) {
|
|
2137
|
+
try {
|
|
2138
|
+
rmSync(paths.base, { recursive: true, force: true });
|
|
2139
|
+
say(`\n The demo removed everything it created: ${paths.base} is gone, and nothing of it is left on this machine.`);
|
|
2140
|
+
} catch (e) {
|
|
2141
|
+
// A FAILED CLEANUP IS SAID, NOT SWALLOWED. The promise this prints is the one thing a reader
|
|
2142
|
+
// cannot check for themselves once the window closes.
|
|
2143
|
+
err(`\n The demo could not remove ${paths.base} (${e.code ?? e.message}) — remove it by hand if nothing in it is wanted.`);
|
|
2144
|
+
}
|
|
2145
|
+
} else if (DEMO) {
|
|
2146
|
+
say(`\n The demo's folder is kept at ${paths.base}. Remove it when you are done: rm -rf ${paths.base}`);
|
|
2147
|
+
}
|
|
2073
2148
|
process.exit(code);
|
|
2074
2149
|
}
|
|
2075
2150
|
|
|
@@ -2203,7 +2278,7 @@ if (isMain) {
|
|
|
2203
2278
|
|
|
2204
2279
|
say("");
|
|
2205
2280
|
say(` Open ${envs.url}`);
|
|
2206
|
-
for (const line of foreignPageHint(DEMO ? "demo" : "start")) say(` ${line}`);
|
|
2281
|
+
for (const line of foreignPageHint(DEMO ? "demo" : "start", ports.portal)) say(` ${line}`);
|
|
2207
2282
|
say("");
|
|
2208
2283
|
// ── TWO DOORS, TWO AUDIENCES, BOTH NAMED ( — F26) ─────────────────────────
|
|
2209
2284
|
//
|
|
@@ -2285,6 +2360,11 @@ if (isMain) {
|
|
|
2285
2360
|
say(` │ WRITE THE PASSPHRASE DOWN NOW. It is stored only as a digest, so`);
|
|
2286
2361
|
say(` │ nothing — not this product, not this terminal — can read it back.`);
|
|
2287
2362
|
say(` │ Lost it? ${reset}`);
|
|
2363
|
+
// THE HINT BELONGS IN THE BOX TOO, and this was the reader the whole sentence was written for. The
|
|
2364
|
+
// frame exists because a first-time reader skips the log wall and acts on it — so the one address
|
|
2365
|
+
// they copy was the one address with nothing beside it saying what to do when the page that opens
|
|
2366
|
+
// is somebody else's. It was printed nine lines above, to a reader who by design did not read there.
|
|
2367
|
+
for (const line of foreignPageHint(DEMO ? "demo" : "start", ports.portal)) say(` │ ${line}`);
|
|
2288
2368
|
say(` └${rule}┘`);
|
|
2289
2369
|
} else {
|
|
2290
2370
|
// THE WAY BACK IN FIRST, then which credential, when and for whom: laterStartLines says why. Read for
|
|
@@ -2340,10 +2420,30 @@ if (isMain) {
|
|
|
2340
2420
|
// cannot succeed and given a service manager that is not on the machine and cannot be put there.
|
|
2341
2421
|
// Reported from a real run. Same rule as the engine refusal above: do not name a route this platform
|
|
2342
2422
|
// does not have.
|
|
2343
|
-
for (const line of backgroundOfferLines({ demo: DEMO, manager: backgroundManager(), start: invoke("start") })) say(line);
|
|
2423
|
+
for (const line of backgroundOfferLines({ demo: DEMO, keep: DEMO_KEEP || BASE_GIVEN, manager: backgroundManager(), start: invoke("start") })) say(line);
|
|
2344
2424
|
say("");
|
|
2345
2425
|
}
|
|
2346
2426
|
|
|
2427
|
+
/**
|
|
2428
|
+
* WHICH DIRECTORY, IF ANY, A DEMO MAY RESET BEFORE IT STARTS. Null means none, and null is the answer
|
|
2429
|
+
* for everything except one case.
|
|
2430
|
+
*
|
|
2431
|
+
* The one case: the demo's OWN DEFAULT directory, which the reader did not name. Demos before this one
|
|
2432
|
+
* kept their base, so a machine that met the product once carries one; the next demo must neither
|
|
2433
|
+
* refuse it — which is what happened, on a run with no flags, with a remedy telling the reader to drop
|
|
2434
|
+
* a flag they had not passed — nor start half inside it.
|
|
2435
|
+
*
|
|
2436
|
+
* A DIRECTORY THE READER NAMED IS THEIRS. `--base` is a decision about where their data lives, and
|
|
2437
|
+
* nothing in a demo may remove a path somebody chose; the install checks at the call site refuse those
|
|
2438
|
+
* instead. Separated from the call site so the rule can be driven as a table rather than by starting a
|
|
2439
|
+
* product, and because a rule this consequential should be readable in one screen. PURE.
|
|
2440
|
+
*/
|
|
2441
|
+
export function demoBaseResetTarget({ baseGiven = false, base = "", demoDefault = "" } = {}) {
|
|
2442
|
+
if (baseGiven) return null; // the reader chose it, so it is not the demo's to clear
|
|
2443
|
+
if (!base || !demoDefault) return null; // nothing to compare: say no
|
|
2444
|
+
return base === demoDefault ? base : null; // compared whole, never by prefix — a sibling path is not this
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2347
2447
|
/**
|
|
2348
2448
|
* The closing offer of a foreground start: how to get the prompt back, where there is a way.
|
|
2349
2449
|
*
|
|
@@ -2353,10 +2453,16 @@ if (isMain) {
|
|
|
2353
2453
|
* offered one. Where the offer stands, it names what it needs BEFORE the reader stops what is running.
|
|
2354
2454
|
* PURE.
|
|
2355
2455
|
*/
|
|
2356
|
-
export function backgroundOfferLines({ demo = false, manager = null, start = "clearotron start" } = {}) {
|
|
2456
|
+
export function backgroundOfferLines({ demo = false, keep = false, manager = null, start = "clearotron start" } = {}) {
|
|
2357
2457
|
if (demo) return [
|
|
2358
2458
|
" The demo has no background form: it runs as long as this window does. Leave it open and use a",
|
|
2359
2459
|
" second terminal for the commands above.",
|
|
2460
|
+
// SAID BEFORE THE READER PRESSES IT, not only afterwards. A person deciding whether to close the
|
|
2461
|
+
// window is deciding whether to keep the reports, and finding that out from the last line is too
|
|
2462
|
+
// late. `--keep` is named here because this is where the choice is made.
|
|
2463
|
+
keep
|
|
2464
|
+
? " This demo keeps its folder: the reports stay after the window closes, and the last line says how to remove them."
|
|
2465
|
+
: " Everything the demo made goes when this window closes — pass --keep to leave the folder and its reports behind.",
|
|
2360
2466
|
];
|
|
2361
2467
|
if (!manager) return [
|
|
2362
2468
|
" There is no background form on this platform: the product runs as long as this window does.",
|
package/build-info.json
CHANGED
|
@@ -180,6 +180,20 @@ and only where it exceeds the stall window), `CLEAROTRON_KILL_ESCALATE_MS`
|
|
|
180
180
|
fills mid-run surfaces as a *missing artifact* at a later stage rather than as a disk error. `0`
|
|
181
181
|
disables; a non-numeric value throws rather than silently disabling the guard.).
|
|
182
182
|
|
|
183
|
+
`CLEAROTRON_MIN_TOKENS_PER_SEC` (1) and `CLEAROTRON_MIN_TOKENS_WARMUP_MS` (5 minutes) — the trickle floor, and
|
|
184
|
+
how long a turn is left alone before it applies. A stage that streams a token every few seconds is not a
|
|
185
|
+
slow stage: it holds off the stall clock, which resets on any streamed byte, and the no-progress ceiling,
|
|
186
|
+
which counts token movement as progress on purpose, so the only thing that ever stopped one was the wall
|
|
187
|
+
— at which point the attempt is thrown away and done again from the start. The floor is counted in output
|
|
188
|
+
tokens per second of ACTIVE time (elapsed minus tool wait), for the same reason the hard ceiling is: a
|
|
189
|
+
turn waiting on a slow register lookup is producing nothing and must not be killed for it. A turn under
|
|
190
|
+
the floor is stopped there and recorded as a stall, so a retry gets the ordinary budget rather than an
|
|
191
|
+
extended one. `0` disables the floor entirely and is read as "no floor", never as a floor of zero; a
|
|
192
|
+
negative or unreadable value falls back to the default rather than switching the instrument off. The
|
|
193
|
+
default is a floor against a pathology rather than a budget for a stage — the readings it was written
|
|
194
|
+
from were three orders of magnitude apart, 0.08 tokens per second against 74 — and it is expected to
|
|
195
|
+
move once the same window has been read across more runs.
|
|
196
|
+
|
|
183
197
|
### 5.4 Pipeline feature gates & kill switches — T3 (defaults ON; `0` disables)
|
|
184
198
|
|
|
185
199
|
`CLEAROTRON_PLAN_DISPATCH`, `CLEAROTRON_SATPROBE_CODESIDE`,
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.1-beta.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 240673c: Fixed: A chat notice now carries the channel to send it on, so an assistant with several chat channels no longer drops it silently.
|
|
8
|
+
- 240673c: Fixed: Naming a country in words rather than by code now works for every country, including Belgium and Luxembourg. Before, some were carried as unrecognised.
|
|
9
|
+
- 240673c: Fixed: A family search on a name whose first or last word is a single letter or digit now runs. Before, one register refused it and the search was reported as an outage.
|
|
10
|
+
|
|
11
|
+
Fixed: A conflict whose owner could not be identified is no longer given a risk rating. It is carried as an open item naming who must be identified.
|
|
12
|
+
- f96c089: Fixed: Running the test suite from inside another test run no longer lets the outer run delete the inner run's temporary files.
|
|
13
|
+
- f96c089: Fixed: A clearance for a new client can be ordered through an assistant connector without setting up a company first.
|
|
14
|
+
- 240673c: Fixed: In the staff editor, a refused territory in a project now highlights the field it is about, as it already did when editing a customer. Before, the message appeared but no field was marked.
|
|
15
|
+
- 240673c: Fixed: A sign-in refusal now always says which instance answered — by organisation, by sign-in service, or by the address it runs on.
|
|
16
|
+
- 240673c: Fixed: The published list of settings this build reads no longer keeps a name after the code stops reading it. The list was derived from a scan that included the list itself, so a retired name kept itself alive.
|
|
17
|
+
- 240673c: Fixed: A search step that streams at a crawl is now stopped early and retried, instead of running to its time limit and losing the work.
|
|
18
|
+
- f96c089: Fixed: A mark whose main element contains no vowel — a consonant-only initialism, for example — can now be cleared. Before, the search plan refused to compile and the whole clearance ended without delivering anything.
|
|
19
|
+
- 240673c: Fixed: Use your AI now gives the steps your connector actually takes — sign-in where it signs you in, a key only where a key works.
|
|
20
|
+
- f96c089: Fixed: `doctor` now names the deployment it is checking, and refuses a name that is missing or not recognised. Before, a deployment that was misnamed — or not named at all — passed the check in silence.
|
|
21
|
+
- 240673c: Fixed: Setup and the framed first-run box now say what to do when the page that opens belongs to another program.
|
|
22
|
+
|
|
23
|
+
Fixed: The port that advice suggests is never the port already in use.
|
|
24
|
+
- f96c089: For operators: The repository's own comment-to-code references are now checked for having moved, not only for existing.
|
|
25
|
+
- 240673c: Fixed: On WSL, the "on this computer" rows now start the server inside WSL for you, so an assistant running on Windows can use them.
|
|
26
|
+
- 240673c: Fixed: Somebody you add on the People page can sign in straight away. Before, they were refused until the service restarted with a changed setting.
|
|
27
|
+
- 240673c: Fixed: The demo now removes everything it created when its window closes, and says so. Pass `--keep` to leave the folder and its reports.
|
|
28
|
+
|
|
29
|
+
Fixed: Trying the demo a second time on a machine that has run it before now works. Before, it refused its own folder and suggested dropping a flag that had not been given.
|
|
30
|
+
- 240673c: Fixed: The demo opens ports of its own rather than the ones an installation uses, so the page it points you at is the demo's.
|
|
31
|
+
- 240673c: Fixed: A search now covers every spelling and sound-alike of the name in each category of goods or services the engine judges relevant.
|
|
32
|
+
|
|
33
|
+
Fixed: Before, the added categories were searched for the name exactly and nothing else. The matter frame records each one with the reason it was added.
|
|
34
|
+
- 240673c: Fixed: An off-register search now also covers the channels the matter itself names, not only the account's usual marketplaces.
|
|
35
|
+
|
|
36
|
+
Fixed: A channel no pass ran is now recorded as open rather than described in a note.
|
|
37
|
+
|
|
38
|
+
Fixed: A finding reads what the platform's own record says before calling an owner unidentified.
|
|
39
|
+
- 240673c: For operators: The portal can now call the engine over a local socket instead of a network port, by naming it as its engine address. The deployment check reports that address as wired and says which socket it is.
|
|
40
|
+
- f96c089: Fixed: The run purge no longer deletes a clearance whose report or failure notice has not been sent yet.
|
|
41
|
+
|
|
42
|
+
Fixed: Those runs are marked in the table the purge prints, and removing one now takes a flag that says so.
|
|
43
|
+
|
|
44
|
+
For operators: Every applied purge leaves a record of what it removed, when, and whether any of it was still owed.
|
|
45
|
+
- 1905ea4: Fixed: Saving a territory the engine cannot search now says so, instead of suggesting the kind of entry that was just refused.
|
|
46
|
+
|
|
47
|
+
## 0.3.1-beta.1
|
|
48
|
+
|
|
49
|
+
### Patch Changes
|
|
50
|
+
|
|
51
|
+
- c3228f3: Fixed: A clearance that stops is recorded as owing a notice even when the folder its notice is queued in cannot be written to.
|
|
52
|
+
- c3228f3: Fixed: Check now reports the same problems Save would refuse, so a company setting can no longer pass the check and then fail to save.
|
|
53
|
+
- c3228f3: Fixed: On older Windows-Subsystem installations the engine now identifies the platform by its interop registration rather than by the kernel version string.
|
|
54
|
+
|
|
3
55
|
## 0.3.1-beta.0
|
|
4
56
|
|
|
5
57
|
### Patch Changes
|