clearotron 0.3.1-beta.4 → 0.3.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 CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "770229d7131d7aee6f8fb32e2f90c02b44e08d2a",
3
- "version": "0.3.1-beta.4"
2
+ "commit": "6e95648a34fce4d8d0a73b9359a89925b0af3a36",
3
+ "version": "0.3.1"
4
4
  }
@@ -1,5 +1,80 @@
1
1
  # clearotron-driver
2
2
 
3
+ ## 0.3.1
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
+ - a2c4b94: Fixed: A correction to a coverage note or an action is now applied, or the run records why it was not.
9
+ - 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.
10
+ - b320c65: Fixed: A demo sample that cannot be read is named in the demo's output, and the other demos still publish.
11
+ - 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.
12
+
13
+ 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.
14
+ - d0d42f7: Fixed: A name in a knockout batch is no longer rated above every conflict found against it. Its rating now follows from the conflicts on its own page.
15
+
16
+ Fixed: Before, a rule forced any name made of everyday words off the lowest band, whatever the search found. That rule is gone for every client.
17
+ - f96c089: Fixed: Running the test suite from inside another test run no longer lets the outer run delete the inner run's temporary files.
18
+ - f96c089: Fixed: A clearance for a new client can be ordered through an assistant connector without setting up a company first.
19
+ - 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.
20
+ - 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.
21
+ - 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.
22
+ - 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.
23
+ - d9798db: Fixed: A clearance that stops now always records that its notice is still owed, so a failure cannot be passed over as already handled.
24
+ - 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.
25
+ - 56a760f: Fixed: Giving somebody access to everything on the installation now works from the access form. It used to refuse. The message it refused with said you can only give access to what you hold yourself, which was not true of the person seeing it.
26
+ - ae2cc82: For operators: An assistant asking which searches still owe someone a notice now gets all of them, not just the fifty most recent. Asking for recent searches is unchanged.
27
+ - 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.
28
+ - 8c3bc3b: New: Ask AI on a report now opens Claude or ChatGPT with a question about that report already typed in. One press, in a new tab, and nothing is sent until you send it.
29
+
30
+ The button used to hand over a connector address and a question carrying the run's internal code, with no indication of which one you needed. The address belongs on the Use your own AI page, where you set the connector up once. It is no longer shown on reports at all.
31
+
32
+ If you have not connected an assistant yet, the button explains that in a line and offers to take you there.
33
+
34
+ For operators: the report's own "Ask your AI" band is gone, so there is one Ask AI control rather than two. Reports rendered before this upgrade keep the band in their own file, and it is hidden when the portal serves them.
35
+ - 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.
36
+ - 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.
37
+ - 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.
38
+ - 240673c: Fixed: Setup and the framed first-run box now say what to do when the page that opens belongs to another program.
39
+
40
+ Fixed: The port that advice suggests is never the port already in use.
41
+ - 6d0c8f5: Fixed: The portal now tells your browser not to store the data its screens read. Those responses carry people's names, company access and run lists, and nothing previously said how long a browser could keep them.
42
+
43
+ For operators: every JSON response from the portal now sends `Cache-Control: no-store` and `Vary: Accept`. A route that sets a stricter policy of its own keeps it.
44
+ - f96c089: For operators: The repository's own comment-to-code references are now checked for having moved, not only for existing.
45
+ - c3228f3: Fixed: On older Windows-Subsystem installations the engine now identifies the platform by its interop registration rather than by the kernel version string.
46
+ - 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.
47
+ - 56a760f: Fixed: `clearotron grant remove --tenant` now refuses when the person has access to everything on the installation. It used to remove the organisation and then warn that nothing they could see had changed.
48
+
49
+ Fixed: Removing somebody whose address is spelled with different capitalisation in different parts of the access file now removes all of them. Half of the entry used to survive, and the command reported success.
50
+ - 56a760f: New: Access can now be narrowed and taken away, not only added to. Somebody who manages one organisation removes that organisation alone. Somebody who can see all of a person removes their access to the installation, and withdraws the keys their AI assistant was using.
51
+
52
+ Fixed: A removal now says plainly when the connector cannot be told about it yet, instead of implying the assistant lost access too.
53
+ - 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.
54
+ - e390417: For operators: The portal's start-up check now says when its engine address is behind a sign-in it cannot pass, instead of reporting that address as reachable.
55
+ - 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.
56
+
57
+ 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.
58
+ - 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.
59
+ - 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.
60
+
61
+ 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.
62
+ - 240673c: Fixed: An off-register search now also covers the channels the matter itself names, not only the account's usual marketplaces.
63
+
64
+ Fixed: A channel no pass ran is now recorded as open rather than described in a note.
65
+
66
+ Fixed: A finding reads what the platform's own record says before calling an owner unidentified.
67
+ - 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.
68
+ - f96c089: Fixed: The run purge no longer deletes a clearance whose report or failure notice has not been sent yet.
69
+
70
+ Fixed: Those runs are marked in the table the purge prints, and removing one now takes a flag that says so.
71
+
72
+ For operators: Every applied purge leaves a record of what it removed, when, and whether any of it was still owed.
73
+ - 1905ea4: Fixed: Saving a territory the engine cannot search now says so, instead of suggesting the kind of entry that was just refused.
74
+ - f1c5925: Fixed: Where a search covers two ratified forms of a name, the report now reasons each form and says which conflicts differ between them.
75
+
76
+ Fixed: Before, both forms were searched but one combined read came back. When the forms read alike the report now says so, rather than leaving it unsaid.
77
+
3
78
  ## 0.3.1-beta.4
