clearotron 0.3.1-beta.0 → 0.3.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +8 -0
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +13 -6
- package/driver/pipeline.mjs +21 -7
- package/driver/profile-page.html +11 -3
- package/driver/suite-census.json +8 -2
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-CwPAS0we.js → index-DLa7PlKY.js} +20 -1
- package/portal-ui/dist/index.html +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
package/build-info.json
CHANGED
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.1-beta.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
- 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.
|
|
9
|
+
- c3228f3: Fixed: On older Windows-Subsystem installations the engine now identifies the platform by its interop registration rather than by the kernel version string.
|
|
10
|
+
|
|
3
11
|
## 0.3.1-beta.0
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
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.3.1-beta.
|
|
5
|
+
"version": "0.3.1-beta.1",
|
|
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": {
|
|
@@ -1146,7 +1146,19 @@ export async function knockoutInner(ctx, job, opts = {}) {
|
|
|
1146
1146
|
// because mark_sent is the only clear and it settles a failure.json on the same evidence a delivery
|
|
1147
1147
|
// needs. The marker is NOT duplicated — the primary lane's own packet lands `<runId>.failed.pending`
|
|
1148
1148
|
// and the *.pending watch matches it; a second `<runId>.pending` would outlive every settle.
|
|
1149
|
+
// THE RECORD OF AN OBLIGATION MUST NOT BE CONTINGENT ON THE ACTION IT OBLIGES — clearance parity,
|
|
1150
|
+
// and the same defect this lane would have had. `writeOutboxPacket` returns null rather than throwing,
|
|
1151
|
+
// so an unwritable outbox reaches the raw marker write below; with the flag after it, that throw took
|
|
1152
|
+
// the record of the obligation with it and the run ended failed and NOT owed. Guards first, then the
|
|
1153
|
+
// flag, then the writes that can fail.
|
|
1149
1154
|
let failPingSent = false;
|
|
1155
|
+
try { rmSync(join(run.runDir, ".sent")); } catch { /* none */ }
|
|
1156
|
+
try { rmSync(driverDir(run.runDir, "send-receipts.json"), { force: true }); } catch { /* none */ }
|
|
1157
|
+
// a fresh notice supersedes an older send's skip-guards (a resumed-then-failed-again run must
|
|
1158
|
+
// still notify — the .sent/receipts invariant, clearance parity)
|
|
1159
|
+
try { writeRunStatus(ctx, { sendPending: true }); } catch (sErr) {
|
|
1160
|
+
note(`knockout failure notice could NOT be recorded as owed (${String(sErr?.message ?? sErr).slice(0, 100)}) — the run directory is unwritable`);
|
|
1161
|
+
}
|
|
1150
1162
|
try {
|
|
1151
1163
|
const rich = buildFailurePacket({
|
|
1152
1164
|
runId, agent, job, failedStage, shortReason, terminalKind,
|
|
@@ -1163,12 +1175,7 @@ export async function knockoutInner(ctx, job, opts = {}) {
|
|
|
1163
1175
|
mkdirSync(config.outboxDir, { recursive: true });
|
|
1164
1176
|
writeFileSync(join(config.outboxDir, `${runId}.pending`), `${agent}\n`);
|
|
1165
1177
|
}
|
|
1166
|
-
|
|
1167
|
-
// still notify — the .sent/receipts invariant, clearance parity)
|
|
1168
|
-
try { rmSync(join(run.runDir, ".sent")); } catch { /* none */ }
|
|
1169
|
-
try { rmSync(driverDir(run.runDir, "send-receipts.json"), { force: true }); } catch { /* none */ }
|
|
1170
|
-
writeRunStatus(ctx, { sendPending: true });
|
|
1171
|
-
} catch (nfErr) { note(`knockout failure-notice write skipped (${String(nfErr?.message ?? nfErr).slice(0, 100)})`); }
|
|
1178
|
+
} catch (nfErr) { note(`knockout failure notice is owed but its packet/marker could not be written (${String(nfErr?.message ?? nfErr).slice(0, 100)}) — status.json carries the obligation`); }
|
|
1172
1179
|
note(`=== KNOCKOUT ${terminalKind === REFUSAL_TERMINAL_KIND ? "REFUSED" : "FAILED"} ${run.codename} at ${failedStage}: ${shortReason} ===\n`);
|
|
1173
1180
|
// `codename`: this lane's terminals reach the SAME CLI exit as the clearance lane's, and the
|
|
1174
1181
|
// CLI composes its resume line from the returned identity. Without it a knockout operator is the only
|
package/driver/pipeline.mjs
CHANGED
|
@@ -15374,6 +15374,22 @@ async function pipelineInner(job, opts = {}) {
|
|
|
15374
15374
|
// for each id the run was known by, not this suffixed form — and the sweep re-arms off whatever is
|
|
15375
15375
|
// on disk, which is how a permanent SEND PENDING was born. So: the flag always, the packet and the
|
|
15376
15376
|
// marker only when nothing wrote them.
|
|
15377
|
+
// THE RECORD OF AN OBLIGATION MUST NOT BE CONTINGENT ON THE ACTION IT OBLIGES. The flag says
|
|
15378
|
+
// somebody is owed a notice. Setting it downstream of the attempt to deliver that notice means it is
|
|
15379
|
+
// only set when the delivery path already worked — the case that needed it least. This block did
|
|
15380
|
+
// exactly that for one revision: `writeOutboxPacket` swallows its own failure and returns null, so
|
|
15381
|
+
// an unwritable outbox fell to the branch below, where the RAW marker write threw and took the flag
|
|
15382
|
+
// with it. The run then ended failed and NOT owed, in the one condition where the flag is the only
|
|
15383
|
+
// thing that could still record the obligation. Order, therefore: supersede the old send's guards,
|
|
15384
|
+
// arm the flag, and only then attempt the writes that can fail.
|
|
15385
|
+
try { rmSync(join(run.runDir, ".sent")); } catch { /* none */ }
|
|
15386
|
+
try { rmSync(driverDir(run.runDir, "send-receipts.json"), { force: true }); } catch { /* none */ }
|
|
15387
|
+
// A fresh notice supersedes an older send's .sent marker AND its per-channel receipts (a resumed run
|
|
15388
|
+
// that fails again must still notify on every channel, not be skip-guarded by the earlier send's).
|
|
15389
|
+
// Both are best-effort removals above, so neither can keep the flag below from being written.
|
|
15390
|
+
try { writeRunStatus(ctx, { sendPending: true }); } catch (sErr) {
|
|
15391
|
+
note(`failure notice could NOT be recorded as owed (${String(sErr?.message ?? sErr).slice(0, 100)}) — the run directory is unwritable, so nothing here can say a notice is outstanding`);
|
|
15392
|
+
}
|
|
15377
15393
|
try {
|
|
15378
15394
|
if (!failPingSent) {
|
|
15379
15395
|
const packet = buildFailurePacket({
|
|
@@ -15386,15 +15402,13 @@ async function pipelineInner(job, opts = {}) {
|
|
|
15386
15402
|
mkdirSync(config.outboxDir, { recursive: true });
|
|
15387
15403
|
writeFileSync(join(config.outboxDir, `${packet.runId}.pending`), `${agent}\n`);
|
|
15388
15404
|
}
|
|
15389
|
-
// same per-send invariant as the delivery handoff: a fresh notice supersedes an older send's
|
|
15390
|
-
// .sent marker AND its per-channel receipts (e.g. a resumed run that fails again must still
|
|
15391
|
-
// notify on every channel, not be skip-guarded by the earlier send's receipts).
|
|
15392
|
-
try { rmSync(join(run.runDir, ".sent")); } catch { /* none */ }
|
|
15393
|
-
try { rmSync(driverDir(run.runDir, "send-receipts.json"), { force: true }); } catch { /* none */ }
|
|
15394
|
-
writeRunStatus(ctx, { sendPending: true });
|
|
15395
15405
|
note(`failure notice OWED (${failPingSent ? "event lane wrote the packet" : "handoff wrote the packet"}) — mark_sent is the only clear`);
|
|
15396
15406
|
} catch (nfErr) {
|
|
15397
|
-
|
|
15407
|
+
// NOT "status.json remains the record" — that sentence was here and it was false on exactly the
|
|
15408
|
+
// path that reaches this catch, because the flag was written after the throw. It is written before
|
|
15409
|
+
// it now, so the record genuinely does stand, and saying which part failed is what keeps the next
|
|
15410
|
+
// reader looking in the right place.
|
|
15411
|
+
note(`failure notice is owed but its packet/marker could not be written (${String(nfErr?.message ?? nfErr).slice(0, 100)}) — status.json carries the obligation; the outbox does not`);
|
|
15398
15412
|
}
|
|
15399
15413
|
note(`=== FAILED ${run.codename} at ${failedStage}: ${reason} ===\n`);
|
|
15400
15414
|
// `codename` matches the three sibling terminals (postpone, recovery park, cancelled). Without it
|
package/driver/profile-page.html
CHANGED
|
@@ -223,7 +223,7 @@ function renderEditor(key, p, contextPack, derived, framework, isNew) {
|
|
|
223
223
|
<div class="row2">
|
|
224
224
|
${f("Default trademark classes", "f_classes", (p.defaultClasses||[]).join(", "), "Comma-separated Nice class numbers (e.g. 9, 41). Used only when a request doesn't list its own classes.")}
|
|
225
225
|
</div>
|
|
226
|
-
${f("Default
|
|
226
|
+
${f("Default jurisdictions", "f_juris", (p.defaultJurisdictions||[]).join(", "), "The territories that matter to this customer, comma-separated: a country name like France, or a two-letter code the engine holds such as US, GB or EU. A request can override or add to these.")}
|
|
227
227
|
${f("Email domains", "f_match", (p.matchDomains||[]).join(", "), "Email domains that automatically map a request to this customer (e.g. acme.com). Usually left empty — intake normally tags the customer directly.", undefined, true)}
|
|
228
228
|
${ta("The customer's own names", "f_excl", (p.selfExclusionOwners||[]).join("\n"), "The customer's own and affiliated company names, one per line. We never flag the customer against its own marks.", undefined, true)}
|
|
229
229
|
<div class="sectionh">Default search</div>
|
|
@@ -353,8 +353,16 @@ function prettyServerError(msg) {
|
|
|
353
353
|
[/must be CONTEXT[\s\S]*not a decision rule/i, "f_pack", "Background must be facts and concerns, not rules — rephrase any rule as a concern or a question."],
|
|
354
354
|
[/exceeds the \d+-char budget/i, "f_pack", "Background is too long — keep it under 8000 characters."],
|
|
355
355
|
[/delivery\.email must be/i, "f_email", "Pick a cover email format from the list."],
|
|
356
|
+
// THE TERRITORY REFUSAL NAMES THE OFFENDING ENTRIES ITSELF, so this maps it to the field and keeps
|
|
357
|
+
// the server's sentence rather than replacing it — the other rows replace jargon, and this one is
|
|
358
|
+
// already the plainer of the two. Unmapped, it landed in the headline list with no field marked,
|
|
359
|
+
// which is the half that tells a reader WHERE.
|
|
360
|
+
[/(is not a territory|are not territories) the engine can search/i, "f_juris",
|
|
361
|
+
(m) => String(m).replace(/^profiles\/[^:]+:\s*/, "")],
|
|
356
362
|
];
|
|
357
|
-
|
|
363
|
+
// `friendly` may be a FUNCTION where the server's own wording is better than a replacement — it
|
|
364
|
+
// names the offending entries, which a fixed sentence cannot.
|
|
365
|
+
for (const [re, id, friendly] of MAP) if (re.test(M)) return { id, friendly: typeof friendly === "function" ? friendly(M) : friendly };
|
|
358
366
|
return { id:null, friendly:M };
|
|
359
367
|
}
|
|
360
368
|
function showServerErrors(headline, errors) {
|
|
@@ -490,7 +498,7 @@ function renderProjectEditor(customer, project, data, isNew) {
|
|
|
490
498
|
<div class="row2">
|
|
491
499
|
${f("Default classes", "p_classes", (ov.defaultClasses||[]).join(", "), inheritHint((inh.defaultClasses||[]).join(", ")||"—"))}
|
|
492
500
|
</div>
|
|
493
|
-
${f("Default
|
|
501
|
+
${f("Default jurisdictions", "p_juris", (ov.defaultJurisdictions||[]).join(", "), inheritHint((inh.defaultJurisdictions||[]).join(", ")||"—"))}
|
|
494
502
|
<div class="field"><label for="p_email">Cover email format</label><div class="hint">Inherit, or set just for this project.</div>
|
|
495
503
|
<select id="p_email"><option value=""${!d?" selected":""}>Inherit</option><option value="summary"${d&&d.email!=="table"?" selected":""}>Summary</option><option value="table"${d&&d.email==="table"?" selected":""}>Table</option></select></div>
|
|
496
504
|
<div class="field"><label for="p_priv">Confidentiality marking</label><div class="hint">Only applies when a format is set above.</div><select id="p_priv"><option value=""${d&&d.privileged===false?"":" selected"}>Privileged & Confidential</option><option value="no"${d&&d.privileged===false?" selected":""}>No marking</option></select></div>
|
package/driver/suite-census.json
CHANGED
|
@@ -3904,8 +3904,8 @@
|
|
|
3904
3904
|
"todos": 0
|
|
3905
3905
|
},
|
|
3906
3906
|
"runner.failure-backstop.test.mjs": {
|
|
3907
|
-
"tests":
|
|
3908
|
-
"asserts":
|
|
3907
|
+
"tests": 4,
|
|
3908
|
+
"asserts": 29,
|
|
3909
3909
|
"skips": 0,
|
|
3910
3910
|
"todos": 0
|
|
3911
3911
|
},
|
|
@@ -5737,6 +5737,12 @@
|
|
|
5737
5737
|
"skips": 0,
|
|
5738
5738
|
"todos": 0
|
|
5739
5739
|
},
|
|
5740
|
+
"checkAndSaveCannotDisagree.test.ts": {
|
|
5741
|
+
"tests": 2,
|
|
5742
|
+
"asserts": 6,
|
|
5743
|
+
"skips": 0,
|
|
5744
|
+
"todos": 0
|
|
5745
|
+
},
|
|
5740
5746
|
"clearancesKeepsItsControls.test.ts": {
|
|
5741
5747
|
"tests": 4,
|
|
5742
5748
|
"asserts": 8,
|
package/mcp-server/CHANGELOG.md
CHANGED
package/mcp-server/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trademark-artifacts-mcp",
|
|
3
|
-
"version": "0.3.1-beta.
|
|
3
|
+
"version": "0.3.1-beta.1",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"private": true,
|
|
6
6
|
"description": "MCP server to interrogate clearotron trademark-clearance runs — list/read artifacts, trace the full decision flow, telemetry/cost, coverage, single-run search, and a gated single-step what-if. Imports the clearotron-driver read-only; touches no driver/template/deploy files.",
|
package/package.json
CHANGED
|
@@ -18670,7 +18670,7 @@ var PROFILE_FIELDS = [
|
|
|
18670
18670
|
item: {
|
|
18671
18671
|
ok: isTerritoryEntry,
|
|
18672
18672
|
strict: true,
|
|
18673
|
-
expected: "a territory from the picker below, or a two-letter code"
|
|
18673
|
+
expected: "a territory from the picker below, or a two-letter country code the engine holds, like US, GB or EU"
|
|
18674
18674
|
},
|
|
18675
18675
|
hint: "One per line or comma-separated — pick from the list below, or type a two-letter code like US, EU or GB; both are understood. A region counts as one entry: European Union covers its member states, so there is no need to add them. Anything the engine cannot search is refused rather than stored, because a stored one is a default that quietly does nothing."
|
|
18676
18676
|
},
|
|
@@ -20572,6 +20572,15 @@ function Profile({ ctx }) {
|
|
|
20572
20572
|
return;
|
|
20573
20573
|
}
|
|
20574
20574
|
if (action === "validate") {
|
|
20575
|
+
const errors = Array.isArray(r.value["errors"]) ? r.value["errors"] : [];
|
|
20576
|
+
if (r.value["ok"] === false || errors.length) {
|
|
20577
|
+
setChecked(false);
|
|
20578
|
+
setProblem({
|
|
20579
|
+
title: errors.length === 1 ? "1 thing to fix before saving:" : `${errors.length} things to fix before saving:`,
|
|
20580
|
+
lines: errors.length ? errors : ["This company’s settings were refused, and no reason came back."]
|
|
20581
|
+
});
|
|
20582
|
+
return;
|
|
20583
|
+
}
|
|
20575
20584
|
setChecked(true);
|
|
20576
20585
|
return;
|
|
20577
20586
|
}
|
|
@@ -21588,6 +21597,16 @@ function ProjectEditor({ account, project, mayChange, onBack }) {
|
|
|
21588
21597
|
return;
|
|
21589
21598
|
}
|
|
21590
21599
|
if (action === "validate") {
|
|
21600
|
+
const dryRun = "value" in r && r.value && typeof r.value === "object" ? r.value : {};
|
|
21601
|
+
const errors = Array.isArray(dryRun["errors"]) ? dryRun["errors"] : [];
|
|
21602
|
+
if (dryRun["ok"] === false || errors.length) {
|
|
21603
|
+
setChecked(false);
|
|
21604
|
+
setProblem({
|
|
21605
|
+
title: "That cannot be saved as written",
|
|
21606
|
+
lines: errors.length ? errors : ["This project was refused, and no reason came back."]
|
|
21607
|
+
});
|
|
21608
|
+
return;
|
|
21609
|
+
}
|
|
21591
21610
|
setChecked(true);
|
|
21592
21611
|
return;
|
|
21593
21612
|
}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
-->
|
|
50
50
|
<link rel="preconnect" href="https://api.fontshare.com" crossorigin />
|
|
51
51
|
<link href="https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700,900&display=swap" rel="stylesheet" />
|
|
52
|
-
<script type="module" crossorigin src="/portal/assets/index-
|
|
52
|
+
<script type="module" crossorigin src="/portal/assets/index-DLa7PlKY.js"></script>
|
|
53
53
|
<link rel="stylesheet" crossorigin href="/portal/assets/index-Cv-E_agg.css">
|
|
54
54
|
</head>
|
|
55
55
|
<body>
|
package/portal-ui/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "portal-ui",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.1-beta.
|
|
5
|
+
"version": "0.3.1-beta.1",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"description": "The unified trademark portal UI. One address, one login: who you are decides what you see. Built as a static bundle, served by driver/portal-service.mjs — the browser never reaches profile-service or recipe-service.",
|
|
8
8
|
"engines": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trademark-oauth-mcp-bridge",
|
|
3
|
-
"version": "0.3.1-beta.
|
|
3
|
+
"version": "0.3.1-beta.1",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"private": true,
|
|
6
6
|
"description": "OAuth 2.1 MCP stdio bridge used by the engine's case-law gather stage (courtlistener / legaldatahunter).",
|