clearotron 0.4.0-beta.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +5 -5
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +90 -0
- package/driver/ask-ledger.mjs +2 -2
- package/driver/coverage-form.mjs +6 -0
- package/driver/coverage-ledger.mjs +14 -2
- package/driver/driver.config.mjs +24 -7
- package/driver/engine/mcp/band-server.mjs +3 -3
- package/driver/engine/mcp/stdio-server.mjs +12 -1
- package/driver/gateway.mjs +27 -3
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +41 -10
- package/driver/pipeline.mjs +104 -12
- package/driver/progress.mjs +6 -1
- package/driver/provider-usage.mjs +16 -0
- package/driver/publish/index.mjs +11 -5
- package/driver/publish/knockout.mjs +16 -3
- package/driver/publish/render-knockout.mjs +38 -11
- package/driver/record-carry.mjs +17 -1
- package/driver/reference-strip-signatures.mjs +11 -1
- package/driver/register-plan.mjs +7 -3
- package/driver/register-served.mjs +91 -0
- package/driver/remedy-accounting.mjs +38 -9
- package/driver/reviewer-open-points.mjs +20 -23
- package/driver/score-redaction.mjs +416 -20
- package/driver/screen-gate.mjs +3 -3
- package/driver/skills/clearance-register/SKILL.md +0 -8
- package/driver/skills/clearance-register/digest.md +1 -1
- package/driver/skills/clearance-register/providers/corsearch.md +1 -1
- package/driver/skills/clearance-register/register-recipes.md +3 -9
- package/driver/skills/clearance-search/SKILL.md +2 -2
- package/driver/skills/clearance-search/phase2-execution.md +1 -1
- package/driver/skills/knockout-assess/SKILL.md +2 -0
- package/driver/stages-knockout.mjs +22 -0
- package/driver/suite-census.json +152 -26
- package/driver/unit-inventory.mjs +38 -43
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/package.json +1 -1
- package/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +2 -2
- package/providers/_shared/term-shape.mjs +1 -1
- package/providers/jx/src/core.js +0 -1
- package/providers/jx/src/judge.js +0 -1
- package/providers/jx/src/nativeread.js +0 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/signa/src/core.js +125 -17
- package/scripts/e2e-first-time.mjs +12 -4
- package/scripts/e2e-scenario-ops.mjs +25 -2
- package/scripts/e2e.mjs +246 -24
- package/scripts/mint-reference-strip-backlog.mjs +36 -2
- package/scripts/score.mjs +102 -29
- package/shared/identifier-scan.mjs +31 -1
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -110,7 +110,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
110
110
|
SOFTWARE.
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
## @modelcontextprotocol/sdk@1.30.
|
|
113
|
+
## @modelcontextprotocol/sdk@1.30.1
|
|
114
114
|
|
|
115
115
|
- **Licence declared:** `MIT`
|
|
116
116
|
- **Repository:** https://github.com/modelcontextprotocol/typescript-sdk
|
|
@@ -566,7 +566,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
566
566
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
567
567
|
```
|
|
568
568
|
|
|
569
|
-
## brace-expansion@1.1.
|
|
569
|
+
## brace-expansion@1.1.21
|
|
570
570
|
|
|
571
571
|
- **Licence declared:** `MIT`
|
|
572
572
|
- **Repository:** git://github.com/juliangruber/brace-expansion
|
|
@@ -596,7 +596,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
596
596
|
SOFTWARE.
|
|
597
597
|
```
|
|
598
598
|
|
|
599
|
-
## brace-expansion@2.1.
|
|
599
|
+
## brace-expansion@2.1.7
|
|
600
600
|
|
|
601
601
|
- **Licence declared:** `MIT`
|
|
602
602
|
- **Repository:** git://github.com/juliangruber/brace-expansion
|
|
@@ -1968,7 +1968,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
1968
1968
|
SOFTWARE.
|
|
1969
1969
|
```
|
|
1970
1970
|
|
|
1971
|
-
## fast-uri@3.1.
|
|
1971
|
+
## fast-uri@3.1.8
|
|
1972
1972
|
|
|
1973
1973
|
- **Licence declared:** `BSD-3-Clause`
|
|
1974
1974
|
- **Repository:** https://github.com/fastify/fast-uri
|
|
@@ -2626,7 +2626,7 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
2626
2626
|
PERFORMANCE OF THIS SOFTWARE.
|
|
2627
2627
|
```
|
|
2628
2628
|
|
|
2629
|
-
## ip-address@10.
|
|
2629
|
+
## ip-address@10.7.2
|
|
2630
2630
|
|
|
2631
2631
|
- **Licence declared:** `MIT`
|
|
2632
2632
|
- **Repository:** https://github.com/beaugunderson/ip-address
|
package/build-info.json
CHANGED
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,95 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- New: A knockout search now also asks whether each name is already in use in the client's field, such as a game character or a drink.
|
|
8
|
+
- Before you upgrade: a new operator key must now name the tools it may use, for example `--verbs start_run,stop_run`. Keys already issued keep working.
|
|
9
|
+
|
|
10
|
+
For operators: a revoked key stops working even where no revocation list was set up, because every connector now reads the install's own list.
|
|
11
|
+
|
|
12
|
+
Fixed: over the network, an operator key can no longer start a what-if that its assistant is not shown.
|
|
13
|
+
|
|
14
|
+
Fixed: the portal refuses a change sent from another website, or from another app on the same computer, including a sign-in.
|
|
15
|
+
- New: Searching by judgment.
|
|
16
|
+
|
|
17
|
+
This release changes how a clearance decides what to search. Until now the engine followed fixed rules: a set number of spellings, a fixed list of stores, a stop after the first identical mark. Fixed rules are fast and cheap, but they miss things. On a recent matter, a lawyer's review found marks the engine had counted but never read, and others it had raised that did not matter.
|
|
18
|
+
|
|
19
|
+
So the approach changes. Wherever the engine holds a pile of results, it now looks at what is there and decides what a lawyer would raise. It carries that forward and writes down what it set aside, and why. Nothing is dropped silently. Every wider search, every spelling set aside and every store left out appears in the audit workbook with its reason.
|
|
20
|
+
|
|
21
|
+
What you will notice:
|
|
22
|
+
- Searches widen where it could change the advice, and narrow where it cannot.
|
|
23
|
+
- Near spellings no buyer would confuse with the mark are set aside, with the reason recorded.
|
|
24
|
+
- Searches in other scripts run only in markets that file marks in that script.
|
|
25
|
+
- A large company's register is read for the marks that share the searched name. The rest is counted, not fetched.
|
|
26
|
+
- Marketplace searches cover the stores that sell the client's goods, and say which stores were left out and why.
|
|
27
|
+
- The report no longer opens with the internal reviewer's notes. They reach the reviewing lawyer separately.
|
|
28
|
+
- A large marketplace search no longer fails because its results were too big to return in one piece.
|
|
29
|
+
- Where a company's framework rates through named inputs, the report shows them beside each band.
|
|
30
|
+
|
|
31
|
+
A word on regressions. While testing this line we found two. A crowded search failed before delivering its report, and a knockout's ratings moved one step away from the lawyer's. Both were caught by running the same matters against a lawyer's answers, and both are fixed here. Every build is now tested that way before it ships. We are tuning for three things at once: a report that is right the first time, delivered fast, at a sensible cost. They pull against each other, and each release is our best current balance.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- For operators: a run now records the version of the engine program that served it, and whether that version is older than this release asks for. A search that ran on an old copy can be identified afterwards.
|
|
36
|
+
- Fixed: reports and their cover emails no longer carry the open-question rows or the internal review note. Both were written for the lawyer checking a run rather than the client reading the result, and they stay with the run record.
|
|
37
|
+
- For operators: on the Anthropic engine, a search can no longer read the install's settings or other private files.
|
|
38
|
+
- For operators: stages on the Anthropic engine can no longer run commands on the server; every tool that runs a command is removed from them.
|
|
39
|
+
- Fixed: In a crowded field, the search now reads the spellings a lawyer would raise, rather than the ones that fit under a record limit.
|
|
40
|
+
- Fixed: When a knockout's search for filings fails, the report and the audit workbook no longer show the raw error.
|
|
41
|
+
- Fixed: On Windows, a failed finding card no longer shows as covered in the audit workbook.
|
|
42
|
+
- Fixed: When a search could not be made, the report says so instead of giving a wrong reason.
|
|
43
|
+
- Fixed: A register search limited to the client's goods now searches every other office when one office cannot filter by goods.
|
|
44
|
+
- Fixed: a right the report keeps is no longer dropped because other registrations of the same right were ruled out.
|
|
45
|
+
- Fixed: A knockout search no longer drops a store listing or fan wiki page its web search found; the rating now reads every result.
|
|
46
|
+
- Fixed: A knockout's audit workbook also lists a part that failed as a whole, such as its filings listing or plain-language review. A filing shown without its link now says why.
|
|
47
|
+
- Fixed: on a Mac, `clearotron start` no longer offers to run in the background, which only works on Linux.
|
|
48
|
+
- Fixed: on a Mac, a search step could write into Clearotron's own protected folders by naming them in a different letter case. Those writes are now refused.
|
|
49
|
+
- Fixed: A register search that times out across many countries is asked again in smaller parts; one that still times out is reported as incomplete.
|
|
50
|
+
- Fixed: on the OpenAI engine, Codex's own sandbox no longer refuses the register searches a clearance runs.
|
|
51
|
+
- Fixed: Reports no longer show a gap in coverage when a search was skipped because an identical one had already run.
|
|
52
|
+
- For operators: on the OpenAI engine with Codex's sandbox on, the commands a search runs can no longer read the install's settings or other private files.
|
|
53
|
+
- Fixed: the audit workbook's What was searched tab no longer shows a web or marketplace search that found similar listings as clean.
|
|
54
|
+
- Fixed: Worldwide searches are no longer narrowed to an account's default territories, and results too crowded to read in full are no longer reported clean.
|
|
55
|
+
- Fixed: A report no longer offers a native-language investigation to a client who ordered one, and says when that investigation was not completed.
|
|
56
|
+
- Fixed: When a knockout's lookup of a filing owner fails, its read now says the lookup did not answer, instead of saying it found nothing.
|
|
57
|
+
- Fixed: Clarivate clearances no longer count look-alike spellings that mix Latin with Greek or Cyrillic letters as searches that could not be completed.
|
|
58
|
+
- For operators: Clarivate clearances no longer fetch a record, or repeat a count, search or owner lookup, that the same run already made.
|
|
59
|
+
- New: Clearotron runs natively on Windows, from PowerShell: no WSL2, no Git and no administrator rights.
|
|
60
|
+
- Fixed: closing the window that runs `clearotron start` now stops Clearotron. Before, it kept running in the background.
|
|
61
|
+
- Fixed: Creating a company in the portal no longer stops with a request to run a git command.
|
|
62
|
+
- Fixed: a full country search's report no longer says court decisions were searched when that search failed or its source was down.
|
|
63
|
+
- New: Global preliminary, multi-country and full country searches now search the web in more depth and keep more results from each search.
|
|
64
|
+
- For operators: Knockouts on Signa and Clarivate take their name and close-variation counts from the listing, so each is asked of the register once.
|
|
65
|
+
- Fixed: A new company starts with no marketplaces. The company page offers the usual ones to add, and a company with none is still searched on the general web.
|
|
66
|
+
- Fixed: Opposition windows appear again on register records, and renewal dates are read from where the register now publishes them.
|
|
67
|
+
- Fixed: opening a report no longer contacts any font service; the report carries its typeface, now Plus Jakarta Sans, inside itself.
|
|
68
|
+
- Fixed: searches no longer reuse earlier results.
|
|
69
|
+
- Before you upgrade: setup and `clearotron doctor` now ask for a newer Claude Code, because the older one quietly serves an earlier model generation.
|
|
70
|
+
- Fixed: Knockouts on the Signa register now say how many register hits the listed filings were drawn from.
|
|
71
|
+
- Fixed: Clearances on the Signa register no longer search look-alike spellings that mix Latin with Greek or Cyrillic letters; those searches found only unrelated marks.
|
|
72
|
+
- Fixed: skipped searches are no longer shown as unfinished in the report.
|
|
73
|
+
- For operators: each stage's AI program now receives only the settings it needs, and never the key that signs access keys.
|
|
74
|
+
- Fixed: The audit workbook now lists, on Coverage & gaps, any part of a report that could not be completed on the run. A knockout's Audit Trail also lists an owner lookup that got no answer.
|
|
75
|
+
- Fixed: on the OpenAI engine, a Full country search now reads case law from CourtListener and Legal Data Hunter, not EUR-Lex alone.
|
|
76
|
+
- Fixed: setup, `clearotron doctor` and the start of every search now check that the engine can write a file where a search writes its results.
|
|
77
|
+
- New: The meaning and reputation search now asks the questions the matter's framing names, in the languages whose markets matter. Where the framing names none, the report says no meaning search ran.
|
|
78
|
+
- For operators: the old default agent name is gone from the product. An install upgrading from a version before 0.2.2 must set its own agent name to keep seeing its earlier runs.
|
|
79
|
+
- Fixed: The operator tool no longer accepts instructions, which never reached the run; a call sending them is refused.
|
|
80
|
+
- Fixed: on the OpenAI engine, the tool that reads web pages now refuses addresses on the server's own network, such as cloud metadata addresses.
|
|
81
|
+
- For operators: the background worker no longer receives the key that signs access keys. Run `clearotron start --background` once to update an installed worker.
|
|
82
|
+
- Fixed: Expiry dates appear again on US trademark records, which had been left blank since the register moved where it publishes them.
|
|
83
|
+
|
|
84
|
+
## 0.4.0-beta.3
|
|
85
|
+
|
|
86
|
+
### Patch Changes
|
|
87
|
+
|
|
88
|
+
- Fixed: Reports no longer show a gap in coverage when a search was skipped because an identical one had already run.
|
|
89
|
+
- Fixed: Opposition windows appear again on register records, and renewal dates are read from where the register now publishes them.
|
|
90
|
+
- Before you upgrade: setup and `clearotron doctor` now ask for a newer Claude Code, because the older one quietly serves an earlier model generation.
|
|
91
|
+
- Fixed: Expiry dates appear again on US trademark records, which had been left blank since the register moved where it publishes them.
|
|
92
|
+
|
|
3
93
|
## 0.4.0-beta.2
|
|
4
94
|
|
|
5
95
|
### Minor Changes
|
package/driver/ask-ledger.mjs
CHANGED
|
@@ -583,8 +583,8 @@ export function parseAskClosureLines(text) {
|
|
|
583
583
|
* could-not-look, and the caller below fails toward leaving the ask OPEN rather than closing it on a
|
|
584
584
|
* file it could not read.
|
|
585
585
|
*
|
|
586
|
-
* ✕ NEVER a substring search of the serialized document. `"
|
|
587
|
-
* when the findings name
|
|
586
|
+
* ✕ NEVER a substring search of the serialized document. `"KORFIN" in JSON.stringify(findings)` is true
|
|
587
|
+
* when the findings name KORFIN TECHNOLOGIES OY and nothing else — a membership test that matches every
|
|
588
588
|
* longer name inflates whatever it is counting and reads as a clean result. The field, or nothing.
|
|
589
589
|
* PURE.
|
|
590
590
|
*/
|
package/driver/coverage-form.mjs
CHANGED
|
@@ -1005,6 +1005,12 @@ export function renderCoverageLedgerJsonFromForm(rows, classTokens) {
|
|
|
1005
1005
|
axis: String(r.axis ?? "").trim().toLowerCase(),
|
|
1006
1006
|
scope, status: String(r.status).trim().toLowerCase(), reason,
|
|
1007
1007
|
...(classes.length ? { classes } : {}),
|
|
1008
|
+
// THE COUNT AND THE PLAN KEY THE SOURCE ROW ALREADY HOLDS (ruling 551). The table two functions up
|
|
1009
|
+
// prints `total_hits` as "N hits" and this render dropped it, so the two renders of one form
|
|
1010
|
+
// disagreed about whether the run knew the size of what it left. A row without either — a seat-added
|
|
1011
|
+
// row — emits neither, which says "this row has no count" rather than "the count was lost here".
|
|
1012
|
+
...(Number.isInteger(r.total_hits) ? { total_hits: r.total_hits } : {}),
|
|
1013
|
+
...(r.qid ? { qid: String(r.qid) } : {}),
|
|
1008
1014
|
};
|
|
1009
1015
|
}));
|
|
1010
1016
|
}
|
|
@@ -450,7 +450,19 @@ export function parseCoverageLedger(md) {
|
|
|
450
450
|
// OPTIONAL structured column: the Nice classes a row is about, as an array of class-number strings — the
|
|
451
451
|
// join key scope-facts.mjs uses to attribute a coverage gap to an instructed class without re-parsing
|
|
452
452
|
// prose. Archived ledgers (no classes key) parse byte-identically to before.
|
|
453
|
-
|
|
453
|
+
// TWO MORE OPTIONAL COLUMNS, AND THEY CLOSE A DRIFT THE RENDERER'S OWN DOCTRINE FORBIDS. The form is the
|
|
454
|
+
// source and the prose table and this JSON are both renders of it, "so they agree by construction and
|
|
455
|
+
// neither can be the thing that drifts" — and they did not agree: a form row carries `total_hits` and the
|
|
456
|
+
// table prints it as "N hits", while this JSON dropped it. Measured on R18 of 2026-09-27: 50 rows of four
|
|
457
|
+
// fields, and not one of the 49 coverage-limited rows carried a count, so a reader could not tell a slice
|
|
458
|
+
// holding 200 records from one holding 300,000. Ruling 551 puts the count on the row in as many words.
|
|
459
|
+
//
|
|
460
|
+
// `qid` rides for the same reason the form keeps it in its own column: it is what joins a coverage row to
|
|
461
|
+
// the plan's question, and without it the count can only be re-attached by matching prose. A seat-added
|
|
462
|
+
// row has no qid and no count, which is a fact about that row rather than a gap in this contract.
|
|
463
|
+
//
|
|
464
|
+
// Archived ledgers parse byte-identically: both are optional and absence is unchanged behaviour.
|
|
465
|
+
const ROW_KEYS = ["axis", "scope", "status", "reason", "classes", "total_hits", "qid"];
|
|
454
466
|
|
|
455
467
|
// Extract Nice-class tokens from a ledger row's free-text scope/reason cell ("[cl 5,32]", "Class 30 leg",
|
|
456
468
|
// "nice classes 5/32"). DETERMINISTIC + conservative: only numbers 1–45 introduced by an explicit
|
|
@@ -494,7 +506,7 @@ export function parseCoverageLedgerJson(raw, { allowedAxes = REGISTER_AXES, acti
|
|
|
494
506
|
if (!r || typeof r !== "object" || Array.isArray(r))
|
|
495
507
|
throw new Error("coverage_ledger_unparseable: every row must be a plain object");
|
|
496
508
|
for (const k of Object.keys(r)) {
|
|
497
|
-
if (!ROW_KEYS.includes(k)) throw new Error(`coverage_key_unknown:${short(k)} (keys are EXACTLY: axis, scope, status, reason — plus OPTIONAL classes)`);
|
|
509
|
+
if (!ROW_KEYS.includes(k)) throw new Error(`coverage_key_unknown:${short(k)} (keys are EXACTLY: axis, scope, status, reason — plus OPTIONAL classes, total_hits, qid)`);
|
|
498
510
|
}
|
|
499
511
|
// normalize-then-validate (B): coerce cosmetic formatting (markdown/qualifier/transposition) to the
|
|
500
512
|
// canonical token, but keep the guard STRICT — an unknown axis still fails, with the ORIGINAL token in
|
package/driver/driver.config.mjs
CHANGED
|
@@ -2057,17 +2057,34 @@ export const ENGINE_BINARIES = {
|
|
|
2057
2057
|
// newer", with no ceiling. Setup installs it into the engines folder (enginesFolder, below the table)
|
|
2058
2058
|
// when the reader picks this engine, and the resolver uses it only when the machine has no copy of its
|
|
2059
2059
|
// own. The package's own `bin` field names the program, so no path inside it is written down here.
|
|
2060
|
-
//
|
|
2061
|
-
//
|
|
2062
|
-
//
|
|
2063
|
-
//
|
|
2064
|
-
//
|
|
2065
|
-
|
|
2060
|
+
// THE FLOOR IS THE OLDEST RELEASE WHOSE TIER ALIASES REACH THE CURRENT MODEL GENERATION, and each
|
|
2061
|
+
// tier crossed on its own release rather than together. Measured 2026-09-30 by driving each version's
|
|
2062
|
+
// `-p` turn at a local recorder and reading the `model` it put on the wire:
|
|
2063
|
+
//
|
|
2064
|
+
// version --model opus --model sonnet
|
|
2065
|
+
// 2.1.277 claude-opus-5 claude-sonnet-5
|
|
2066
|
+
// 2.1.278 claude-opus-5 claude-sonnet-5
|
|
2067
|
+
// 2.1.280 claude-opus-5-5 claude-sonnet-5
|
|
2068
|
+
// 2.1.281 claude-opus-5-5 claude-sonnet-5
|
|
2069
|
+
// 2.1.282 claude-opus-5-5 claude-sonnet-5
|
|
2070
|
+
// 2.1.283 claude-opus-5-5 claude-sonnet-5
|
|
2071
|
+
// 2.1.284 claude-opus-5-5 claude-sonnet-5-5
|
|
2072
|
+
//
|
|
2073
|
+
// So the old floor of 2.1.280 was exactly right for the top tier and left the sonnet tier a
|
|
2074
|
+
// generation behind for four releases, silently: the alias resolves inside the program, the request
|
|
2075
|
+
// is well formed either way, and nothing refuses. The floor moves to the release where the SECOND
|
|
2076
|
+
// tier crosses, because a stage that asks for sonnet is asking for the current sonnet.
|
|
2077
|
+
//
|
|
2078
|
+
// Below the floor the previous behaviour still holds: on 2.1.263 the alias returned the older model
|
|
2079
|
+
// and a pinned id was refused outright ("version 2.1.280 or newer is required"), measured 2026-09-22.
|
|
2080
|
+
// A floor that only asks for a program that starts is a floor that passes a machine this engine
|
|
2081
|
+
// cannot run the current generation on.
|
|
2082
|
+
package: "@anthropic-ai/claude-code", floor: "2.1.284",
|
|
2066
2083
|
// WHAT THE INSTALL TAKES ON DISK, in MB, which setup states before it asks to install. MEASURED, not
|
|
2067
2084
|
// declared by the vendor: the engines folder after a fresh install of this package into an empty
|
|
2068
2085
|
// folder, on npm 10.9.8 and on 11.19.1, 2026-09-14. A later release can be larger or smaller, so setup
|
|
2069
2086
|
// says "about". Re-measure when the floor moves.
|
|
2070
|
-
installMB:
|
|
2087
|
+
installMB: 233,
|
|
2071
2088
|
// The licence setup states wherever it tells a reader what they are about to install or use, as the
|
|
2072
2089
|
// vendor's package declares it: "SEE LICENSE IN README.md", Anthropic's own terms.
|
|
2073
2090
|
licence: "proprietary third-party software",
|
|
@@ -159,9 +159,9 @@ const recordUriFile = (uri) => String(uri ?? "").toLowerCase().replace(/^\/mark\
|
|
|
159
159
|
// /mark/ae/229552 the bare uri (the only form that resolved) → ae-229552.json
|
|
160
160
|
// https://tm.corsearch.com/mark/ae/229552 a provider URL (the VENZY join defect's shape)
|
|
161
161
|
// `/mark/ae/229552`, "…/mark/ae/229552." a cite carrying markdown/punctuation
|
|
162
|
-
// /mark/ch/
|
|
163
|
-
// judgment cites the record (screen-gate.mjs
|
|
164
|
-
//
|
|
162
|
+
// /mark/ch/30419 vs ch-30419-2014.json the store holds the registration-INSTANCE uri while
|
|
163
|
+
// judgment cites the record (`findScreenGateViolations` in screen-gate.mjs — a
|
|
164
|
+
// false hard-halt, the same fact at a different
|
|
165
165
|
// granularity)
|
|
166
166
|
// So: canonicalise the CITE through normalizeRecordUri (registry-fidelity.mjs) — the canonical form
|
|
167
167
|
// pipeline, recall-reconciliation and presence-reconciliation already join on, and the
|
|
@@ -156,7 +156,18 @@ export function serve({ name, version = "0.1.0", tools = [] }) {
|
|
|
156
156
|
// a seat can act on it in the turn it happens.
|
|
157
157
|
const unmet = requiredFieldViolations(tool.inputSchema, params?.arguments);
|
|
158
158
|
if (unmet.length) {
|
|
159
|
-
|
|
159
|
+
// THE REASON THE ROW HAD IN SCOPE AND THREW AWAY. A settled row with
|
|
160
|
+
// `ok: false` and nothing else asserts a failure and withholds everything that would let a
|
|
161
|
+
// reader weigh it — and a verdict turns on that, because a failed tool call counts against a
|
|
162
|
+
// run's cleanliness. Measured on the round of 2026-09-26: this gate settled a frame call in the
|
|
163
|
+
// SAME MILLISECOND it started, wrote no call file, and the record could not say why.
|
|
164
|
+
//
|
|
165
|
+
// THE FIELD NAMES, NEVER THE MESSAGE. `unmet` is a list of schema keys, so it carries no matter
|
|
166
|
+
// content; the sentence below carries none today either, but a tool's refusal text is free to
|
|
167
|
+
// interpolate a mark, and this row's own contract is the call and never its content. A row that
|
|
168
|
+
// copied the message would leak the day one of them does; a row that copies `unmet` cannot.
|
|
169
|
+
logToolEvent({ event: "settled", seq, server: name, tool: tool.name, ...(axis ? { axis } : {}), ok: false,
|
|
170
|
+
reason: `missing_required:${unmet.join(",")}` });
|
|
160
171
|
return ok(id, { isError: true, content: [{ type: "text", text:
|
|
161
172
|
`${tool.name}_missing_required:${unmet.join(",")} — the schema you were handed declares `
|
|
162
173
|
+ `${unmet.length === 1 ? "this field" : "these fields"} REQUIRED and the call did not carry `
|
package/driver/gateway.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, wri
|
|
|
13
13
|
import { join, dirname, basename } from "node:path";
|
|
14
14
|
import { driverDir } from "../shared/driver-dir.mjs"; //
|
|
15
15
|
import { tmpdir } from "node:os";
|
|
16
|
-
import { config, resolveModel, modelFamily, modelSnapshotKind, envOn, envGateOn, preflightEngineBinary } from "./driver.config.mjs";
|
|
16
|
+
import { config, resolveModel, modelFamily, modelSnapshotKind, envOn, envGateOn, preflightEngineBinary, olderThanFloor, ENGINE_BINARIES } from "./driver.config.mjs";
|
|
17
17
|
import { probeCliVersion } from "./engine/cli-version.mjs";
|
|
18
18
|
import { stageLog, runLog, note, outputMeta } from "./log.mjs";
|
|
19
19
|
// — the closed disposition set has ONE author; this file dictates it and must not retype it.
|
|
@@ -1185,8 +1185,30 @@ async function runStageLadder(name, opts, stageCodexHome = null) {
|
|
|
1185
1185
|
const cli = (() => {
|
|
1186
1186
|
try {
|
|
1187
1187
|
const pre = preflightEngineBinary(process.env);
|
|
1188
|
-
|
|
1189
|
-
|
|
1188
|
+
const probed = probeCliVersion(pre?.resolved ?? null);
|
|
1189
|
+
// ── THE FLOOR, COMPARED WHERE THE RUN CAN SEE IT ────────────────────────────────────────
|
|
1190
|
+
//
|
|
1191
|
+
// The build declares a minimum version for each engine's program, and until now nothing
|
|
1192
|
+
// compared a running copy against it. The floor was passed to the installer, so a copy this
|
|
1193
|
+
// product installs cannot land beneath it — but resolution walks PATH first and takes the
|
|
1194
|
+
// first hit, so a copy already on the machine wins and was never measured against anything.
|
|
1195
|
+
// That is the common case, not the edge one: most machines have their own copy. A stage then
|
|
1196
|
+
// runs on a program below the floor and is served whatever that program serves, silently.
|
|
1197
|
+
//
|
|
1198
|
+
// RECORDED, NOT REFUSED. Refusing here would stop every run on any machine whose copy is
|
|
1199
|
+
// behind — including ones already deployed and working — and that is a decision about those
|
|
1200
|
+
// deployments rather than a defect fix. What was missing is the fact, so the fact is what is
|
|
1201
|
+
// written: an operator, and anyone reading the run afterwards, can now see it. Whether it
|
|
1202
|
+
// should also refuse is a separate question and is not answered here.
|
|
1203
|
+
//
|
|
1204
|
+
// THREE-VALUED and written unconditionally, like the model fields below: true, false, or null
|
|
1205
|
+
// when either side names no version this build can compare — an unknown is an unknown, never
|
|
1206
|
+
// an all-clear. The floor itself rides along so the record says what it was measured against
|
|
1207
|
+
// rather than leaving a later reader to guess which build's floor applied.
|
|
1208
|
+
const floor = ENGINE_BINARIES[pre?.engine]?.floor ?? null;
|
|
1209
|
+
const belowFloor = floor ? olderThanFloor(probed?.version ?? null, floor) : null;
|
|
1210
|
+
return { ...probed, source: pre?.source ?? null, floor, belowFloor };
|
|
1211
|
+
} catch (e) { return { version: null, probe: "unreadable", why: String(e?.message ?? e).slice(0, 160), source: null, floor: null, belowFloor: null }; }
|
|
1190
1212
|
})();
|
|
1191
1213
|
if (modelActual) lastModelWire = modelActual; // — never overwritten with null
|
|
1192
1214
|
// The comparison is by FAMILY (driver.config modelFamily), because `--model haiku` legitimately comes
|
|
@@ -1658,6 +1680,7 @@ async function runStageLadder(name, opts, stageCodexHome = null) {
|
|
|
1658
1680
|
// different from "this record predates the gauge".
|
|
1659
1681
|
modelActual, modelBasis, modelSnapshot, modelMismatch, providerReported,
|
|
1660
1682
|
cliVersion: cli.version, cliVersionProbe: cli.probe, ...(cli.why ? { cliVersionWhy: cli.why } : {}), cliSource: cli.source,
|
|
1683
|
+
cliFloor: cli.floor ?? null, cliBelowFloor: cli.belowFloor ?? null,
|
|
1661
1684
|
// W3 billing telemetry: which engine ran + the RESOLVED billing mode (subscription vs api-key). This
|
|
1662
1685
|
// records INTENT (the mode the engine was configured to bill under), not independent billing evidence
|
|
1663
1686
|
// — the actual proof is the provider console (claude's stream also reports apiKeySource; codex does
|
|
@@ -1797,6 +1820,7 @@ async function runStageLadder(name, opts, stageCodexHome = null) {
|
|
|
1797
1820
|
// ran. `model` stays the requested resolution (its existing readers); `modelActual` is the wire.
|
|
1798
1821
|
model: modelRequested, modelActual, modelBasis, modelSnapshot, modelMismatch, providerReported,
|
|
1799
1822
|
cliVersion: cli.version, cliVersionProbe: cli.probe, ...(cli.why ? { cliVersionWhy: cli.why } : {}), cliSource: cli.source,
|
|
1823
|
+
cliFloor: cli.floor ?? null, cliBelowFloor: cli.belowFloor ?? null,
|
|
1800
1824
|
wrote, warm: warm || undefined, warmEscalated: attempt === warmEscalatedAt || undefined,
|
|
1801
1825
|
rescued: rescued ?? undefined, killed: killed || undefined,
|
|
1802
1826
|
quiescentMs: Number.isFinite(quiescentMs) ? Math.round(quiescentMs) : undefined, // — see the per-stage row
|
package/driver/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "clearotron-driver",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.0
|
|
5
|
+
"version": "0.4.0",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"description": "Deterministic driver for the trademark clearance workflow: orchestration in code (fan-out, fan-in barrier, gating, retries); the model does judgment leaves only, through a reasoning CLI spawned per stage.",
|
|
8
8
|
"engines": {
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
import { RunCancelled, assertNotCancelledBeforePublish } from "./cancel.mjs"; // stop-by-user: never the failure lane below
|
|
48
48
|
import { loadFrameworkManifest, parseFrameworkManifest, frameworkFor, DEFAULT_FRAMEWORK } from "./framework.mjs";
|
|
49
49
|
import { attachFrameworkMethod, methodPathFor, FROZEN_METHOD_FILE } from "./framework-method.mjs";
|
|
50
|
-
import { KO_STAGES, KO_STEPS, KO_STEP_REGISTER_COUNT, koSteps, koPaths, kebab, knockoutGridSpec, KNOCKOUT_WEB, KNOCKOUT_MINUTES_BAR, koChunks } from "./stages-knockout.mjs";
|
|
50
|
+
import { KO_STAGES, KO_STEPS, KO_STEP_REGISTER_COUNT, koSteps, koPaths, kebab, knockoutGridSpec, KNOCKOUT_WEB, KNOCKOUT_MINUTES_BAR, knockoutBarVerdict, koChunks } from "./stages-knockout.mjs";
|
|
51
51
|
import { kebabCollisions, reportIdentityFor, CAPABILITY_SKIPPED_CAUSE, CAPABILITY_SKIPPED_NOTE } from "./search-policy.mjs";
|
|
52
52
|
import { countPreflight, countRegisterHits, countedMarks, resolveCountExecutor } from "./register-count.mjs";
|
|
53
53
|
import { recordsPreflight, listRegisterRecords, listedMarks, resolveRecordExecutor } from "./register-records.mjs";
|
|
@@ -60,7 +60,7 @@ import { validators as koValidators, validateMergedFindings, worstBand, register
|
|
|
60
60
|
import { reviewAbout, reviewEvidence, reviewEvidenceLines, applyKnockoutReview, knockoutReviewFile } from "./knockout-review-record.mjs";
|
|
61
61
|
import { stripNextStepSections } from "./knockout-next-step.mjs";
|
|
62
62
|
import { publishKnockout, composeKnockoutEmail } from "./publish/knockout.mjs";
|
|
63
|
-
import { writeRunStatus, rollupStatus, atomicWrite, identitySeed } from "./progress.mjs"; // — the identity seed is shared; the stepper is not
|
|
63
|
+
import { writeRunStatus, readRunStatus, rollupStatus, atomicWrite, identitySeed } from "./progress.mjs"; // — the identity seed is shared; the stepper is not
|
|
64
64
|
import { batchMarkName } from "./mark-name.mjs";
|
|
65
65
|
import { runLog, note, outputMeta } from "./log.mjs";
|
|
66
66
|
import { defaultTerritoryState } from "./effective-scope.mjs"; // the stored-defaults reading — one producer, shared with the clearance lane
|
|
@@ -70,7 +70,6 @@ import { rollupTokens, stampTokenRollup } from "./tokens.mjs";
|
|
|
70
70
|
import { recordRunConsumption } from "./consumption-ledger.mjs";
|
|
71
71
|
import { writeSettleStamp } from "./settle-stamp.mjs"; // — the pool copy's own terminal state
|
|
72
72
|
import { stopReason } from "../shared/stop-reason.mjs"; //
|
|
73
|
-
import { envFrom } from "../shared/env-aliases.mjs"; // — resolves EITHER spelling; names the retired one because that is the live-writable half
|
|
74
73
|
// The scoped owner lookup a promoted register filing is owed. Bounded, deduplicated
|
|
75
74
|
// per owner, and structurally unable to withhold a report.
|
|
76
75
|
import { ownersOwedACheck, runOwnerChecks } from "./owner-use-check.mjs";
|
|
@@ -994,13 +993,16 @@ export async function knockoutInner(ctx, job, opts = {}) {
|
|
|
994
993
|
const rows = readFileSync(K.sweepLedger, "utf8").split("\n").filter(Boolean).map((l) => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
995
994
|
const cells = rows.reduce((n, r) => n + (Number.isFinite(r?.cells) ? r.cells : 0), 0);
|
|
996
995
|
const places = rows.reduce((n, r) => Math.max(n, Array.isArray(r?.places) ? r.places.length : 0), 0);
|
|
997
|
-
|
|
996
|
+
// THE SWEEP'S OWN ELAPSED TIME, UNDER A NAME THAT SAYS SO, and the bar is no longer asserted here.
|
|
997
|
+
// The bar is about the RUN (ruling 591) and this span is one stage of it: both knockouts of
|
|
998
|
+
// 2026-09-26 recorded `overBar: false` at 0.6 and 0.8 minutes while the runs took 14.4 minutes
|
|
999
|
+
// wall, which e2e scored as over. A bar recorded against the wrong quantity is worse than an
|
|
1000
|
+
// unrecorded one, because the field's name says it answered the question. It is recorded at
|
|
1001
|
+
// delivery instead, where the run's own start is readable.
|
|
1002
|
+
const sweepMinutes = Number(((Date.now() - sweepStartedAt) / 60000).toFixed(1));
|
|
998
1003
|
runLog(run.runDir, { event: "knockout-sweep-total", marks: planMarks.length, calls: rows.length,
|
|
999
|
-
cells, mostPlacesForOneName: places,
|
|
1000
|
-
|
|
1001
|
-
// rather than a judgment about the build. It changes nothing this run did.
|
|
1002
|
-
overBar: minutes > KNOCKOUT_MINUTES_BAR });
|
|
1003
|
-
note(`knockout sweep: ${cells} cell(s) across ${rows.length} call(s) in ${minutes} min (the bar is ${KNOCKOUT_MINUTES_BAR})`);
|
|
1004
|
+
cells, mostPlacesForOneName: places, sweepMinutes });
|
|
1005
|
+
note(`knockout sweep: ${cells} cell(s) across ${rows.length} call(s) in ${sweepMinutes} min`);
|
|
1004
1006
|
} catch { /* the receipts are the record; a summary that could not be read never fails a sweep */ }
|
|
1005
1007
|
}
|
|
1006
1008
|
|
|
@@ -1153,7 +1155,18 @@ export async function knockoutInner(ctx, job, opts = {}) {
|
|
|
1153
1155
|
const published = await publishKnockout({
|
|
1154
1156
|
runId, codename: run.codename, runDir: run.runDir,
|
|
1155
1157
|
findings: merged, plan, framework: ctx.framework, overall,
|
|
1156
|
-
poolRoot: config.poolRoot,
|
|
1158
|
+
poolRoot: config.poolRoot,
|
|
1159
|
+
// THE FALLBACK HERE COULD NOT FIRE, AND COULD NOT HAVE HELPED IF IT HAD. It read
|
|
1160
|
+
// `config.poolUrl ?? envFrom(process.env, "CLEAROTRON_REPORTS_URL") ?? null`. Two things were wrong
|
|
1161
|
+
// with it and both were invisible: the getter returns "" when the variable is unset, and `??` only
|
|
1162
|
+
// falls through on null or undefined — so the second operand was unreachable — and that second
|
|
1163
|
+
// operand read the same `process.env` the getter had just read, so reaching it would have returned
|
|
1164
|
+
// the same empty answer. A reader saw a safety net twice over where there was none.
|
|
1165
|
+
//
|
|
1166
|
+
// `|| null` instead, which says the one true thing: no base URL configured, so no link. That is the
|
|
1167
|
+
// state `reportUrlFor` already handles by returning null per report rather than an empty string, and
|
|
1168
|
+
// a null link is what the delivery packet is specified to carry when no pool URL is set.
|
|
1169
|
+
poolUrl: config.poolUrl || null,
|
|
1157
1170
|
customerKey: ctx.profile?.profileKey ?? "generic",
|
|
1158
1171
|
// — the frozen profile's delivery overlay decides the confidentiality marking, exactly as it
|
|
1159
1172
|
// does on the clearance lane. Absent (an unbound run, or a profile silent on the field) is the
|
|
@@ -1216,6 +1229,24 @@ export async function knockoutInner(ctx, job, opts = {}) {
|
|
|
1216
1229
|
writeFileSync(join(config.outboxDir, `${packet.runId}.pending`), `${agent}\n`);
|
|
1217
1230
|
} catch (e) { note(`delivery: outbox marker write skipped (${String(e.message).slice(0, 100)})`); }
|
|
1218
1231
|
const deliveredAt = new Date().toISOString();
|
|
1232
|
+
// ── THE TEN-MINUTE BAR, AGAINST THE RUN (ruling 591) ────────────────────────────────────────────
|
|
1233
|
+
//
|
|
1234
|
+
// Recorded here because this is the first point at which the run's own span is known: `startedAt` is
|
|
1235
|
+
// in the run's status and `deliveredAt` is the line above. It used to be recorded at the web sweep,
|
|
1236
|
+
// against the sweep's own elapsed time, so both knockouts of 2026-09-26 stated `overBar: false` at
|
|
1237
|
+
// 0.6 and 0.8 minutes while their runs took 14.4 minutes wall — the number beside the bar was not the
|
|
1238
|
+
// number the bar is about.
|
|
1239
|
+
//
|
|
1240
|
+
// RECORDED, NEVER APPLIED, exactly as before: nothing reads `overBar` and nothing about this run
|
|
1241
|
+
// changes on it.
|
|
1242
|
+
//
|
|
1243
|
+
// AND AN UNREADABLE START SAYS SO RATHER THAN VANISHING. `readRunStatus` answers `{}` when it cannot
|
|
1244
|
+
// read, and a bar quietly omitted because a file was unreadable is an absence that reads as "not
|
|
1245
|
+
// over". So the event always carries the field, and names the reason when it has no span.
|
|
1246
|
+
try {
|
|
1247
|
+
const verdict = knockoutBarVerdict({ startedAt: readRunStatus(run.runDir)?.startedAt, deliveredAt });
|
|
1248
|
+
runLog(run.runDir, { event: "knockout-run-total", barMinutes: KNOCKOUT_MINUTES_BAR, ...verdict });
|
|
1249
|
+
} catch { /* the bar is a record, never a gate: a summary that could not be written never fails a delivery */ }
|
|
1219
1250
|
// `tier` beside `verdict` — the same band word under the name the clearance lane records it by, so a
|
|
1220
1251
|
// reader of either lane's status finds the rating in one place. `verdict` stays as it was.
|
|
1221
1252
|
writeRunStatus(ctx, { state: "delivered", tier: overall, statement: published.statement, url: published.url, reports: published.reports.map((r) => ({ mark: r.mark, url: r.url })), deliveredAt, sendPending: true, stepIndex: STEPS.length - 1, stepLabel: STEPS[STEPS.length - 1], stepN: STEPS.length, stepTotal: STEPS.length });
|