clearotron 0.4.0-beta.2 → 0.4.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +5 -5
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +9 -0
- package/driver/coverage-form.mjs +6 -0
- package/driver/coverage-ledger.mjs +14 -2
- package/driver/driver.config.mjs +24 -7
- package/driver/engine/mcp/stdio-server.mjs +12 -1
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +29 -8
- package/driver/pipeline.mjs +58 -8
- package/driver/progress.mjs +6 -1
- package/driver/publish/knockout.mjs +16 -3
- package/driver/publish/render-knockout.mjs +38 -11
- package/driver/record-carry.mjs +16 -0
- package/driver/reference-strip-signatures.mjs +11 -1
- package/driver/register-plan.mjs +7 -3
- package/driver/register-served.mjs +91 -0
- package/driver/remedy-accounting.mjs +38 -9
- package/driver/score-redaction.mjs +389 -20
- package/driver/skills/clearance-register/SKILL.md +0 -8
- package/driver/skills/clearance-register/digest.md +1 -1
- package/driver/skills/clearance-register/providers/corsearch.md +1 -1
- package/driver/skills/clearance-register/register-recipes.md +3 -9
- package/driver/skills/clearance-search/SKILL.md +2 -2
- package/driver/skills/clearance-search/phase2-execution.md +1 -1
- package/driver/skills/knockout-assess/SKILL.md +2 -0
- package/driver/stages-knockout.mjs +22 -0
- package/driver/suite-census.json +120 -18
- package/driver/unit-inventory.mjs +38 -43
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/package.json +1 -1
- package/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +2 -2
- package/providers/jx/src/core.js +0 -1
- package/providers/jx/src/judge.js +0 -1
- package/providers/jx/src/nativeread.js +0 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/signa/src/core.js +37 -14
- package/scripts/e2e-first-time.mjs +12 -4
- package/scripts/e2e-scenario-ops.mjs +25 -2
- package/scripts/e2e.mjs +113 -15
- package/scripts/mint-reference-strip-backlog.mjs +36 -2
- package/scripts/score.mjs +102 -29
- package/shared/identifier-scan.mjs +31 -1
|
@@ -23,6 +23,26 @@ var EXPANSION_MAX = 100000
|
|
|
23
23
|
// characters) so legitimate input is unaffected.
|
|
24
24
|
var EXPANSION_MAX_LENGTH = 4000000
|
|
25
25
|
|
|
26
|
+
// `expand` recurses once per level of brace *nesting* - both when expanding a
|
|
27
|
+
// set's comma members and when re-wrapping a set whose body is a single part.
|
|
28
|
+
// The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
|
|
29
|
+
// level per chained group), which left nesting depth unbounded: about 3,100
|
|
30
|
+
// levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
|
|
31
|
+
// and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
|
|
32
|
+
// will follow nesting. It sits far above any realistic pattern and well below
|
|
33
|
+
// the depth at which the stack runs out.
|
|
34
|
+
var EXPANSION_MAX_DEPTH = 1000
|
|
35
|
+
|
|
36
|
+
// Bash keeps a quirk where a brace group followed by a comma set still expands
|
|
37
|
+
// (`{a},b}`). The parser implements it by rewriting the string and restarting
|
|
38
|
+
// the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
|
|
39
|
+
// full passes over a string that itself grows by one `escClose` sentinel each
|
|
40
|
+
// time - quadratic in `n`, with a ~26x constant from the sentinel's length.
|
|
41
|
+
// 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
|
|
42
|
+
// seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
|
|
43
|
+
// times the scan may restart. Real `{a},b}` input needs a handful.
|
|
44
|
+
var EXPANSION_MAX_REWRITES = 1000
|
|
45
|
+
|
|
26
46
|
function numeric(str) {
|
|
27
47
|
return parseInt(str, 10) == str
|
|
28
48
|
? parseInt(str, 10)
|
|
@@ -46,34 +66,54 @@ function unescapeBraces(str) {
|
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
|
|
69
|
+
// Like `target.push(...items)` but doesn't overflow the stack
|
|
70
|
+
function pushAll(target, items) {
|
|
71
|
+
for (var i = 0; i < items.length; i++) {
|
|
72
|
+
target.push(items[i]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
49
76
|
// Basically just str.split(","), but handling cases
|
|
50
77
|
// where we have nested braced sections, which should be
|
|
51
78
|
// treated as individual members, like {a,{b,c},d}
|
|
52
79
|
function parseCommaParts(str) {
|
|
53
|
-
if (!str)
|
|
54
|
-
return [''];
|
|
55
|
-
|
|
56
80
|
var parts = [];
|
|
57
|
-
var m = balanced('{', '}', str);
|
|
58
81
|
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
// Walk the brace groups iteratively. Recursing on `post` once per group let a
|
|
83
|
+
// chain of them exhaust the stack - the parsing-side counterpart to
|
|
84
|
+
// the `expand` overflow fixed for CVE-2026-14257, and not something `max` or
|
|
85
|
+
// `maxLength` can bound, since it happens before expansion.
|
|
86
|
+
//
|
|
87
|
+
// The part the next chunk continues
|
|
88
|
+
var carry = '';
|
|
61
89
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
var post = m.post;
|
|
65
|
-
var p = pre.split(',');
|
|
90
|
+
for (;;) {
|
|
91
|
+
var m = balanced('{', '}', str);
|
|
66
92
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
93
|
+
if (!m) {
|
|
94
|
+
var tail = str.split(',');
|
|
95
|
+
tail[0] = carry + tail[0];
|
|
96
|
+
pushAll(parts, tail);
|
|
97
|
+
return parts;
|
|
98
|
+
}
|
|
73
99
|
|
|
74
|
-
|
|
100
|
+
var pre = m.pre;
|
|
101
|
+
var body = m.body;
|
|
102
|
+
var post = m.post;
|
|
103
|
+
var p = pre.split(',');
|
|
75
104
|
|
|
76
|
-
|
|
105
|
+
p[0] = carry + p[0];
|
|
106
|
+
p[p.length-1] += '{' + body + '}';
|
|
107
|
+
|
|
108
|
+
if (!post.length) {
|
|
109
|
+
pushAll(parts, p);
|
|
110
|
+
return parts;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
carry = p.pop();
|
|
114
|
+
pushAll(parts, p);
|
|
115
|
+
str = post;
|
|
116
|
+
}
|
|
77
117
|
}
|
|
78
118
|
|
|
79
119
|
function expandTop(str, options) {
|
|
@@ -83,6 +123,8 @@ function expandTop(str, options) {
|
|
|
83
123
|
options = options || {};
|
|
84
124
|
var max = options.max == null ? EXPANSION_MAX : options.max;
|
|
85
125
|
var maxLength = options.maxLength == null ? EXPANSION_MAX_LENGTH : options.maxLength;
|
|
126
|
+
var maxDepth = options.maxDepth == null ? EXPANSION_MAX_DEPTH : options.maxDepth;
|
|
127
|
+
var maxRewrites = options.maxRewrites == null ? EXPANSION_MAX_REWRITES : options.maxRewrites;
|
|
86
128
|
|
|
87
129
|
// I don't know why Bash 4.3 does this, but it does.
|
|
88
130
|
// Anything starting with {} will have the first two bytes preserved
|
|
@@ -94,7 +136,7 @@ function expandTop(str, options) {
|
|
|
94
136
|
str = '\\{\\}' + str.substr(2);
|
|
95
137
|
}
|
|
96
138
|
|
|
97
|
-
return expand(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
|
|
139
|
+
return expand(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
|
|
98
140
|
}
|
|
99
141
|
|
|
100
142
|
function identity(e) {
|
|
@@ -218,8 +260,18 @@ function expand(
|
|
|
218
260
|
str,
|
|
219
261
|
max,
|
|
220
262
|
maxLength,
|
|
263
|
+
maxDepth,
|
|
264
|
+
depth,
|
|
265
|
+
maxRewrites,
|
|
221
266
|
isTop
|
|
222
267
|
) {
|
|
268
|
+
// Too deeply nested to keep following: treat the rest as literal, the same
|
|
269
|
+
// way a group that cannot expand is already handled. Truncating rather than
|
|
270
|
+
// throwing keeps expansion total, matching `max` and `maxLength`.
|
|
271
|
+
if (depth > maxDepth) {
|
|
272
|
+
return [str];
|
|
273
|
+
}
|
|
274
|
+
|
|
223
275
|
// Consume the string's top-level brace groups left to right, threading a
|
|
224
276
|
// running set of combined prefixes (`acc`). Expanding the tail iteratively -
|
|
225
277
|
// rather than recursing on `m.post` once per group - keeps the native stack
|
|
@@ -240,6 +292,9 @@ function expand(
|
|
|
240
292
|
// `accBase[a]` records how much of `acc[a]` predates the current run;
|
|
241
293
|
// `combine` treats an expansion as empty when it adds nothing past that.
|
|
242
294
|
var accBase = [0]
|
|
295
|
+
// How many times the `{a},b}` rewrite below has restarted the scan. Each pass
|
|
296
|
+
// re-reads the whole string, so leaving this unbounded is quadratic.
|
|
297
|
+
var rewrites = 0
|
|
243
298
|
var dropEmpties = false
|
|
244
299
|
var firstGroup = true
|
|
245
300
|
var nextBase
|
|
@@ -271,7 +326,8 @@ function expand(
|
|
|
271
326
|
var isOptions = m.body.indexOf(',') >= 0;
|
|
272
327
|
if (!isSequence && !isOptions) {
|
|
273
328
|
// {a},b}
|
|
274
|
-
if (m.post.match(/,(?!,).*\}/)) {
|
|
329
|
+
if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
|
|
330
|
+
rewrites++;
|
|
275
331
|
str = m.pre + '{' + m.body + escClose + m.post;
|
|
276
332
|
// The rewritten string is expanded as if it were a fresh top-level one,
|
|
277
333
|
// so start a new empty-drop run: anchor the baseline at what `acc`
|
|
@@ -310,7 +366,7 @@ function expand(
|
|
|
310
366
|
var n = parseCommaParts(m.body);
|
|
311
367
|
if (n.length === 1 && n[0] !== undefined) {
|
|
312
368
|
// x{{a,b}}y ==> x{a}y x{b}y
|
|
313
|
-
n = expand(n[0], max, maxLength, false).map(embrace);
|
|
369
|
+
n = expand(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
|
|
314
370
|
//XXX is this necessary? Can't seem to hit it in tests.
|
|
315
371
|
/* c8 ignore start */
|
|
316
372
|
if (n.length === 1) {
|
|
@@ -349,7 +405,7 @@ function expand(
|
|
|
349
405
|
values = []
|
|
350
406
|
var valuesLength = 0
|
|
351
407
|
outer: for (var j = 0; j < n.length; j++) {
|
|
352
|
-
var expanded = expand(n[j], max, maxLength, false)
|
|
408
|
+
var expanded = expand(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false)
|
|
353
409
|
for (var k = 0; k < expanded.length; k++) {
|
|
354
410
|
var v = expanded[k]
|
|
355
411
|
if (dropsEmpties && !v) continue
|
|
@@ -22,6 +22,26 @@ var EXPANSION_MAX = 100000
|
|
|
22
22
|
// characters) so legitimate input is unaffected.
|
|
23
23
|
var EXPANSION_MAX_LENGTH = 4000000
|
|
24
24
|
|
|
25
|
+
// `expand` recurses once per level of brace *nesting* - both when expanding a
|
|
26
|
+
// set's comma members and when re-wrapping a set whose body is a single part.
|
|
27
|
+
// The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
|
|
28
|
+
// level per chained group), which left nesting depth unbounded: about 3,100
|
|
29
|
+
// levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
|
|
30
|
+
// and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
|
|
31
|
+
// will follow nesting. It sits far above any realistic pattern and well below
|
|
32
|
+
// the depth at which the stack runs out.
|
|
33
|
+
var EXPANSION_MAX_DEPTH = 1000
|
|
34
|
+
|
|
35
|
+
// Bash keeps a quirk where a brace group followed by a comma set still expands
|
|
36
|
+
// (`{a},b}`). The parser implements it by rewriting the string and restarting
|
|
37
|
+
// the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
|
|
38
|
+
// full passes over a string that itself grows by one `escClose` sentinel each
|
|
39
|
+
// time - quadratic in `n`, with a ~26x constant from the sentinel's length.
|
|
40
|
+
// 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
|
|
41
|
+
// seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
|
|
42
|
+
// times the scan may restart. Real `{a},b}` input needs a handful.
|
|
43
|
+
var EXPANSION_MAX_REWRITES = 1000
|
|
44
|
+
|
|
25
45
|
function numeric(str) {
|
|
26
46
|
return parseInt(str, 10) == str
|
|
27
47
|
? parseInt(str, 10)
|
|
@@ -45,34 +65,54 @@ function unescapeBraces(str) {
|
|
|
45
65
|
}
|
|
46
66
|
|
|
47
67
|
|
|
68
|
+
// Like `target.push(...items)` but doesn't overflow the stack
|
|
69
|
+
function pushAll(target, items) {
|
|
70
|
+
for (var i = 0; i < items.length; i++) {
|
|
71
|
+
target.push(items[i]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
48
75
|
// Basically just str.split(","), but handling cases
|
|
49
76
|
// where we have nested braced sections, which should be
|
|
50
77
|
// treated as individual members, like {a,{b,c},d}
|
|
51
78
|
function parseCommaParts(str) {
|
|
52
|
-
if (!str)
|
|
53
|
-
return [''];
|
|
54
|
-
|
|
55
79
|
var parts = [];
|
|
56
|
-
var m = balanced('{', '}', str);
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
// Walk the brace groups iteratively. Recursing on `post` once per group let a
|
|
82
|
+
// chain of them exhaust the stack - the parsing-side counterpart to
|
|
83
|
+
// the `expand` overflow fixed for CVE-2026-14257, and not something `max` or
|
|
84
|
+
// `maxLength` can bound, since it happens before expansion.
|
|
85
|
+
//
|
|
86
|
+
// The part the next chunk continues
|
|
87
|
+
var carry = '';
|
|
60
88
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
var post = m.post;
|
|
64
|
-
var p = pre.split(',');
|
|
89
|
+
for (;;) {
|
|
90
|
+
var m = balanced('{', '}', str);
|
|
65
91
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
if (!m) {
|
|
93
|
+
var tail = str.split(',');
|
|
94
|
+
tail[0] = carry + tail[0];
|
|
95
|
+
pushAll(parts, tail);
|
|
96
|
+
return parts;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var pre = m.pre;
|
|
100
|
+
var body = m.body;
|
|
101
|
+
var post = m.post;
|
|
102
|
+
var p = pre.split(',');
|
|
103
|
+
|
|
104
|
+
p[0] = carry + p[0];
|
|
105
|
+
p[p.length-1] += '{' + body + '}';
|
|
72
106
|
|
|
73
|
-
|
|
107
|
+
if (!post.length) {
|
|
108
|
+
pushAll(parts, p);
|
|
109
|
+
return parts;
|
|
110
|
+
}
|
|
74
111
|
|
|
75
|
-
|
|
112
|
+
carry = p.pop();
|
|
113
|
+
pushAll(parts, p);
|
|
114
|
+
str = post;
|
|
115
|
+
}
|
|
76
116
|
}
|
|
77
117
|
|
|
78
118
|
function expandTop(str, options) {
|
|
@@ -82,6 +122,8 @@ function expandTop(str, options) {
|
|
|
82
122
|
options = options || {};
|
|
83
123
|
var max = options.max == null ? EXPANSION_MAX : options.max;
|
|
84
124
|
var maxLength = options.maxLength == null ? EXPANSION_MAX_LENGTH : options.maxLength;
|
|
125
|
+
var maxDepth = options.maxDepth == null ? EXPANSION_MAX_DEPTH : options.maxDepth;
|
|
126
|
+
var maxRewrites = options.maxRewrites == null ? EXPANSION_MAX_REWRITES : options.maxRewrites;
|
|
85
127
|
|
|
86
128
|
// I don't know why Bash 4.3 does this, but it does.
|
|
87
129
|
// Anything starting with {} will have the first two bytes preserved
|
|
@@ -93,7 +135,7 @@ function expandTop(str, options) {
|
|
|
93
135
|
str = '\\{\\}' + str.substr(2);
|
|
94
136
|
}
|
|
95
137
|
|
|
96
|
-
return expand(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
|
|
138
|
+
return expand(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
|
|
97
139
|
}
|
|
98
140
|
|
|
99
141
|
function embrace(str) {
|
|
@@ -205,8 +247,18 @@ function expand(
|
|
|
205
247
|
str,
|
|
206
248
|
max,
|
|
207
249
|
maxLength,
|
|
250
|
+
maxDepth,
|
|
251
|
+
depth,
|
|
252
|
+
maxRewrites,
|
|
208
253
|
isTop
|
|
209
254
|
) {
|
|
255
|
+
// Too deeply nested to keep following: treat the rest as literal, the same
|
|
256
|
+
// way a group that cannot expand is already handled. Truncating rather than
|
|
257
|
+
// throwing keeps expansion total, matching `max` and `maxLength`.
|
|
258
|
+
if (depth > maxDepth) {
|
|
259
|
+
return [str];
|
|
260
|
+
}
|
|
261
|
+
|
|
210
262
|
// Consume the string's top-level brace groups left to right, threading a
|
|
211
263
|
// running set of combined prefixes (`acc`). Expanding the tail iteratively -
|
|
212
264
|
// rather than recursing on `m.post` once per group - keeps the native stack
|
|
@@ -219,6 +271,9 @@ function expand(
|
|
|
219
271
|
// comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
|
|
220
272
|
// is on the final strings, so it is applied to whichever `combine` produces
|
|
221
273
|
// them (the one with no brace set left in the tail).
|
|
274
|
+
// How many times the `{a},b}` rewrite below has restarted the scan. Each pass
|
|
275
|
+
// re-reads the whole string, so leaving this unbounded is quadratic.
|
|
276
|
+
var rewrites = 0
|
|
222
277
|
var dropEmpties = false
|
|
223
278
|
var firstGroup = true
|
|
224
279
|
|
|
@@ -254,7 +309,8 @@ function expand(
|
|
|
254
309
|
var isOptions = m.body.indexOf(',') >= 0;
|
|
255
310
|
if (!isSequence && !isOptions) {
|
|
256
311
|
// {a},b}
|
|
257
|
-
if (m.post.match(/,(?!,).*\}/)) {
|
|
312
|
+
if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
|
|
313
|
+
rewrites++;
|
|
258
314
|
str = m.pre + '{' + m.body + escClose + m.post;
|
|
259
315
|
isTop = true;
|
|
260
316
|
continue;
|
|
@@ -282,7 +338,7 @@ function expand(
|
|
|
282
338
|
var n = parseCommaParts(m.body);
|
|
283
339
|
if (n.length === 1 && n[0] !== undefined) {
|
|
284
340
|
// x{{a,b}}y ==> x{a}y x{b}y
|
|
285
|
-
n = expand(n[0], max, maxLength, false).map(embrace);
|
|
341
|
+
n = expand(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
|
|
286
342
|
//XXX is this necessary? Can't seem to hit it in tests.
|
|
287
343
|
/* c8 ignore start */
|
|
288
344
|
if (n.length === 1) {
|
|
@@ -316,7 +372,7 @@ function expand(
|
|
|
316
372
|
values = []
|
|
317
373
|
var valuesLength = 0
|
|
318
374
|
outer: for (var j = 0; j < n.length; j++) {
|
|
319
|
-
var expanded = expand(n[j], max, maxLength, false)
|
|
375
|
+
var expanded = expand(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false)
|
|
320
376
|
for (var k = 0; k < expanded.length; k++) {
|
|
321
377
|
var v = expanded[k]
|
|
322
378
|
if (dropsEmpties && !v) continue
|
package/package.json
CHANGED
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.4.0-beta.
|
|
5
|
+
"version": "0.4.0-beta.3",
|
|
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": {
|
|
@@ -24,6 +24,6 @@
|
|
|
24
24
|
"@types/react-dom": "^19.3.0",
|
|
25
25
|
"@vitejs/plugin-react": "^6.1.1",
|
|
26
26
|
"typescript": "^7.0.2",
|
|
27
|
-
"vite": "^8.3.
|
|
27
|
+
"vite": "^8.3.1"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/providers/jx/src/core.js
CHANGED
|
@@ -120,7 +120,6 @@ export function buildCandidateRequest({ mark, productContext = "", lane = "zh",
|
|
|
120
120
|
model,
|
|
121
121
|
max_tokens: 2048, // 8 Han candidates + rationales need headroom; truncation is checked either way
|
|
122
122
|
tools: [CANDIDATE_TOOL],
|
|
123
|
-
tool_choice: { type: "tool", name: CANDIDATE_TOOL.name }, // FORCED tool answer; shape validated at parse
|
|
124
123
|
messages: [{ role: "user", content: promptFor({ mark: String(mark).trim(), productContext: String(productContext ?? "").trim() }) }],
|
|
125
124
|
};
|
|
126
125
|
}
|
|
@@ -79,7 +79,6 @@ export function buildJudgeRequest({ mark, hits, model = DEFAULT_MODEL }) {
|
|
|
79
79
|
model,
|
|
80
80
|
max_tokens: 4096, // 40 one-clause judgments fit well inside; truncation checked either way
|
|
81
81
|
tools: [JUDGE_TOOL],
|
|
82
|
-
tool_choice: { type: "tool", name: JUDGE_TOOL.name }, // FORCED tool answer; shape validated at parse
|
|
83
82
|
messages: [{ role: "user", content: prompt }],
|
|
84
83
|
};
|
|
85
84
|
}
|
|
@@ -81,7 +81,6 @@ export function buildNativereadRequest({ mark, lane = "zh", payload, model = DEF
|
|
|
81
81
|
model,
|
|
82
82
|
max_tokens: 4096, // 12 grounded items with grounds quotes need headroom; truncation checked either way
|
|
83
83
|
tools: [NATIVEREAD_TOOL],
|
|
84
|
-
tool_choice: { type: "tool", name: NATIVEREAD_TOOL.name }, // FORCED tool answer; shape validated at parse
|
|
85
84
|
messages: [{ role: "user", content: prompt }],
|
|
86
85
|
};
|
|
87
86
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trademark-oauth-mcp-bridge",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.3",
|
|
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).",
|
|
@@ -319,7 +319,14 @@ export function normalizeRecord(rec, officeHint = null) {
|
|
|
319
319
|
registrationNumber: rec.registration_number ?? rec.ir_number ?? null,
|
|
320
320
|
applicationDate: toIso(rec.filing_date),
|
|
321
321
|
registrationDate: toIso(rec.registration_date),
|
|
322
|
-
|
|
322
|
+
// — THE OFFICE'S DATE FIRST, THEN SIGNA'S OWN (their 27 September release). That release split what
|
|
323
|
+
// an office published from what Signa computes: `expiry_date` now carries only the published date and
|
|
324
|
+
// is EMPTY on USPTO records, where the computed one moved to `derived.expiry_date`. Read one and the
|
|
325
|
+
// field goes quietly null on every US record — measured on the record bodies of the run that
|
|
326
|
+
// straddled the release: 4 of 9 US records carried it only under `derived`, and all 4 normalised to
|
|
327
|
+
// null here. Where both are present they are identical (12 of 12 on that run), so the office's value
|
|
328
|
+
// first costs nothing and keeps the published date authoritative when there is one.
|
|
329
|
+
expiryDate: toIso(rec.expiry_date ?? rec.derived?.expiry_date),
|
|
323
330
|
statusClass: statusClassOf(rec), // live | dead | unknown — authoritative for the gates
|
|
324
331
|
statusText: pickStatusText(rec),
|
|
325
332
|
markText: rec.mark_text ?? null,
|
|
@@ -353,16 +360,31 @@ export function normalizeRecord(rec, officeHint = null) {
|
|
|
353
360
|
// "absent from the register", and `nativeScriptIndex` is settled from the record, not from this.
|
|
354
361
|
markTextScript: rec.mark_text_script ?? null,
|
|
355
362
|
markTextLanguage: rec.mark_text_language ?? null,
|
|
356
|
-
|
|
363
|
+
// The same split as `expiryDate` above, and the vendor names both fields in it. UNEXERCISED ON THE
|
|
364
|
+
// DATA WE HOLD: of the 22 record bodies from the run that straddled the release, none carried a
|
|
365
|
+
// renewal date in either place, so the fallback recovers nothing we can point at. It is here because
|
|
366
|
+
// the register says this field moved with expiry, and reading one place is how the expiry loss
|
|
367
|
+
// happened; the arm for it is therefore synthetic and says so.
|
|
368
|
+
renewalDueDate: toIso(rec.renewal_due_date ?? rec.derived?.renewal_due_date),
|
|
357
369
|
publicationDate: toIso(rec.publication_date),
|
|
358
370
|
priorityDate: toIso(rec.priority_date),
|
|
359
371
|
terminationDate: toIso(rec.termination_date),
|
|
360
|
-
// Opposition data IS on the record
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
|
|
372
|
+
// Opposition data IS on the record, and NOT where this used to look. The register reports the window
|
|
373
|
+
// under its computed object, and as an OBJECT rather than a scalar:
|
|
374
|
+
// `{supported, window_opens, window_closes, …}`, or `{supported: false, reason, rule_id}` where the
|
|
375
|
+
// stage cannot be opposed. `proceedings_count` is still the office's own, top-level.
|
|
376
|
+
//
|
|
377
|
+
// THE OLD READ RETURNED NULL ON EVERY RECORD WE HOLD, and the comment here asserted otherwise — that
|
|
378
|
+
// the window "rides every search row". Measured over all four saved runs on this register: 0 of 6,
|
|
379
|
+
// 0 of 5, 0 of 7 and 0 of 22 bodies carried it top-level, and 6, 3, 7 and 22 carried it under the
|
|
380
|
+
// computed object. So the earliest evidence we have already reads the new place, eleven days before
|
|
381
|
+
// the release the register attributes the move to. Which of the two dates is right does not change
|
|
382
|
+
// what to read; it does mean this was not reported missing for as long as it was missing.
|
|
383
|
+
//
|
|
384
|
+
// The per-proceeding detail behind GET /v1/trademarks/{id}/proceedings is still an unwired tool, so
|
|
385
|
+
// the window and the count are reported and the absence of detail is stated rather than left to look
|
|
386
|
+
// like an absence of proceedings.
|
|
387
|
+
oppositionWindow: rec.opposition_window ?? rec.derived?.opposition_window ?? null,
|
|
366
388
|
proceedingsCount: rec.proceedings_count ?? null,
|
|
367
389
|
oppositions: null,
|
|
368
390
|
_provenance: { opposition: rec.proceedings_count != null ? `count=${rec.proceedings_count} from the record; per-proceeding detail via /proceedings, tool not wired` : "per-proceeding detail via /proceedings, tool not wired" },
|
|
@@ -485,8 +507,8 @@ export function normalizeSearchResponse(body, echoQuery) {
|
|
|
485
507
|
//
|
|
486
508
|
// THIS HEADER USED TO ADVERTISE AN ENVIRONMENT VARIABLE, `CLAWDI_SIGNA_MOCK`. There was never such a
|
|
487
509
|
// variable: two occurrences in the whole tree, both comments, zero reads. Following the line produced a
|
|
488
|
-
// run that died at preflight in seconds, and it cost a real investigation
|
|
489
|
-
//
|
|
510
|
+
// run that died at preflight in seconds, and it cost a real investigation — found while looking for a
|
|
511
|
+
// register path that needs no credential, to inject faults into.
|
|
490
512
|
//
|
|
491
513
|
// The lane below is real and works. What does not exist is a way to REACH it from a run: `doSearch`,
|
|
492
514
|
// `doRecordFetch` and `doEnumerate` each take `{ mock = false }`, the capability wrappers pass
|
|
@@ -494,10 +516,11 @@ export function normalizeSearchResponse(body, echoQuery) {
|
|
|
494
516
|
// (driver.config.mjs) call these functions with no options object at all. So the only callers that
|
|
495
517
|
// enable it are this provider's own tests, and that is the whole of it.
|
|
496
518
|
//
|
|
497
|
-
// AND IT STAYS THAT WAY DELIBERATELY, rather than for want of wiring.
|
|
498
|
-
// acceptance that comes with wiring is "preflightCredentials must not demand
|
|
499
|
-
// mock
|
|
500
|
-
// register stage, answering from ten in-repo fixtures. ADR-0003's first
|
|
519
|
+
// AND IT STAYS THAT WAY DELIBERATELY, rather than for want of wiring. Wiring it was offered and
|
|
520
|
+
// declined; the acceptance that comes with wiring is "preflightCredentials must not demand
|
|
521
|
+
// SIGNA_API_KEY when the mock path is on", and that produces a clearance run with no register
|
|
522
|
+
// credential, reaching the register stage, answering from ten in-repo fixtures. ADR-0003's first
|
|
523
|
+
// table row rules exactly that
|
|
501
524
|
// out: the register REFUSES at preflight, by name, because "an unconfigured register that answered
|
|
502
525
|
// 'no conflicts found' is the most dangerous output this system can produce".
|
|
503
526
|
//
|
|
@@ -163,10 +163,18 @@ export const NOT_COUNTED_EVENTS = {
|
|
|
163
163
|
"jx-aim-consumed", "jx-candidate-fold", "jx-nativeread",
|
|
164
164
|
"jx-serp-grid", "jx-serp-grid-overflow", "jx-serp-grid-spec", "jx-slices-stated",
|
|
165
165
|
"knockout-published", "knockout-receipts",
|
|
166
|
-
"knockout-register-records",
|
|
167
|
-
// The
|
|
168
|
-
//
|
|
169
|
-
//
|
|
166
|
+
"knockout-register-records",
|
|
167
|
+
// The run's own span against the ten-minute target, written at delivery because that is the first
|
|
168
|
+
// point at which the run's span is known. Same class as the sweep's line below: a RECORD, never a
|
|
169
|
+
// failure. `overBar: null` on it means the span could not be measured, which is a gap in the record
|
|
170
|
+
// and still not a retry (515).
|
|
171
|
+
"knockout-run-total",
|
|
172
|
+
"knockout-sweep-skipped", "knockout-sweep-start",
|
|
173
|
+
// The sweep's cost line: marks, calls, cells, the largest place list any one name carried, and the
|
|
174
|
+
// sweep's own elapsed minutes. A RECORD, never a failure — nothing about it is a step that went
|
|
175
|
+
// wrong, a question re-asked or an attempt repeated, so it does not bear on "first time" (515).
|
|
176
|
+
// The target moved off this line to the run's own, above: the bar is about a run and this span is
|
|
177
|
+
// one stage of it.
|
|
170
178
|
"knockout-sweep-total", "level-scope-note",
|
|
171
179
|
"named-band-merged", "one-shot-stamp-settled", "order-probe", "output-snapshot", "owner-screen-derived",
|
|
172
180
|
"placement-borderline", "placement-form-written", "plan-execution", "plan-execution-census",
|
|
@@ -282,17 +282,40 @@ export function narrowingNamesItsCrowd(a, runDir) {
|
|
|
282
282
|
const exec = readJson(driverDir(runDir, "plan-execution.json"));
|
|
283
283
|
if (!exec) return { ok: false, saw: "_driver/plan-execution.json absent, so no question's count can be read" };
|
|
284
284
|
const counts = executedCounts(exec);
|
|
285
|
-
|
|
285
|
+
// A QUESTION ASKED OFF THE PLAN IS DISPOSITIONED, NOT OMITTED, and this op had no branch for it.
|
|
286
|
+
//
|
|
287
|
+
// The plan record carries an `unplanned` bucket: the engine asked something the plan did not contain
|
|
288
|
+
// and said so. Such an entry is on the record with a disposition and carries no executed count, and
|
|
289
|
+
// this check used to render that as "carries no count of its own" — which describes a silent omission.
|
|
290
|
+
// On the run that surfaced it, that ONE narrowing was the whole failure: 35 of 36 carried their own
|
|
291
|
+
// count, all 14 crowds were on the record, and neither crowd branch fired.
|
|
292
|
+
//
|
|
293
|
+
// IT IS NAMED, NOT COUNTED AGAINST THE ENGINE. The rule this op states is that the crowd a narrowing
|
|
294
|
+
// replaced is on the record with a count. An off-plan question satisfies the first half and cannot
|
|
295
|
+
// satisfy the second, because no count exists to read — and whether the engine ought to record one for
|
|
296
|
+
// a question it asked off-plan is a question about the RECORD, not about this narrowing. That belongs
|
|
297
|
+
// to the lane that writes the record; this op says what it found and declines to invent a verdict.
|
|
298
|
+
const offPlanQids = new Set(list(exec.unplanned).map((e) => text(e?.qid)).filter(Boolean));
|
|
299
|
+
const crowdMissing = [], crowdUncounted = [], narrowingUncounted = [], narrowingOffPlan = [];
|
|
286
300
|
for (const n of narrowings) {
|
|
287
301
|
const crowd = text(n.narrows);
|
|
288
302
|
if (!byQid.has(crowd)) crowdMissing.push(`${n.qid} → ${crowd}`);
|
|
289
303
|
else if (!counts.has(crowd)) crowdUncounted.push(`${n.qid} → ${crowd}`);
|
|
290
|
-
if (!counts.has(text(n.qid)))
|
|
304
|
+
if (!counts.has(text(n.qid))) {
|
|
305
|
+
if (offPlanQids.has(text(n.qid))) narrowingOffPlan.push(text(n.qid));
|
|
306
|
+
else narrowingUncounted.push(text(n.qid));
|
|
307
|
+
}
|
|
291
308
|
}
|
|
292
309
|
const parts = [`${plural(narrowings.length, "narrowing")} naming ${new Set(narrowings.map((n) => text(n.narrows))).size} crowd(s)`];
|
|
293
310
|
if (crowdMissing.length) parts.push(`${crowdMissing.length} name a crowd that is not on the record${sample(crowdMissing)}`);
|
|
294
311
|
if (crowdUncounted.length) parts.push(`${crowdUncounted.length} name a crowd with no count${sample(crowdUncounted)}`);
|
|
295
312
|
if (narrowingUncounted.length) parts.push(`${narrowingUncounted.length} carry no count of their own${sample(narrowingUncounted)}`);
|
|
313
|
+
// Said whether or not anything failed, because it is the difference between a question the engine
|
|
314
|
+
// never answered and one it asked outside the plan — and a reader cannot tell those apart from a
|
|
315
|
+
// count's absence alone.
|
|
316
|
+
if (narrowingOffPlan.length) parts.push(`${narrowingOffPlan.length} asked OFF THE PLAN: on the record`
|
|
317
|
+
+ ` in plan-execution's \`unplanned\` bucket with a disposition and no executed count, so this check`
|
|
318
|
+
+ ` has no count to read rather than a count that is missing${sample(narrowingOffPlan)}`);
|
|
296
319
|
if (parts.length === 1) parts.push("each crowd and each narrowing on the record with its count");
|
|
297
320
|
return { ok: !crowdMissing.length && !crowdUncounted.length && !narrowingUncounted.length, saw: parts.join("; ") };
|
|
298
321
|
}
|