clearotron 0.4.0 → 0.4.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/THIRD-PARTY-NOTICES.md +2 -2
- package/bin/README.md +1 -1
- package/bin/clearotron.mjs +2 -0
- package/bin/mcp.mjs +80 -0
- package/bin/start.mjs +14 -1
- package/build-info.json +2 -2
- package/demo/README.md +1 -1
- package/docs/SECURITY-OWASP.md +21 -13
- package/docs/SECURITY.md +20 -14
- package/docs/architecture/04-configuration-reference.md +3 -3
- package/docs/architecture/05-config-governance.md +1 -1
- package/docs/architecture/06-operations-runbook.md +5 -4
- package/docs/architecture/08-development-guide.md +3 -1
- package/docs/architecture/09-security-and-data.md +16 -11
- package/driver/CHANGELOG.md +29 -0
- package/driver/binding-layers.mjs +28 -6
- package/driver/citation-census.json +3 -3
- package/driver/common-law-coverage-status.mjs +52 -0
- package/driver/common-law-receipts.mjs +1 -1
- package/driver/compare.mjs +1 -1
- package/driver/connotation-search.mjs +5 -5
- package/driver/contract-audit.mjs +6 -6
- package/driver/contract-dictation-registry.mjs +1 -1
- package/driver/contract-e3-backlog.mjs +18 -17
- package/driver/contract-vocabulary.mjs +145 -132
- package/driver/declination-call.mjs +5 -1
- package/driver/degraded-parts.mjs +10 -3
- package/driver/dev-portal.mjs +2 -1
- package/driver/dispatch-record.mjs +19 -0
- package/driver/doubt-closure-tool.mjs +2 -2
- package/driver/driver.config.mjs +39 -6
- package/driver/engagement-receipt.mjs +1 -1
- package/driver/engine/CONTRACT.md +41 -1
- package/driver/engine/anthropic-agent.mjs +56 -4
- package/driver/engine/mcp/codex-config.mjs +8 -3
- package/driver/engine/mcp/coverage-server.mjs +1 -1
- package/driver/engine/mcp/gather-config.mjs +1 -1
- package/driver/engine/mcp/recording-server.mjs +32 -5
- package/driver/engine/mcp/stdio-server.mjs +45 -7
- package/driver/engine/mcp/supplemental.mjs +26 -1
- package/driver/engine/openai-agent.mjs +113 -20
- package/driver/engine/session-record.mjs +223 -0
- package/driver/enqueue-schema.mjs +2 -2
- package/driver/findings-model.mjs +4 -4
- package/driver/flag-snapshot.mjs +2 -1
- package/driver/form-neighbourhood.mjs +7 -7
- package/driver/frame-diff-model.mjs +3 -3
- package/driver/framework.mjs +22 -0
- package/driver/gateway.mjs +96 -14
- package/driver/jx.mjs +1 -1
- package/driver/knockout-review-record.mjs +55 -1
- package/driver/named-band.mjs +4 -0
- package/driver/owner-screen.mjs +7 -4
- package/driver/package.json +1 -1
- package/driver/phase0.mjs +13 -2
- package/driver/pipeline-knockout.mjs +15 -13
- package/driver/pipeline.mjs +132 -48
- package/driver/portal-families.mjs +3 -3
- package/driver/portal-service.mjs +6 -5
- package/driver/predelivery-lint.mjs +19 -19
- package/driver/products.mjs +1 -1
- package/driver/profiles.mjs +4 -3
- package/driver/publish/index.mjs +1 -1
- package/driver/publish/knockout.mjs +5 -4
- package/driver/publish/office-record-links.mjs +17 -2
- package/driver/publish/publish-inputs.mjs +3 -3
- package/driver/publish/render-knockout.mjs +17 -5
- package/driver/publish/render.mjs +4 -2
- package/driver/publish/xlsx.mjs +1 -1
- package/driver/record-fold.mjs +177 -0
- package/driver/register-count.mjs +20 -3
- package/driver/register-coverage.mjs +1 -1
- package/driver/register-plan.mjs +83 -18
- package/driver/register-records.mjs +7 -0
- package/driver/registry-fidelity.mjs +2 -2
- package/driver/repairs.mjs +1 -1
- package/driver/report-card-record.mjs +4 -4
- package/driver/result-noun-fields.mjs +3 -3
- package/driver/scope-facts.mjs +11 -3
- package/driver/scope-rules.mjs +2 -2
- package/driver/search-policy.mjs +20 -1
- package/driver/skills/clearance-common-law/SKILL.md +22 -22
- package/driver/skills/clearance-register/digest.md +3 -3
- package/driver/skills/clearance-register/register-recipes.md +11 -11
- package/driver/skills/clearance-register/status-rules.md +1 -1
- package/driver/skills/clearance-register/unit.md +1 -1
- package/driver/skills/clearance-search/firm-wide-reasoning.md +3 -2
- package/driver/skills/clearance-search/phase2-execution.md +6 -5
- package/driver/skills/clearance-search/synthesis-rules.md +4 -4
- package/driver/skills/clearance-search/worked-examples.md +39 -39
- package/driver/skills/clearance-variants/SKILL.md +16 -16
- package/driver/skills/frame-diff/SKILL.md +2 -2
- package/driver/stage-context.mjs +36 -1
- package/driver/stages-knockout.mjs +6 -5
- package/driver/stages.mjs +30 -30
- package/driver/suite-census.json +259 -55
- package/driver/synthesis-record.mjs +5 -0
- package/driver/unit-inventory.mjs +17 -28
- package/driver/verify.mjs +7 -7
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/CONNECT.md +9 -3
- package/mcp-server/demo-mode.mjs +10 -0
- package/mcp-server/lib/evidence.mjs +8 -8
- package/mcp-server/lib/knockout.mjs +1 -1
- package/mcp-server/lib/runs.mjs +7 -1
- package/mcp-server/lib/scrub.mjs +1 -1
- package/mcp-server/package.json +2 -2
- package/mcp-server/packs/README.md +1 -1
- package/mcp-server/server.mjs +16 -2
- package/package.json +4 -3
- package/portal-ui/package.json +3 -3
- package/providers/_shared/count.mjs +9 -2
- package/providers/_shared/enumerate.mjs +125 -41
- package/providers/_shared/execute-plan.mjs +15 -4
- package/providers/corsearch/src/capabilities.js +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +2 -2
- package/providers/signa/src/capabilities.js +32 -2
- package/providers/signa/src/core.js +262 -36
- package/scripts/band-shape-probe.mjs +2 -2
- package/scripts/deprecate-below.mjs +28 -4
- package/scripts/e2e-first-time.mjs +17 -2
- package/scripts/env-audit.mjs +3 -2
- package/scripts/freeze-example-run.mjs +10 -10
- package/scripts/mcp-registry-listing.mjs +199 -0
- package/scripts/mint-plugin-folder.mjs +65 -0
- package/scripts/release-version.mjs +44 -0
- package/scripts/score.mjs +1 -1
- package/shared/register-floor.mjs +19 -0
- package/shared/scope.mjs +18 -2
- package/shared/trigger-lane.mjs +1 -1
- package/skills/clearotron-ops/SKILL.md +81 -24
|
@@ -1193,8 +1193,8 @@ export function findRegistryArithmeticIssues(text) {
|
|
|
1193
1193
|
// most national registers actually do — is TEN YEARS FROM FILING, with registration landing
|
|
1194
1194
|
// somewhere in between. Two live records from the 2026-07-28 worldwide run, both correct and both
|
|
1195
1195
|
// failed by the old rule:
|
|
1196
|
-
// ZA
|
|
1197
|
-
// PA
|
|
1196
|
+
// ZA FROSTY GRIP filed 2023-07-12 registered 2025-04-23 expires 2033-07-12 (filing +10)
|
|
1197
|
+
// PA WAVA filed 2023-10-19 registered 2025-04-16 expires 2033-10-19 (filing +10)
|
|
1198
1198
|
// It blocked delivery of a finished report — "expiry 2033 is not a 10-year cycle from claimed
|
|
1199
1199
|
// registration 2025" — over data that was right. So the cycle may anchor on EITHER year when both
|
|
1200
1200
|
// are stated; an expiry that fits neither is still a real arithmetic fault and still fails.
|
package/driver/repairs.mjs
CHANGED
|
@@ -708,7 +708,7 @@ export function decideRecovery({ failClass, sig, reason = "", history = [], prio
|
|
|
708
708
|
// the literal remedy for a provider that is down or refusing, so the second occurrence earns the
|
|
709
709
|
// next rung. Everything else keeps repeat-terminal:
|
|
710
710
|
// transient-but-wedged — nonzero_exit, unparseable_json, lane_wedge, a stage that times out every
|
|
711
|
-
// time: a byte-identical repeat means it is stuck, and three parks is the
|
|
711
|
+
// time: a byte-identical repeat means it is stuck, and three parks is the Open Country shape
|
|
712
712
|
// stale — the remedy is a deterministic recompute; if the recompute did not settle it, running
|
|
713
713
|
// it again cannot (the real fix for the observed stale kill is restamping, not retries)
|
|
714
714
|
// unknown — its one fresh sample already failed identically, so it has disproved itself
|
|
@@ -110,9 +110,9 @@ export function findingsDocFor(runDir, ordinal) {
|
|
|
110
110
|
// "jurisdiction"}` — there is no `register` and no `number`, on any record, ever. So both fallbacks
|
|
111
111
|
// fired on every card and the composed bullet read
|
|
112
112
|
//
|
|
113
|
-
// - Source: [register-euipo · /mark/eu/
|
|
113
|
+
// - Source: [register-euipo · /mark/eu/099999992](https://euipo.europa.eu/…)
|
|
114
114
|
//
|
|
115
|
-
// where the corpus it replaced reads `- Source: [EUIPO ·
|
|
115
|
+
// where the corpus it replaced reads `- Source: [EUIPO · 099999992](…)`. An internal enum token and an
|
|
116
116
|
// internal URI path, in the one line of a client-facing card whose job is to say which register holds
|
|
117
117
|
// the record and under what number. publish/index.mjs quotes the correct shape verbatim in a comment of
|
|
118
118
|
// its own, which is what makes the divergence provable rather than a matter of taste.
|
|
@@ -122,7 +122,7 @@ export function findingsDocFor(runDir, ordinal) {
|
|
|
122
122
|
// delivered report.md" — and writing it is what caught this. The guard now lives beside the code it checks, and
|
|
123
123
|
// this is the argument for building the guard an issue asks for even when the code already looks done.
|
|
124
124
|
//
|
|
125
|
-
// THE NUMBER comes off the URI's last segment: `/mark/eu/
|
|
125
|
+
// THE NUMBER comes off the URI's last segment: `/mark/eu/099999992` → `099999992`. That is a parse, and
|
|
126
126
|
// a parse is what this file exists to avoid — but the alternative is a field that does not exist, and
|
|
127
127
|
// the URI's shape is the driver's own (`/mark/<jurisdiction>/<id>`, dictated in gateway.mjs and bound
|
|
128
128
|
// from the fetched record). A URI that does not match it yields NO number rather than a wrong one, and
|
|
@@ -136,7 +136,7 @@ export function findingsDocFor(runDir, ordinal) {
|
|
|
136
136
|
// against evidence that does not exist.
|
|
137
137
|
const REGISTER_OFFICE = Object.freeze({ "register-euipo": "EUIPO" });
|
|
138
138
|
|
|
139
|
-
/** The registration number inside a driver record URI (`/mark/eu/
|
|
139
|
+
/** The registration number inside a driver record URI (`/mark/eu/099999992`), or `""`. PURE. */
|
|
140
140
|
export function registrationNumber(uri) {
|
|
141
141
|
const m = /^\/mark\/[a-z]{2}\/([A-Za-z0-9._-]+)$/.exec(str(uri));
|
|
142
142
|
return m ? m[1] : "";
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
// `verdict` is one of:
|
|
45
45
|
// "result" — the value states what happened. The name is honest.
|
|
46
46
|
// "invocation" — the value states that something was CALLED. Fix by renaming, or by adding a sibling
|
|
47
|
-
// that carries the result (the remedy
|
|
47
|
+
// that carries the result (the remedy createRepairLedger() in repairs.mjs already uses, keeping
|
|
48
48
|
// `outcome` for existing readers beside `dispatch` and `verdict`).
|
|
49
49
|
// "out-of-scope" — never reaches `_driver/*.json` or `run.jsonl`; `atWriteSite` is 0.
|
|
50
50
|
|
|
@@ -62,8 +62,8 @@ export const RESULT_NOUN_FIELDS = Object.freeze([
|
|
|
62
62
|
{ file: "driver/pipeline-knockout.mjs", noun: "recovered", sites: 1, atWriteSite: 1, verdict: "result",
|
|
63
63
|
why: "written after the chunk files were actually removed" },
|
|
64
64
|
{ file: "driver/pipeline-knockout.mjs", noun: "outcome", sites: 3, atWriteSite: 3, verdict: "result",
|
|
65
|
-
why: "the reviewing pass's own verdict on itself, read at each writing site: `nothing-flagged`
|
|
66
|
-
+ "the
|
|
65
|
+
why: "the reviewing pass's own verdict on itself, read at each writing site: `nothing-flagged` when "
|
|
66
|
+
+ "the record had no line a reader meets first, `applied` after the rewrite passed the merged gate and was "
|
|
67
67
|
+ "written, and the `keep()` forms after the artifact or the gate refused it. Every one is a fact "
|
|
68
68
|
+ "about what already happened to the record, never the return of the call being logged" },
|
|
69
69
|
{ file: "driver/pipeline.mjs", noun: "closed", sites: 6, atWriteSite: 4, verdict: "result",
|
package/driver/scope-facts.mjs
CHANGED
|
@@ -77,9 +77,12 @@ export function deriveScopeFacts({ instructedScope = null, plan = null, planExec
|
|
|
77
77
|
// nothing. This reader took the state and dropped the number, and the coverage line then described a
|
|
78
78
|
// slice that found nothing as one that "returned more records than could be listed in full".
|
|
79
79
|
const hitsByQid = new Map((planExecution?.executed ?? []).map((x) => [x.qid, Number.isFinite(x.total_hits) ? x.total_hits : null]));
|
|
80
|
+
// A slice that ran and that the executor deferred: the register could not answer it (an owner name it never
|
|
81
|
+
// resolved), which is positive evidence it was not an overflow. See the producer.
|
|
82
|
+
const deferredExecutedSet = new Set((planExecution?.executed ?? []).filter((x) => x?.deferred === true).map((x) => x.qid));
|
|
80
83
|
const missingSet = new Set(planExecution?.missing ?? []);
|
|
81
84
|
// The skip's own reason: why the broader search did not enumerate. See the producer.
|
|
82
|
-
const skippedByQid = new Map((planExecution?.skipped ?? []).map((x) => [x.qid, String(x.parent_state ?? "")]));
|
|
85
|
+
const skippedByQid = new Map((planExecution?.skipped ?? []).map((x) => [x.qid, { state: String(x.parent_state ?? ""), error: x.parent_error === true }]));
|
|
83
86
|
const skippedSet = new Set(skippedByQid.keys());
|
|
84
87
|
const deferredByQid = new Map((planExecution?.deferred ?? []).map((x) => [x.qid, String(x.reason ?? "")]));
|
|
85
88
|
// A WAITING FAMILY THAT NEVER BECAME A SEARCH OF ITS OWN LEAVES THE COUNT. One the reading turn asked
|
|
@@ -131,8 +134,11 @@ export function deriveScopeFacts({ instructedScope = null, plan = null, planExec
|
|
|
131
134
|
// AN ABSENT STATE IS NOT EVIDENCE, and this is the same trap as the count below: a receipt written
|
|
132
135
|
// before the producer carried the parent's state says nothing about why, and reading its silence as
|
|
133
136
|
// a failure would relabel every archived run's skips. Silence leaves the line as it was.
|
|
137
|
+
//
|
|
138
|
+
// AND `incomplete` IS NOT EVIDENCE OF A CROWD ON ITS OWN: the executor writes a provider error as an
|
|
139
|
+
// `incomplete` band with `error: true` beside it, and the producer now carries that flag with the skip.
|
|
134
140
|
const parent = skippedByQid.get(e.qid);
|
|
135
|
-
if (parent && parent !== "incomplete") a.skipped_after_failure++;
|
|
141
|
+
if (parent && (parent.error || (parent.state && parent.state !== "incomplete"))) a.skipped_after_failure++;
|
|
136
142
|
continue;
|
|
137
143
|
}
|
|
138
144
|
if (missingSet.has(e.qid)) { a.missing.push(e.qid); continue; }
|
|
@@ -170,7 +176,9 @@ export function deriveScopeFacts({ instructedScope = null, plan = null, planExec
|
|
|
170
176
|
// rather than too much. Only a MEASURED zero demotes it: a null is a count that could not be
|
|
171
177
|
// taken, which is not evidence either way, and reading absence as evidence is the defect this
|
|
172
178
|
// whole file keeps meeting.
|
|
173
|
-
|
|
179
|
+
// An owner sweep the executor deferred is the same fact reached another way: the register could not
|
|
180
|
+
// resolve the name, so the search found nothing to list, and there is no count to measure.
|
|
181
|
+
if (hitsByQid.get(e.qid) === 0 || deferredExecutedSet.has(e.qid)) a.incomplete_found_nothing++;
|
|
174
182
|
}
|
|
175
183
|
} else {
|
|
176
184
|
// a plan entry in NO execution bucket is UNACCOUNTED — the fail-closed reading is "missing"
|
package/driver/scope-rules.mjs
CHANGED
|
@@ -99,7 +99,7 @@ export function checkClearanceScopeRules({ job = null, profile = null, resolved
|
|
|
99
99
|
//
|
|
100
100
|
// Everything a CLIENT reads quotes `named`. This module was the one caller that discarded the
|
|
101
101
|
// originals, so the wall refused a two-country Full country search naming "GB", "FR" while the door
|
|
102
|
-
// refused the identical request naming "United Kingdom", "France" — products.mjs
|
|
102
|
+
// refused the identical request naming "United Kingdom", "France" — tally() in products.mjs states exactly
|
|
103
103
|
// that failure ("a refusal that quotes \"EU\" at someone who typed \"European Union\" sends them
|
|
104
104
|
// hunting for a value they never used") and territory-tiers.mjs restates it for partitionTerritories.
|
|
105
105
|
// The COUNT is the same either way: canonicalize dedups on code and products.mjs `tally` dedups on
|
|
@@ -131,7 +131,7 @@ export function checkClearanceScopeRules({ job = null, profile = null, resolved
|
|
|
131
131
|
// `nativeLanguage: true` over a knockout base) and through any account whose defaultProduct does not
|
|
132
132
|
// carry it.
|
|
133
133
|
//
|
|
134
|
-
// That is the shape products.mjs
|
|
134
|
+
// That is the shape CASE_LAW_NOT_A_REQUEST declared in products.mjs refuses `caseLaw` for in as many words — "a flag accepted and
|
|
135
135
|
// dropped is the worst available shape: whoever sent it believes they bought the deep reading, and
|
|
136
136
|
// nothing anywhere disagrees" — and it was this build's own doctrine broken by this build's own code.
|
|
137
137
|
//
|
package/driver/search-policy.mjs
CHANGED
|
@@ -1165,7 +1165,26 @@ export function countJobMarks(job) {
|
|
|
1165
1165
|
// The mark → filesystem-key derivation (research payloads, fixtures). ONE definition — the knockout
|
|
1166
1166
|
// lane keys per-mark artifacts on it, so two batch marks that collide here ("MOTO X" / "MOTO-X") would
|
|
1167
1167
|
// silently share a research payload; every door checks collisions with THIS function.
|
|
1168
|
-
|
|
1168
|
+
//
|
|
1169
|
+
// THE KEY IS UNIQUE FOR TWO NAMES THAT DIFFER IN ANY LETTER OR NUMBER, in any script. The readable part
|
|
1170
|
+
// keeps only `a-z0-9`, because the key is also a report route segment. It used to be the whole key, so
|
|
1171
|
+
// every name written wholly in Greek, Cyrillic or Han keyed as `mark`, and two different names of that
|
|
1172
|
+
// kind were refused as duplicates at intake; two Latin names differing only in an accented letter merged
|
|
1173
|
+
// the same way. A name whose letters and numbers the readable part does not hold in full now carries a
|
|
1174
|
+
// short hash of them, NFKC-folded and lowercased. Spacing, punctuation, symbols and case still change
|
|
1175
|
+
// nothing, so a true duplicate still collides. A name the readable part already held in full keys exactly
|
|
1176
|
+
// as before, byte for byte: research payloads, published report files and per-name links were keyed on it.
|
|
1177
|
+
// SYMBOLS GO BEFORE THE FOLD. NFKC turns ™ into "TM" and ℠ into "SM", which would move "BRAND™" off the key
|
|
1178
|
+
// it shares with "BRAND"; symbols are the only class NFKC turns into letters or numbers, so dropping them
|
|
1179
|
+
// first leaves the fold acting on letters and numbers alone.
|
|
1180
|
+
const asciiKey = (s) => String(s ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1181
|
+
const nameLetters = (s) => String(s ?? "").replace(/\p{S}/gu, "").normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "");
|
|
1182
|
+
export const kebab = (s) => {
|
|
1183
|
+
const ascii = asciiKey(s);
|
|
1184
|
+
const letters = nameLetters(s);
|
|
1185
|
+
if (letters === ascii.replace(/-/g, "")) return ascii.slice(0, 60) || "mark";
|
|
1186
|
+
return `${(ascii || "mark").slice(0, 51).replace(/-+$/, "")}-${createHash("sha256").update(letters).digest("hex").slice(0, 8)}`;
|
|
1187
|
+
};
|
|
1169
1188
|
export function kebabCollisions(names) {
|
|
1170
1189
|
const seen = new Map(); const out = [];
|
|
1171
1190
|
for (const n of names ?? []) {
|
|
@@ -137,7 +137,7 @@ they render as nothing: no reader ever sees them.
|
|
|
137
137
|
not need to match the template's wording, and you will not be failed for choosing different words.
|
|
138
138
|
|
|
139
139
|
```markdown
|
|
140
|
-
# Common-law findings —
|
|
140
|
+
# Common-law findings — Dusk: Tales of Thornmantle (2026-05-11)
|
|
141
141
|
|
|
142
142
|
## Summary
|
|
143
143
|
|
|
@@ -146,7 +146,7 @@ not need to match the template's wording, and you will not be failed for choosin
|
|
|
146
146
|
- Findings surfaced: 12 risk-relevant + 3 competitor intel + 1 PR risk
|
|
147
147
|
- Open verification flags: 2
|
|
148
148
|
|
|
149
|
-
## Findings — Mark:
|
|
149
|
+
## Findings — Mark: Dusk: Tales of Thornmantle
|
|
150
150
|
<!-- clearotron:section=findings -->
|
|
151
151
|
|
|
152
152
|
### Consumer-confusion risks (gaming-industry overlap)
|
|
@@ -155,24 +155,24 @@ For game-title rows, the `developer_of_record` and `publisher_of_record` columns
|
|
|
155
155
|
|
|
156
156
|
| Finding | Source / Platform | URL | developer_of_record | publisher_of_record | Type | Notes |
|
|
157
157
|
|---|---|---|---|---|---|---|
|
|
158
|
-
| "
|
|
159
|
-
| "Thornmantle" | itch.io | https://... | Thornfall Games | not extracted | Direct conflict — identical game-title | Single-player adventure
|
|
158
|
+
| "Tales of Dusk" | Steam | https://... | Glimmerhouse | Northgale Interactive | Direct conflict — similar gaming title | "Mostly negative" reviews; commercially active 2013/2015 |
|
|
159
|
+
| "Thornmantle" | itch.io | https://... | Thornfall Games | not extracted | Direct conflict — identical game-title | Single-player adventure made for a game-design course brief; little online presence; not reviewed in 7 years |
|
|
160
160
|
| "Thornmantle: Of Magic and Power" | Moby Games | https://... | not extracted | not extracted | Direct conflict — historical gaming title | Released 2007; status unavailable; verify-publisher flag set |
|
|
161
|
-
| "Thornmantle"
|
|
162
|
-
| "Ysolde the Thornmantle" |
|
|
161
|
+
| "Thornmantle" Starhollow ARPG | News / web | https://... | Starhollow | not extracted | Direct conflict — gaming title | Browser game; appears cancelled |
|
|
162
|
+
| "Ysolde the Thornmantle" | tabletop RPG wiki | https://... | N/A | Grimtable Press | Conceptual — deity name in tabletop RPG | Tabletop, not video game; commercial risk in RPG space |
|
|
163
163
|
|
|
164
164
|
### Commercial awareness (identical/similar in unrelated fields)
|
|
165
165
|
|
|
166
166
|
| Finding | Source / Platform | URL | Notes |
|
|
167
167
|
|---|---|---|---|
|
|
168
|
-
| "Raising Your Play" |
|
|
169
|
-
| 1,600+ "
|
|
168
|
+
| "Raising Your Play" | Brightwick marketing | https://... | Brightwick uses tagline for gaming hardware; no register protection found (flagged for clearance-register cross-check) |
|
|
169
|
+
| 1,600+ "Dusk" titles on Steam | Steam | https://... | Crowded field — supportive evidence |
|
|
170
170
|
|
|
171
171
|
### Competitor intelligence
|
|
172
172
|
|
|
173
173
|
| Finding | Source / Platform | URL | Notes |
|
|
174
174
|
|---|---|---|---|
|
|
175
|
-
|
|
|
175
|
+
| Veltrona "Pulse Raise" portfolio | Veltrona products | https://... | Veltrona uses "Raise" in audio products; flagged for clearance-register cross-check |
|
|
176
176
|
| Foxglade "Borealis" console "Raise Your Play" tagline (prior usage) | Foxglade Interactive marketing | https://... | Client's own prior use — note as supportive |
|
|
177
177
|
|
|
178
178
|
### PR / reputational risk
|
|
@@ -187,7 +187,7 @@ means *blackberry*" is context, not a clearance: a connotation reads clean only
|
|
|
187
187
|
|
|
188
188
|
| Finding | Source | Notes |
|
|
189
189
|
|---|---|---|
|
|
190
|
-
| meaning readings surfaced | Urban Dictionary / Wikipedia / news / forums | per form, the readings the search returned + benign/loaded label — e.g. `
|
|
190
|
+
| meaning readings surfaced | Urban Dictionary / Wikipedia / news / forums | per form, the readings the search returned + benign/loaded label — e.g. `RAISE → ordinary verb "lift" (benign)`; near-forms `RAIZE / RAYSE → no slang/gang/offensive reading`. Write `(None identified)` as the *bottom line* only after the readings are laid out and none is loaded. |
|
|
191
191
|
|
|
192
192
|
A clean PR/connotation row MUST cite its search — add a `**Connotation-search source:** <URL | "perplexity_research — no result">` line. The driver dictates the meaning sweep into the grid (the matter frame's meaning questions) and the plugin records every query into the ledger's `extras.pr_risk[]`; the `commonLaw` validator rejects a `(None identified)` claim with no recorded queries (`connotation_search_missing`). An empty-results search is a clean receipt; a *missing* search is not.
|
|
193
193
|
|
|
@@ -340,13 +340,13 @@ forms (and the gap form) — this is what the driver's receipt gate counts:
|
|
|
340
340
|
|
|
341
341
|
| Variant | Platform | Result |
|
|
342
342
|
|---|---|---|
|
|
343
|
-
|
|
|
344
|
-
|
|
|
345
|
-
|
|
|
346
|
-
|
|
|
347
|
-
|
|
|
348
|
-
|
|
|
349
|
-
|
|
|
343
|
+
| Dusk: Tales of Thornmantle | Steam | No results |
|
|
344
|
+
| Dusk: Tales of Thornmantle | Epic Games Store | No similar listings (6 candidates reviewed) |
|
|
345
|
+
| Dusk: Tales of Thornmantle | Google Play | No results |
|
|
346
|
+
| Dusk: Tales of Thornmantle | Apple App Store | No similar listings (3 candidates reviewed) |
|
|
347
|
+
| Dusk: Tales of Thornmantle | Microsoft Store | No results |
|
|
348
|
+
| Dusk: Tales of Thornmantle | itch.io | Similar listing(s) found — see Findings (2 candidates) |
|
|
349
|
+
| Dusk: Tales of Thornmantle | web | No similar listings (8 candidates reviewed) |
|
|
350
350
|
| エバーライト | Steam | not executed — coverage-limited (see ledger) |
|
|
351
351
|
|
|
352
352
|
### Coverage ledger (feeds synthesis coverage-honesty + skeptic audit)
|
|
@@ -366,8 +366,8 @@ Record the same statuses by calling `record_coverage_status`, passing `grid_spec
|
|
|
366
366
|
|
|
367
367
|
| Trigger | Suggested cross-check |
|
|
368
368
|
|---|---|
|
|
369
|
-
|
|
|
370
|
-
|
|
|
369
|
+
| Brightwick appears using "RAISING YOUR PLAY" | Check register: does Brightwick have any raise-related trademark in target classes? |
|
|
370
|
+
| Veltrona "Pulse Raise" portfolio | Check register: any Veltrona raise filings in target classes? |
|
|
371
371
|
| Thornfall Games "Thornmantle" (itch.io) | Check register: any Thornfall trademark protection? |
|
|
372
372
|
|
|
373
373
|
### Audit trail
|
|
@@ -377,8 +377,8 @@ Record the same statuses by calling `record_coverage_status`, passing `grid_spec
|
|
|
377
377
|
|---|---|---|---|
|
|
378
378
|
| 1 | Grid (sandbox) | Search-as-code grid: 9 variants × 7 platforms + extras | 63 cells, 0 gaps, 487 candidates recorded; 12 risk-relevant after judgment |
|
|
379
379
|
| 2 | Famous-mark | None flagged in manifest | (not run) |
|
|
380
|
-
| 3 | Follow-up |
|
|
381
|
-
| 4 | Follow-up |
|
|
380
|
+
| 3 | Follow-up | Brightwick gaming portfolio + "raising your play" deeper trace | 3 additional findings |
|
|
381
|
+
| 4 | Follow-up | Veltrona pulse-raise portfolio | 2 additional findings |
|
|
382
382
|
|
|
383
383
|
**Grid program receipt** — the executed sandbox program returned by the tool, verbatim:
|
|
384
384
|
|
|
@@ -389,7 +389,7 @@ Record the same statuses by calling `record_coverage_status`, passing `grid_spec
|
|
|
389
389
|
### Open verification flags
|
|
390
390
|
|
|
391
391
|
- Thornfall Games "Thornmantle" itch.io listing — URL needs re-verification (404'd during follow-up call)
|
|
392
|
-
-
|
|
392
|
+
- Brightwick "Raising Your Play" marketing presence in EU — coverage was thin, may need a region-specific follow-up
|
|
393
393
|
```
|
|
394
394
|
|
|
395
395
|
## Process
|
|
@@ -135,8 +135,8 @@ drop can be neither examined nor audited. Batching stays correct for *retrieval*
|
|
|
135
135
|
enumeration, `_query` attribution); it is only forbidden as a unit of dismissal.
|
|
136
136
|
|
|
137
137
|
One entry, one record, one reason. A `drop_reason` is the judgment alone — the uri, verdict, class and
|
|
138
|
-
status are rendered beside it: `dropped — off-field (relevance gate):
|
|
139
|
-
descriptive-compound risk theory retains
|
|
138
|
+
status are rendered beside it: `dropped — off-field (relevance gate): DUSK-only hit; the
|
|
139
|
+
descriptive-compound risk theory retains DUSK-only hits only in gaming, and this is film production`.
|
|
140
140
|
|
|
141
141
|
### Dominant-element reconciliation (driver-gated — every screened composite ENDS somewhere)
|
|
142
142
|
|
|
@@ -435,7 +435,7 @@ read. Keep if plausibly on-point; drop only genuine field-irrelevance, or a near
|
|
|
435
435
|
| Archetype | Keep when | Drop when |
|
|
436
436
|
|---|---|---|
|
|
437
437
|
| **Coined word** | Mark text contains a phonetic/visual neighbour of the coined element; or is the coined element exact-match | Mark text shares only an unrelated real-word token via tokenisation (e.g., searching coined THORNMANTLE and surfacing a mark whose only overlap is a "LIGHT" token) |
|
|
438
|
-
| **Descriptive compound** | Mark contains the distinctive anchor; OR is an identical/near-identical compound-phrase match; OR is a single-element registered mark on a common element with goods/services in the target field | Mark is a 5+ element compound that happens to share one common token (e.g., a film studio's "
|
|
438
|
+
| **Descriptive compound** | Mark contains the distinctive anchor; OR is an identical/near-identical compound-phrase match; OR is a single-element registered mark on a common element with goods/services in the target field | Mark is a 5+ element compound that happens to share one common token (e.g., a film studio's "DUSK OF VALOR" filing surfaced when searching "Dusk: Tales of Thornmantle" — DUSK is common, surrounding context unrelated) |
|
|
439
439
|
| **Slogan** | Mark matches the slogan structure (same template, same semantic field, family-pattern hit, verb-swap, slang variant, co-brand wildcard); OR is the unique distinctive token of the slogan if any | Single-word hit on a slogan element in an unrelated context (DAWN alone surfaced when searching "DAWN OF NEW DAY" slogan); slogan in a wildly different semantic field with no template overlap |
|
|
440
440
|
| **Acronym / initialism** | Mark text contains the letter string in target classes; OR is the expansion form of the acronym | Random word that happens to contain the letters as a substring of a longer word |
|
|
441
441
|
| **Device-led** (modifier) | Standard tests for primary archetype, plus visual similarity per design-code search | Pure-text noise with no visual signal |
|
|
@@ -46,13 +46,13 @@ the cross-cutting relevance / sufficiency / owner-aggregation / prioritisation o
|
|
|
46
46
|
|
|
47
47
|
## Recipe 1 — Compound tagline (multi-word mark with distinctive element)
|
|
48
48
|
|
|
49
|
-
**When to use:** mark has 3+ words where 1 element is distinctive and the others are common (e.g., "
|
|
49
|
+
**When to use:** mark has 3+ words where 1 element is distinctive and the others are common (e.g., "Dusk: Tales of Lumengarde" — LUMENGARDE distinctive, DUSK + TALES common).
|
|
50
50
|
|
|
51
51
|
**Pattern:**
|
|
52
52
|
|
|
53
53
|
```
|
|
54
54
|
1. Crowd descriptor — count-only probe each saturated common element (limit=1, fields=[uri])
|
|
55
|
-
→ name:
|
|
55
|
+
→ name:DUSK classes 9,28,41,42 → write an `incomplete` crowd-descriptor block (count + reason)
|
|
56
56
|
→ name:LEGEND classes 9,28,41,42 → write an `incomplete` crowd-descriptor block
|
|
57
57
|
(these enumerate nothing — they describe the crowd for judgment)
|
|
58
58
|
|
|
@@ -60,11 +60,11 @@ the cross-cutting relevance / sufficiency / owner-aggregation / prioritisation o
|
|
|
60
60
|
→ name:LUMENGARDE classes 9,28,41,42 → enumerated|incomplete block (every record carried forward)
|
|
61
61
|
|
|
62
62
|
3. ENUMERATE each common element as a named query in the in-scope classes
|
|
63
|
-
→ name:
|
|
63
|
+
→ name:DUSK match=exact classes 9,28,41,42 → register_enumerate (exact-in-class name-list)
|
|
64
64
|
→ name:LEGEND match=exact classes 9,28,41,42 → register_enumerate
|
|
65
65
|
|
|
66
66
|
4. ENUMERATE the compound phrase
|
|
67
|
-
→ name:"
|
|
67
|
+
→ name:"Dusk: Tales of Lumengarde" match=phrase classes 9,28,41,42 → register_enumerate
|
|
68
68
|
|
|
69
69
|
5. ENUMERATE a cross-class merch query if any identical match
|
|
70
70
|
→ name:<exact mark> match=exact classes 25 → register_enumerate
|
|
@@ -168,11 +168,11 @@ For each transliteration variant in the manifest:
|
|
|
168
168
|
|
|
169
169
|
```
|
|
170
170
|
1. Crowd descriptors — count-only probes on the saturated common elements (limit=1, fields=[uri])
|
|
171
|
-
→ name:
|
|
172
|
-
→ name:
|
|
171
|
+
→ name:RAISE classes 9/28/41/42 → `incomplete` crowd-descriptor block
|
|
172
|
+
→ name:PLAY classes 9/28/41/42 → `incomplete` crowd-descriptor block
|
|
173
173
|
|
|
174
174
|
2. Crowd descriptor — partial-phrase prefix count (the narrative anchor)
|
|
175
|
-
→ phrase:"
|
|
175
|
+
→ phrase:"RAISE YOUR" classes <target> limit=1 → `incomplete` block ("234 live RAISE YOUR ___")
|
|
176
176
|
|
|
177
177
|
3. ENUMERATE the compound phrase, 3 match-modes (register_enumerate each)
|
|
178
178
|
→ exact:"RAISE YOUR PLAY" classes <target> → identical-mark band
|
|
@@ -186,12 +186,12 @@ For each transliteration variant in the manifest:
|
|
|
186
186
|
→ ...
|
|
187
187
|
|
|
188
188
|
5. ENUMERATE wildcard variant queries (per manifest)
|
|
189
|
-
→ name:"
|
|
190
|
-
→ name:"
|
|
191
|
-
→ name:"* your
|
|
189
|
+
→ name:"Raise * play" classes <target> → register_enumerate
|
|
190
|
+
→ name:"Raise your *" classes <target> → register_enumerate
|
|
191
|
+
→ name:"* your play" classes <target> → register_enumerate
|
|
192
192
|
|
|
193
193
|
6. ENUMERATE each common element's exact-in-class name-list (register_enumerate)
|
|
194
|
-
→ name:
|
|
194
|
+
→ name:RAISE match=exact classes <target> (scope by CLASS + REGION, not a product/goods term)
|
|
195
195
|
|
|
196
196
|
7. ENUMERATE a cross-class merch query if identical match
|
|
197
197
|
```
|
|
@@ -143,7 +143,7 @@ The order matters. Specifically:
|
|
|
143
143
|
| `"PLAYVERSE LEVEL UP GAMING"` | `"Level up together"` | false |
|
|
144
144
|
| `"It's Your Play"` | `"In Your Play"` | false (apostrophe matters) |
|
|
145
145
|
|
|
146
|
-
These fixtures should be encoded in test cases for any agent or analyzer implementing the rule. Worked example for the "
|
|
146
|
+
These fixtures should be encoded in test cases for any agent or analyzer implementing the rule. Worked example for the "Dusk: Tales of Lumengarde" matter: "Dusk: Tales of Lumengarde" ≡ "DUSK TALES OF LUMENGARDE" ≡ "dusk tales of lumengarde" — all identical. "Dusk of Lumengarde" or "Dusk: Tales" — NOT identical.
|
|
147
147
|
|
|
148
148
|
## Chinese status strings
|
|
149
149
|
|
|
@@ -150,7 +150,7 @@ band its plan entries call for is missing is refused as `named_band_missing`.
|
|
|
150
150
|
### Per-axis specifics
|
|
151
151
|
|
|
152
152
|
- **`saturation-probe`** — fires when any element is flagged `saturation: high`/`very-high`, plus a partial-phrase structural-prefix probe for slogan/descriptive-compound multi-word marks. Run count-only probes (`limit:1, fields:["uri"]`) capturing `total_hits`, and write **one `incomplete` crowd-descriptor block per probed element** (`fetched:0`, `reason:"crowd descriptor — saturated element <X>, count-only"`). It measures how crowded an element is — it **enumerates nothing and clears nothing**. Skip (not applicable) if all elements are distinctive/low-saturation. **A meaning-translation variant (`translit-*-meaning`) is NOT special-cased into a drop:** an everyday-word-scale count is just a crowd descriptor; whether the field-scoped named slice inside it matters is judgment's call — the funnel surfaces the count, and the **primary-sweep** axis runs the field-scoped named enumeration where the frame says that market matters (see `transliteration-numeric`). Record `translit-too-generic` in the descriptor `reason` as a context note (NOT a drop / re-narrow / swap-to-phonetic signal). An implausibly LOW count on a translation that ought to have peers is the separate `translit-underretrieved` context note — also recorded in the descriptor `reason`, never acted on as a drop. Both are signals the lawyer reads.
|
|
153
|
-
- **`primary-sweep`** — always applicable. Run the playbook's named variant queries: the exact mark + each `phrase-substitution`, `visual-substitution`, family-pattern wildcard, and slang variant the manifest lists, **each via `register_enumerate`** (the tool guarantees the page loop). Owns the family-pattern / phrase-substitution / slang / visual-substitution variant queries. **Owns the dead-inclusive named enumeration** (its `register_enumerate` calls carry live AND dead records forward with their status — see step 6; there is no separate "dead-but-identical" or "D4" pass, because the funnel no longer filters dead records). **On a saturated unit, owns the [named-band enumeration of the dangerous category](#exact-in-class-live-floor-primary-sweep-unit-owns-it)** (the exact mark + the **distinctive** formative root / dominant token as the substring-in-scope-class-live named slice, per major+material jurisdiction, plus the phonetic fringe) — enumerated, not sampled. **The named enumeration covers a saturated `translit-*-meaning` meaning token too, where the frame says that market matters (see `transliteration-numeric`)** (scoped to the filed in-scope Nice classes as the `nice_classes` × `regions` filter on the `register_enumerate` call, NOT goods-words ANDed into the search text) — not only the Latin dominant element / formative root. **But a COMMON component that is NOT the distinctive anchor** (a "stripped" common word the manifest marks common/descriptive —
|
|
153
|
+
- **`primary-sweep`** — always applicable. Run the playbook's named variant queries: the exact mark + each `phrase-substitution`, `visual-substitution`, family-pattern wildcard, and slang variant the manifest lists, **each via `register_enumerate`** (the tool guarantees the page loop). Owns the family-pattern / phrase-substitution / slang / visual-substitution variant queries. **Owns the dead-inclusive named enumeration** (its `register_enumerate` calls carry live AND dead records forward with their status — see step 6; there is no separate "dead-but-identical" or "D4" pass, because the funnel no longer filters dead records). **On a saturated unit, owns the [named-band enumeration of the dangerous category](#exact-in-class-live-floor-primary-sweep-unit-owns-it)** (the exact mark + the **distinctive** formative root / dominant token as the substring-in-scope-class-live named slice, per major+material jurisdiction, plus the phonetic fringe) — enumerated, not sampled. **The named enumeration covers a saturated `translit-*-meaning` meaning token too, where the frame says that market matters (see `transliteration-numeric`)** (scoped to the filed in-scope Nice classes as the `nice_classes` × `regions` filter on the `register_enumerate` call, NOT goods-words ANDed into the search text) — not only the Latin dominant element / formative root. **But a COMMON component that is NOT the distinctive anchor** (a "stripped" common word the manifest marks common/descriptive — DUSK / TALES / GREAT / OUTDOORS) **is count-only, owned by `saturation-probe`, and is NOT enumerated here**: `saturation-probe` already counts it and hands that count up; primary-sweep does **not** run the substring / per-major / phonetic enumeration on it and does **not** author a second crowd block for it (see the linked section — wholesale-enumerating a common component per-jurisdiction is the mega-crowd grind that SIGKILLs the stage).
|
|
154
154
|
- **`transliteration-numeric`** — fires when the manifest has `translit-*` variants or numeric-substitution variants. Ask one script question per market the frame names. Where the register files foreign marks by their romanised form, and it says so, the Latin question already covers them; do not ask it again in other scripts. A word's meaning in another language is asked only where the frame says that market matters and buyers would read the word as the mark. For each numeric-substitution variant enumerate its query. Owns the foreign-transliteration + numeric-substitution variant queries. Skip if the manifest classifies the mark English-only / single-language. (Multi-script accumulation is no longer a window risk for the unit — `register_enumerate` owns the page loop and a crowd returns `incomplete`, so the unit does not deep-page raw rows itself.)
|
|
155
155
|
- **`incumbent-class`** — fires when the manifest has an `industry_incumbent_alert`. `register_enumerate` the named band in the matter's in-scope Nice set and markets — **never an all-class incumbent sweep** (that is the unscoped crowd that timed this very axis out live). **Beyond the NAMED watchlist incumbents, the named enumeration is an UNNAMED-OWNER exact-in-class-live enumeration in those classes** — the watchlist seeds prioritise *attention* (judgment's use), they do **not** bound the funnel's enumeration. Every exact-token-in-class-live record crosses the firewall whether or not its owner was pre-listed; the watchlist is provably incomplete, so it never decides what the funnel enumerates. Skip only if there is no incumbent alert AND the frozen plan carries no entries on this axis — the plan is the search authority. **The plan carries no owner lane from the manifest's watchlist.** Read owners from the records this axis and the primary sweep returned. Propose an owner slice only where an owner's other marks could change the assessment, for example when the records show a family of marks sharing the element. A bare-owner count is never written up as a reviewed portfolio or as "portfolio too large, noted". Read an owner's portfolio as far as it answers a question about this owner: can it block, would it, and what kind of filer it is. Never its whole register because it is an incumbent.
|
|
156
156
|
|
|
@@ -229,12 +229,13 @@ sensitivity, or a commercial partnership cannot lift a finding past what the fra
|
|
|
229
229
|
they only change *whether they'd win* or the *character* of the dispute, and the framework then produces
|
|
230
230
|
the band.
|
|
231
231
|
|
|
232
|
-
### Enforcer profiling — mandatory for findings
|
|
232
|
+
### Enforcer profiling — mandatory for findings rated medium risk or higher
|
|
233
233
|
|
|
234
234
|
Assess: Is this a portfolio brand? TTAB/UDRP history? Institutional vs individual filer? "No large brands
|
|
235
235
|
or assertive enforcers" is a practical factor the staff lawyer weights heavily. *(`courtlistener__search` covers US
|
|
236
236
|
federal courts incl. CAFC trademark-appeal opinions — NOT raw TTAB proceedings. For TTAB direct
|
|
237
|
-
(oppositions, cancellations, ex parte), source is TTABVUE — separate integration, not yet wired.)*
|
|
237
|
+
(oppositions, cancellations, ex parte), source is TTABVUE — separate integration, not yet wired.)* The Step 3.5 query (`phase2-execution.md`) asks for
|
|
238
|
+
the owner's enforcement record; what it finds is a lead, cited inline, never a verified authority.
|
|
238
239
|
|
|
239
240
|
Profiling informs the **business / practical read only — it never raises the legal read** (the read is the
|
|
240
241
|
confusion comparison; see *The consumer confusion test*). When the signals cut both ways — a broad
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- [Variant execution strategy (register layer)](#variant-execution-strategy-register-layer)
|
|
18
18
|
- [Step 2.6 — Skeptic review](#step-26--skeptic-review-a-fresh-eyes-self-audit-before-you-trust-the-findings)
|
|
19
19
|
- [Step 3 — Cross-pollination (Option D — deterministic cross-checks, cap N=10)](#step-3--cross-pollination-option-d--deterministic-cross-checks-cap-n10)
|
|
20
|
-
- [Step 3.5 — Actual-use check (mandatory for
|
|
20
|
+
- [Step 3.5 — Actual-use and enforcement check (mandatory for register hits rated medium risk or higher)](#step-35--actual-use-and-enforcement-check-mandatory-for-register-hits-rated-medium-risk-or-higher)
|
|
21
21
|
- [Step 3.6 — Owner workup (mandatory for high-risk findings)](#step-36--owner-workup-mandatory-for-high-risk-findings)
|
|
22
22
|
- [Step 4 — Joint synthesis](#step-4--joint-synthesis)
|
|
23
23
|
- [Step 4.5 — Case-law grounding](#step-45--case-law-grounding)
|
|
@@ -78,7 +78,7 @@ skipped step must be logged with its reason in the unit's digest audit.
|
|
|
78
78
|
1. **Macro probes** — one search per element of the compound + one for the full compound phrase (limit=10 each), to establish saturation levels. Minimum 3 searches. SKIP only if all elements are non-common-words (rare).
|
|
79
79
|
2. **Compound-phrase sweeps** — the compound phrase across **three match-modes (exact, phrase, default)**, limit=100 per search. Minimum 3 searches. SKIP only if the phrase is a single word (then it's a single-word funnel, not compound).
|
|
80
80
|
3. **Single-word coverage hunts** — for each element marked "demote to filter" in the manifest, one search with product/industry filters applied (limit=50). Minimum 1 per such element.
|
|
81
|
-
4. **Wildcard reorders** — if the manifest lists word-order variants, at least one wildcard sweep per distinct ordering (e.g. `
|
|
81
|
+
4. **Wildcard reorders** — if the manifest lists word-order variants, at least one wildcard sweep per distinct ordering (e.g. `Raise * play`, `play * Raise`).
|
|
82
82
|
5. **Numeric-substitution and transliteration sweeps** — execute every sweep marked ✅ (requires verification) in the variant manifest, UNLESS the manifest classifies the mark as English-only (single-language Recipe 1 pattern). Skipping any ✅ requires a stated reason in the unit's digest audit.
|
|
83
83
|
6. **Material-jurisdiction sub-queries** — a **protected, non-yielding** line item, NOT folded into the sweep budget. Run one scoped sub-query per jurisdiction `matter-context` declares materially-matters (no top-N cap). These outrank within-axis breadth: if budget is tight, an extra script group (step 5) yields and is logged `coverage-limited` — a material jurisdiction is never dropped to fund breadth, and a jurisdiction that genuinely can't run is logged `deferred`, never silent.
|
|
84
84
|
|
|
@@ -230,9 +230,9 @@ Cap-overflow MUST be flagged in the deliverable audit trail with a specific reas
|
|
|
230
230
|
|
|
231
231
|
Every cross-check (including "checked, found nothing") logged in the unified audit trail as proof-of-work. The collaborator value-add (vs assistant) is in this section: synthesis can be decisive ("HP uses, does not register → Level 2 B") rather than tentative ("recommend the staff lawyer check HP register").
|
|
232
232
|
|
|
233
|
-
## Step 3.5 — Actual-use check (mandatory for register hits
|
|
233
|
+
## Step 3.5 — Actual-use and enforcement check (mandatory for register hits rated medium risk or higher)
|
|
234
234
|
|
|
235
|
-
Before synthesis, for **every register finding** advisory-rated **
|
|
235
|
+
Before synthesis, for **every register finding** advisory-rated on a rung **the framework rates medium risk or higher** (the stage message names those rungs; a rung below that is not checked, on any client's ladder), run a marketplace check via `perplexity_research` to determine whether the registered mark is **actually being used in the marketplace within the scope of the current search** (i.e. for the goods/services and field this clearance targets). **The same query also asks for the owner's enforcement record:** whether the owner has opposed, disputed or sued over this mark or a similar one.
|
|
236
236
|
|
|
237
237
|
Example: a JELLY register hit owned by Jellycat (a toy company) for Class 9 video game software — check whether Jellycat actually sells or markets video games under JELLY. If they don't (their commercial use is toys), note the filed-vs-used distinction in Key Factors and weigh it as a Stage-2 factor together with revocation-vulnerability — no automatic downgrade (see `synthesis-rules.md` → *Actual-use assessment results*).
|
|
238
238
|
|
|
@@ -244,6 +244,7 @@ Example: a JELLY register hit owned by Jellycat (a toy company) for Class 9 vide
|
|
|
244
244
|
- If actual use in the relevant field is **not found** (despite the filing), do **NOT** automatically downgrade — weigh the lack of field use as a Stage-2 factor together with revocation-vulnerability (`synthesis-rules.md` → *Actual-use assessment results* / *Rule on downgrade quantum*), and note in Key Factors: "Register filing claims [field] but no marketplace use identified — filed-vs-used noted; weighed with revocability."
|
|
245
245
|
- This check measures *use in the field*, not registry status: a renewal / re-registration / "Registered" status is administrative upkeep, not marketplace use, and does not satisfy the actual-use check or defeat a non-use mitigant (see `synthesis-rules.md` → "Administrative liveness is not market use").
|
|
246
246
|
- Owner-portfolio-bound common-law checks already counted in this step count toward the Option D cap (Step 3).
|
|
247
|
+
- **Enforcement the query finds is a lead, not a verified authority** (the ENFORCEMENT AUTHORITY rule in the stage message): it informs the enforcer meter with basis `inferred-from-signal`, and its URL is cited inline in the finding's prose. If the query finds no enforcement record, the enforcer is `low` with basis `inferred-from-signal`, and the finding says the search found none. The enforcer stays `unknown`, and nothing is claimed, only when the query could not run.
|
|
247
248
|
|
|
248
249
|
**Output contract — ENFORCED by the driver (spec 11), not optional.** For every Composite 3+ finding whose
|
|
249
250
|
mitigant or verdict turns on the **absence** of use (it says the mark is "not in actual use" / "unused" / has
|
|
@@ -360,7 +361,7 @@ the verdict** (the driver decides this in code):
|
|
|
360
361
|
Runs **ONCE**, after all marks have completed Steps 2–4.5. Review findings across all marks and identify:
|
|
361
362
|
|
|
362
363
|
1. **Shared findings:** a single entity/finding relevant to more than one proposed mark (e.g., "Prize Crate Retail" is relevant to both "PRIZE CRATE" and "CAVERN CRATES")
|
|
363
|
-
2. **Pattern findings:** recurring themes across marks (e.g., "the '
|
|
364
|
+
2. **Pattern findings:** recurring themes across marks (e.g., "the 'prize' prefix is crowded across all gaming platforms")
|
|
364
365
|
3. **Compound risk:** where the combination of marks might amplify risk (e.g., using both "PRIZE CRATE" and "CAVERN CRATES" increases the chance of a dispute with Prize Crate Retail)
|
|
365
366
|
|
|
366
367
|
For each cross-reference: note which marks are affected, describe the connection, flag any compounding risk. Cross-references appear in the narrative summary and as a note in the Findings sheet.
|
|
@@ -28,7 +28,7 @@ Across the synthesis, two postures are load-bearing:
|
|
|
28
28
|
- [Per-finding synthesis structure](#per-finding-synthesis-structure)
|
|
29
29
|
- [Cancelled / invalid marks — include if exact, no commentary](#cancelled--invalid-marks--include-if-exact-no-commentary)
|
|
30
30
|
- [The mark itself — standing assessment (every run)](#the-mark-itself--standing-assessment-every-run)
|
|
31
|
-
- [Filed-vs-used check — mandatory for findings
|
|
31
|
+
- [Filed-vs-used check — mandatory for findings rated medium risk or higher](#filed-vs-used-check--mandatory-for-findings-rated-medium-risk-or-higher)
|
|
32
32
|
- [Prosecution history — mandatory for identical-mark hits in relevant classes](#prosecution-history--mandatory-for-identical-mark-hits-in-relevant-classes)
|
|
33
33
|
- [Revocability — mandatory assessment for register findings above the lowest band](#revocability--mandatory-assessment-for-register-findings-above-the-lowest-band)
|
|
34
34
|
- [Verify-or-defer — procedure, deadlines, registry statistics](#verify-or-defer--procedure-deadlines-registry-statistics)
|
|
@@ -449,15 +449,15 @@ reviewing lawyer to assess), frames the report, and **never moves any band**.
|
|
|
449
449
|
into a longer paragraph. Real PR / reputational **hits** still live in the PR / reputational section (see
|
|
450
450
|
above); this block is the standing read, not the incident report.
|
|
451
451
|
|
|
452
|
-
## Filed-vs-used check — mandatory for findings
|
|
452
|
+
## Filed-vs-used check — mandatory for findings rated medium risk or higher
|
|
453
453
|
|
|
454
|
-
For every register finding advisory-rated
|
|
454
|
+
For every register finding advisory-rated on a rung the framework rates medium risk or higher, synthesis runs an actual-use marketplace check via `perplexity_research` (see `phase2-execution.md` Step 3.5, which also asks for the owner's enforcement record), scoped to owner + mark + the goods/field, and **records the result inline**. Any finding whose mitigant turns on the *absence* of use (non-use revocation/cancellation, "not in actual use", "no marketplace use found", "owner's use unknown") MUST carry a `- **Use-check source:** <URL | "perplexity_research — no result">` line — the driver's `narrative` validator rejects the narrative otherwise (spec 11). A use-negative is sayable **only** when a search produced it; an inference from the owner's profile ("a music act → probably no game use") is **not** a searched result and must be either run or labelled an inference, never dressed as a clean negative.
|
|
455
455
|
|
|
456
456
|
## Own-rights evidence — mandatory when reasoning relies on the client's house mark (spec-v3 A4)
|
|
457
457
|
|
|
458
458
|
The same enforced contract applies to the client's OWN rights: if a finding's clearance reasoning relies on the client's house mark or franchise root ("the prefix is the client's own registered mark"), synthesis runs ONE own-portfolio check over the run's FROZEN register material — `band_lookup` with `owner:<the applicant/house-mark owner>` (plus `band_record` for any registration cited) — and the finding MUST end with a `- **Own-rights source:** <record URI(s) | "no applicant-owned registrations in the searched register material">` line — the driver's `narrative` validator rejects the narrative otherwise. **Know what the negative means:** the frozen material covers only this matter's dispatched slices (its classes, its mark texts), so an empty owner lookup is NEVER evidence the client's registrations do not exist — a house mark's registrations usually live in the client's home classes under mark text no slice covers. Never write "no registrations found" or any portfolio-wide register-negative: state the honest scoped negative, let the reasoning stand or fall without the crutch, and record the un-run owner query (owner + house root + its home classes/key jurisdictions) as an open Coverage/open-item row so the escalation lane can propose it through the supplemental mint — the same treatment as any instructed check the frozen material cannot answer. The affiliate-exclusion mandate governs *conflicts* (never flag the client against itself); it does **not** suppress this *supporting-evidence* check — those are different things.
|
|
459
459
|
|
|
460
|
-
**The staff lawyer's rule:** Any use of a registered trademark that poses medium risk or higher (any band
|
|
460
|
+
**The staff lawyer's rule:** Any use of a registered trademark that poses medium risk or higher (any band the framework rates medium risk or higher, whatever its position on the ladder) is relevant. The actual-use check applies **after** the preliminary legal read is made — not before. The correct sequence is:
|
|
461
461
|
|
|
462
462
|
1. **Stage 1 — Legal claim strength, net of merits defences:** Make the legal read as the probability the claim prevails, stated in plain likelihood words — the whole-mark confusion read, **net of the credible merits defences** named in the per-finding structure (descriptiveness / weak senior mark, the opponent's own undermining conduct, the client's fair-use defence, priority). Whether **our actual use meets theirs in the market** (real commercial / manner-of-use / channel overlap) IS part of this Stage-1 read — it is the goods-half of likelihood-of-confusion and it sets the read: our use being a descriptive / in-product label vs their standalone source-brand, or the two sitting in a genuinely different commercial field, is a merits reason the claim may not prevail → the **client-favoured read**. **The basis for "theirs" splits by right type:** a **registered** senior meets us on the mark and G&S **as registered** (read the specification; quote it when it decides the point) — never on the owner's current presentation; a **common-law** senior meets us on its **actual trade**. OUR side of the meeting stays our own actual/intended manner of use (per the request form) — that lever is untouched. What stays in Stage 2 is the *senior's enforcement reality* — is the mark actually used, revocable for non-use, will the owner bother, how the owner currently dresses or positions it in the market — **NOT** whether the uses meet. (Merits defences are claim-strength, not practical mitigation; they belong in the legal read.)
|
|
463
463
|
2. **Stage 2 — Practical risk mitigation:** For every hit whose legal read reaches "more likely than not they'd win" or worse, apply the practical mitigants — actual use, revocation-vulnerability, owner enforcement appetite, coexistence. These are the **business read**. **Response availability is Stage 2 at most:** that a route exists — coexistence, consent, rebrand, a cancellation lever — never enters the Stage-1 Legal position; mitigation-availability speaks only through the framework's own **Practical position** words for the band it belongs to.
|