4
79
 
5
80
  ### Patch Changes
@@ -2,7 +2,7 @@
2
2
  "name": "clearotron-driver",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "0.3.1-beta.4",
5
+ "version": "0.3.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": {
@@ -4593,6 +4593,12 @@
4593
4593
  "skips": 0,
4594
4594
  "todos": 0
4595
4595
  },
4596
+ "the-cut-approves-its-own-parked-run.test.mjs": {
4597
+ "tests": 4,
4598
+ "asserts": 10,
4599
+ "skips": 0,
4600
+ "todos": 0
4601
+ },
4596
4602
  "the-demo-account-shows-a-configured-customer.test.mjs": {
4597
4603
  "tests": 4,
4598
4604
  "asserts": 24,
@@ -1,5 +1,20 @@
1
1
  # trademark-artifacts-mcp
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f96c089: Fixed: A clearance for a new client can be ordered through an assistant connector without setting up a company first.
8
+ - 56a760f: Fixed: Removing somebody's access now ends their assistant's connection on its next request. Until now an assistant that had already connected kept the access it started with for up to half an hour.
9
+ - c05e0a7: Fixed: Ask your assistant for a client's recent searches by the client's name and it finds them. Every search in the list now says which client it was for, and which project. A search whose own record cannot be read says so, instead of appearing to belong to nobody.
10
+ - d9798db: Fixed: A clearance that stops now always records that its notice is still owed, so a failure cannot be passed over as already handled.
11
+ - 240673c: Fixed: Sending an access key to the engine's network address now gets a refusal that says so. It reports that the address takes an identity from the sign-in proxy and never a key.
12
+
13
+ Fixed: That refusal also names the local door where a key is accepted. Before, it reported only a missing sign-in assertion, which sent operators to the wrong configuration.
14
+
15
+ For operators: A program on the same machine can now reach the engine's local key door without setting a host name for it. The local door no longer applies a browser protection that only a network address needs.
16
+ - f96c089: New: The free preview of a search lists, for each territory ordered, which registers legally bind it.
17
+
3
18
  ## 0.3.1-beta.4
4
19
 
5
20
  No changes in this release.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trademark-artifacts-mcp",
3
- "version": "0.3.1-beta.4",
3
+ "version": "0.3.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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clearotron",
3
3
  "type": "module",
4
- "version": "0.3.1-beta.4",
4
+ "version": "0.3.1",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
7
7
  "type": "git",
@@ -2,7 +2,7 @@
2
2
  "name": "portal-ui",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "0.3.1-beta.4",
5
+ "version": "0.3.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,5 +1,9 @@
1
1
  # trademark-oauth-mcp-bridge
2
2
 
3
+ ## 0.3.1
4
+
5
+ No changes in this release.
6
+
3
7
  ## 0.3.1-beta.4
4
8
 
5
9
  No changes in this release.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trademark-oauth-mcp-bridge",
3
- "version": "0.3.1-beta.4",
3
+ "version": "0.3.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).",
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+ // Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
4
+ //
5
+ // release-approve-parked.mjs — approve the version pull request's parked CI run, so a cut does not wait
6
+ // on a person.
7
+ //
8
+ // WHY A PERSON HAS BEEN CLICKING. The version pull request is authored by the repository's own Actions
9
+ // bot, and a run triggered by a bot-authored pull request arrives `action_required`. Measured across the
10
+ // last forty `pull_request` CI runs: every parked one is on the version branch and no human-authored
11
+ // branch parks, so the thing that distinguishes them is the author, not a repository setting. The
12
+ // approval endpoint works on such a run — proved against a live parked run, which moved from
13
+ // `action_required` to `queued` — even though its documentation names fork pull requests only.
14
+ //
15
+ // AND WHY IT NEEDS A TOKEN RATHER THAN THE BUILT-IN ONE. `GITHUB_TOKEN` cannot approve a workflow run;
16
+ // GitHub blocks self-approval deliberately. So this reads a separate token with Actions read and write.
17
+ //
18
+ // WHAT THAT PERMISSION ACTUALLY BUYS, stated because the next person deciding whether to reuse this
19
+ // secret will read this sentence and not the permission page. Actions write is not "approve runs": it
20
+ // also creates workflow_dispatch events, cancels any run in the repository, and deletes run logs. This
21
+ // workflow carries `workflow_dispatch`, so the token can reach a PUBLISH by the same door a person
22
+ // uses. It cannot push code, open or merge a pull request, or authenticate to the registry — the
23
+ // publish credential is minted per run from this workflow's OIDC token and stored nowhere. So the
24
+ // boundary is real but it is not "it can only do what this script does".
25
+ //
26
+ // IT NEVER FAILS THE CUT. No token, no parked run, an API that refuses — each prints what happened and
27
+ // exits 0, because the cut's own wait still ends the way it always did: a person clicks, or the wait
28
+ // expires. A step that could turn a green cut red in order to save a click would be a worse trade than
29
+ // the click.
30
+ //
31
+ // BY SHA, AND ONLY THE CURRENT ONE. Approving a STALE parked run cancels the live one — CI's concurrency
32
+ // is workflow plus ref with cancel-in-progress on non-main refs — so this resolves the version branch's
33
+ // head at the moment it runs and approves only a run whose `head_sha` equals it.
34
+ //
35
+ // THE INTERVAL THAT MATTERS IS BETWEEN THE READ AND THE POST. Filtering the run list by head_sha and
36
+ // re-checking head_sha per row guards a case the API already guarantees; no row can disagree with the
37
+ // value it was queried by. The way this goes wrong is the HEAD going stale: the version step
38
+ // force-pushes that branch whenever it runs, so a push landing between the list and the approval leaves
39
+ // this approving the run on the superseded head — the exact act that cancels the live cut. The head is
40
+ // therefore re-read immediately before each approval, and a move aborts the whole pass rather than
41
+ // skipping one row, because if the branch moved then every id in the list is stale.
42
+ import { argv, env, exit } from "node:process";
43
+ import { pathToFileURL } from "node:url";
44
+
45
+ const REPO = env.GITHUB_REPOSITORY || "CordilleraSarl/clearotron";
46
+ const BRANCH = arg("--branch") || "changeset-release/main";
47
+ const TOKEN = env.ACTIONS_APPROVE_TOKEN || "";
48
+
49
+ function arg(name) { const i = argv.indexOf(name); return i === -1 ? null : argv[i + 1]; }
50
+ const say = (s) => process.stdout.write(`release-approve-parked: ${s}\n`);
51
+
52
+ async function api(path, init = {}) {
53
+ const r = await fetch(`https://api.github.com${path}`, {
54
+ ...init,
55
+ headers: {
56
+ accept: "application/vnd.github+json",
57
+ authorization: `Bearer ${TOKEN}`,
58
+ "x-github-api-version": "2022-11-28",
59
+ ...(init.headers ?? {}),
60
+ },
61
+ });
62
+ return r;
63
+ }
64
+
65
+ /**
66
+ * THE DECISION, SEPARATED FROM THE NETWORK so it can be driven against a table rather than matched as
67
+ * source text. Given the runs on a head and that head, which run ids should be approved?
68
+ *
69
+ * `event` AND `conclusion` both: the dispatched CI run on the same head is not the one the pull
70
+ * request's rollup reads, and approving it would do nothing while reading as success. PURE.
71
+ */
72
+ export function runsToApprove(runs, head) {
73
+ return (Array.isArray(runs) ? runs : [])
74
+ .filter((r) => r?.event === "pull_request" && r?.conclusion === "action_required" && r?.head_sha === head)
75
+ .map((r) => r.id);
76
+ }
77
+
78
+ // The run list is PAGED, and a silent truncation here reads as "nothing parked". 100 is the API maximum;
79
+ // the loop stops when a page comes back short, and says so if it ever hits the cap, because a cut that
80
+ // was not approved because the list was cut off should not look like a cut with nothing to approve.
81
+ async function allRunsOn(head) {
82
+ const out = [];
83
+ for (let page = 1; page <= 5; page++) {
84
+ const r = await api(`/repos/${REPO}/actions/runs?head_sha=${head}&per_page=100&page=${page}`);
85
+ if (!r.ok) return { ok: false, status: r.status, runs: out };
86
+ const batch = (await r.json())?.workflow_runs ?? [];
87
+ out.push(...batch);
88
+ if (batch.length < 100) return { ok: true, runs: out };
89
+ }
90
+ say(`more than 500 runs on this head — reading the first 500 only.`);
91
+ return { ok: true, runs: out };
92
+ }
93
+
94
+ // One reading of "where is the version branch now", used twice: once to choose, once immediately before
95
+ // the approval. Null means the question could not be answered, which is never treated as "unchanged".
96
+ async function branchHead() {
97
+ const br = await api(`/repos/${REPO}/branches/${encodeURIComponent(BRANCH)}`);
98
+ if (!br.ok) return null;
99
+ return (await br.json())?.commit?.sha ?? null;
100
+ }
101
+
102
+ async function main() {
103
+ // THE ABSENT SECRET IS THE ORDINARY CASE UNTIL THE TOKEN IS MINTED, and it must read as ordinary.
104
+ if (!TOKEN) {
105
+ say("no ACTIONS_APPROVE_TOKEN — nothing approved; the version run waits for a person, as before.");
106
+ return 0;
107
+ }
108
+
109
+ const head = await branchHead();
110
+ if (!head) { say(`could not read ${BRANCH} — nothing approved.`); return 0; }
111
+ say(`${BRANCH} is at ${head.slice(0, 7)}`);
112
+
113
+ const rr = await allRunsOn(head);
114
+ if (!rr.ok) { say(`could not list runs for ${head.slice(0, 7)} (${rr.status}) — nothing approved.`); return 0; }
115
+ const ids = runsToApprove(rr.runs, head);
116
+ if (!ids.length) {
117
+ const others = rr.runs.filter((r) => r?.conclusion === "action_required").length;
118
+ say(`no parked pull_request run on ${head.slice(0, 7)}${others ? ` (${others} parked on another event or head, left alone)` : ""} — nothing to approve.`);
119
+ return 0;
120
+ }
121
+
122
+ for (const id of ids) {
123
+ // ── THE INTERVAL THAT MATTERS IS BETWEEN THE READ AND THE POST, NOT INSIDE THE LIST ──────────────
124
+ //
125
+ // Filtering the list by head_sha and then re-checking head_sha on each row guards a case the API
126
+ // already guarantees: no row can disagree with the value it was queried by. What can actually go
127
+ // wrong is `head` itself going stale — the version step force-pushes this branch whenever it runs,
128
+ // so a push landing between the list and this POST leaves us approving the run on the SUPERSEDED
129
+ // head. That is precisely the act that cancels the live cut, because CI's concurrency is workflow
130
+ // plus ref with cancel-in-progress on non-main refs.
131
+ //
132
+ // So the head is re-read immediately before each approval, and a move aborts rather than skips: if
133
+ // the branch has moved, every id in this list is stale, not just this one.
134
+ const now = await branchHead();
135
+ if (now == null) { say(`could not re-read ${BRANCH} before approving — nothing approved.`); return 0; }
136
+ if (now !== head) {
137
+ say(`${BRANCH} moved ${head.slice(0, 7)} -> ${now.slice(0, 7)} since the run list was taken — ` +
138
+ `NOTHING APPROVED. Approving a run on the superseded head would cancel the live one; the cut ` +
139
+ `waits for a person, or for the next dispatch.`);
140
+ return 0;
141
+ }
142
+ const a = await api(`/repos/${REPO}/actions/runs/${id}/approve`, { method: "POST" });
143
+ say(a.ok ? `approved run ${id} on ${head.slice(0, 7)}.` : `run ${id} refused approval (${a.status}) — the cut still waits for a person.`);
144
+ }
145
+ return 0;
146
+ }
147
+
148
+ // RUN WHEN INVOKED, whatever the file is called. The old guard compared argv[1] to this file's NAME, so
149
+ // a rename made the step print nothing and exit 0 — a script that never ran, wearing the face of a
150
+ // successful no-op. Comparing the resolved URLs asks the real question instead.
151
+ if (import.meta.url === pathToFileURL(argv[1] ?? "").href) main().then((c) => exit(c)).catch((e) => { say(`could not run (${e?.message ?? e}) — nothing approved.`); exit(0); });