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
package/driver/register-plan.mjs
CHANGED
|
@@ -2079,7 +2079,7 @@ export function foldSupplementalEntries(plan, entries) {
|
|
|
2079
2079
|
const eTerm = String(e.term ?? e.terms?.[0] ?? "");
|
|
2080
2080
|
if (inBatch.has(e.qid) && inBatch.get(e.qid) === eTerm) continue; // the same row twice — one refusal
|
|
2081
2081
|
if (inBatch.has(e.qid)) {
|
|
2082
|
-
refused.push({ qid: e.qid, term: String(e.term ?? e.terms?.[0] ?? ""),
|
|
2082
|
+
refused.push({ qid: e.qid, term: String(e.term ?? e.terms?.[0] ?? ""), kind: "identity-collision",
|
|
2083
2083
|
issue: planRowRefusal({ qid: e.qid, issue:
|
|
2084
2084
|
`a different term ("${inBatch.get(e.qid)}") already minted this identity in the same batch, so `
|
|
2085
2085
|
+ `one of the two would be dropped with nothing recorded. Two DIFFERENT terms sharing one qid is `
|
|
@@ -2098,7 +2098,7 @@ export function foldSupplementalEntries(plan, entries) {
|
|
|
2098
2098
|
e = kept;
|
|
2099
2099
|
const issues = entryTermIssues(e);
|
|
2100
2100
|
if (issues.length) {
|
|
2101
|
-
refused.push({ qid: e.qid, term: String(e.term ?? e.terms?.[0] ?? ""),
|
|
2101
|
+
refused.push({ qid: e.qid, term: String(e.term ?? e.terms?.[0] ?? ""), kind: "malformed-term",
|
|
2102
2102
|
issue: planRowRefusal({ qid: e.qid, issue: issues[0].issue }) });
|
|
2103
2103
|
continue;
|
|
2104
2104
|
}
|
|
@@ -2107,7 +2107,11 @@ export function foldSupplementalEntries(plan, entries) {
|
|
|
2107
2107
|
const key = entryQuestionKey(e, plan);
|
|
2108
2108
|
const twin = key ? asked.get(key) : undefined;
|
|
2109
2109
|
if (twin) {
|
|
2110
|
-
|
|
2110
|
+
// THE TWIN IS A FIELD, NOT ONLY A PHRASE IN THE PROSE. This row is the only place that knows the
|
|
2111
|
+
// question is already asked, and by which plan row. A reader could parse it back out of the
|
|
2112
|
+
// sentence; a caller cannot be asked to. The remedy accounting reads it to tell a duplicate
|
|
2113
|
+
// refusal (answered elsewhere) from the other two kinds (genuinely unasked).
|
|
2114
|
+
refused.push({ qid: e.qid, term: String(e.term ?? e.terms?.[0] ?? ""), kind: "duplicate-question", twin,
|
|
2111
2115
|
issue: planRowRefusal({ qid: e.qid, issue:
|
|
2112
2116
|
`it asks the same question as plan row "${twin}" once regions are resolved (an entry with no `
|
|
2113
2117
|
+ `regions of its own inherits the plan's, so an empty list is the WIDEST scope, not a narrower `
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
|
+
//
|
|
4
|
+
// register-served.mjs — which register actually served this run, as a field in the run's own record.
|
|
5
|
+
//
|
|
6
|
+
// THE KNOCKOUT LANE ALREADY RECORDS THIS and the clearance lane recorded nothing equivalent, so the same
|
|
7
|
+
// question about a clearance run had no answer to look up. What stood in for the field was searching for a
|
|
8
|
+
// register's NAME as a substring inside three artifacts that happen to mention it — and measured on the
|
|
9
|
+
// archived runs that instrument could not attribute more than half of two scenarios' runs to any register
|
|
10
|
+
// at all. Those are runs whose register is unknown to the record, not runs that used none, and nothing in
|
|
11
|
+
// the record said which.
|
|
12
|
+
//
|
|
13
|
+
// WHAT SERVED, NOT WHAT WAS CONFIGURED. The value is taken from the same resolver the dispatch itself goes
|
|
14
|
+
// through, at the moment a register is used, rather than from the frozen settings the run started on. The
|
|
15
|
+
// per-run settings sidecar is deliberately NOT the home for it: that file exists to freeze what the run
|
|
16
|
+
// was sold, so a resume runs on what it started on, and a register recorded there would be the launch
|
|
17
|
+
// value presented with a frozen file's authority. A run whose provider changed under it would read wrong
|
|
18
|
+
// and confident, which is the failure this field exists to end rather than to relocate.
|
|
19
|
+
//
|
|
20
|
+
// A LIST, BECAUSE ONE IS A LIST OF ONE. A run that used two registers says two rather than naming the
|
|
21
|
+
// first, and a reader never has to know whether the field is a value or a set.
|
|
22
|
+
//
|
|
23
|
+
// BEST-EFFORT AND SILENT, like every other telemetry write in this lane: a field that cannot be recorded
|
|
24
|
+
// must never fail a register call. An absence is then the honest answer — this run's register is unknown
|
|
25
|
+
// to the record — which is exactly the state the archive is full of and the reason nothing here guesses.
|
|
26
|
+
import { readRunStatus, writeRunStatus } from "./progress.mjs";
|
|
27
|
+
|
|
28
|
+
/** The status key. One name, so the writer and every reader cannot drift. */
|
|
29
|
+
export const REGISTERS_SERVED = "registersServed";
|
|
30
|
+
|
|
31
|
+
// Per-process memo so the common case — every call of a run resolving the same register — costs one read
|
|
32
|
+
// and one write for the whole run rather than one of each per call. Keyed by run directory because a
|
|
33
|
+
// process can carry more than one (a resume, a batch).
|
|
34
|
+
const seen = new Map();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Note that `id` served this run, and return the run's full list. Writes only when the set changes.
|
|
38
|
+
* `id` is the resolver's own answer, never a raw environment read. PURE apart from the run's record.
|
|
39
|
+
*/
|
|
40
|
+
export function noteRegisterServed(runDir, id) {
|
|
41
|
+
const register = String(id ?? "").trim().toLowerCase();
|
|
42
|
+
if (!runDir || !register) return null;
|
|
43
|
+
let set = seen.get(runDir);
|
|
44
|
+
if (!set) {
|
|
45
|
+
// A RESUME READS WHAT THE EARLIER PROCESS RECORDED, so a run that changed register across a resume
|
|
46
|
+
// says both rather than only the half this process saw.
|
|
47
|
+
set = new Set(registersServedFrom(readRunStatus(runDir)));
|
|
48
|
+
seen.set(runDir, set);
|
|
49
|
+
}
|
|
50
|
+
if (set.has(register)) return [...set];
|
|
51
|
+
set.add(register);
|
|
52
|
+
const registers = [...set];
|
|
53
|
+
try { writeRunStatus(null, { [REGISTERS_SERVED]: registers }, runDir); }
|
|
54
|
+
catch { /* telemetry must never break a register call */ }
|
|
55
|
+
return registers;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The registers a run's status records, or [] when it records none. PURE. */
|
|
59
|
+
export function registersServedFrom(status) {
|
|
60
|
+
const v = status?.[REGISTERS_SERVED];
|
|
61
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim()) : [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* One line for a reader, three-valued rather than two: the register, the registers, or the absence NAMED
|
|
66
|
+
* as an absence. "not recorded" is not "none served" and a reader must never have to guess which.
|
|
67
|
+
* PURE.
|
|
68
|
+
*/
|
|
69
|
+
export function registerServedLine(status) {
|
|
70
|
+
const r = registersServedFrom(status);
|
|
71
|
+
if (!r.length) return "NOT RECORDED — this run's record carries no register, which is not the same fact as using none";
|
|
72
|
+
return r.length === 1 ? r[0] : `${r.length} registers served this run: ${r.join(", ")}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The caveat a whole-run provider tally needs when the run served more than one register: `{}` when the
|
|
77
|
+
* tally's single key cannot be wrong, and the spanning list when it can. PURE.
|
|
78
|
+
*
|
|
79
|
+
* A TALLY'S KEY IS RESOLVED AT PUBLISH and the tally spans the whole run, so on a run whose register
|
|
80
|
+
* changed part-way every call is filed under whichever was active at the end. That is the shape the
|
|
81
|
+
* served field exists to end, and until the field existed nothing could contradict it. This does not
|
|
82
|
+
* split the tally — attributing a whole-run count to one of two registers is a different question — it
|
|
83
|
+
* stops the two register fields in one record disagreeing in silence.
|
|
84
|
+
*/
|
|
85
|
+
export function providerUsageCaveat(served) {
|
|
86
|
+
const r = Array.isArray(served) ? served.filter((x) => typeof x === "string" && x.trim()) : [];
|
|
87
|
+
return r.length > 1 ? { providerUsageSpans: r } : {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Test seam: forget the per-process memo, so an arm can drive a second run in the same process. */
|
|
91
|
+
export function forgetRegistersServed() { seen.clear(); }
|
|
@@ -136,15 +136,40 @@ function material(block) {
|
|
|
136
136
|
* `unaccounted` when there is no reason either — a legacy receipt states neither, and inventing
|
|
137
137
|
* "not dispatched" from silence would be the same guess in a new place);
|
|
138
138
|
* 2. ANY slice that did not land clean ⇒ `dispatch-failed`, naming the first such qid. Every slice
|
|
139
|
-
* must land, exactly as verifyRegisterDirectiveClose requires of a directive — one convention
|
|
139
|
+
* must land, exactly as verifyRegisterDirectiveClose requires of a directive — one convention. A
|
|
140
|
+
* slice the FOLD refused as already-asked is read through the twin it duplicates first, because that
|
|
141
|
+
* slice cannot land and the twin is where its question was answered;
|
|
140
142
|
* 3. all landed, ANY slice material ⇒ `found`;
|
|
141
143
|
* 4. all landed, EVERY slice a counted zero ⇒ `searched-empty`;
|
|
142
144
|
* 5. otherwise ⇒ `unaccounted`, with the shape that defeated the join stated.
|
|
143
145
|
* PURE.
|
|
144
146
|
*/
|
|
145
|
-
export function classifyRemedyTerm(row, { blocksByQid = new Map(), executedQids = new Set() } = {}) {
|
|
147
|
+
export function classifyRemedyTerm(row, { blocksByQid = new Map(), executedQids = new Set(), foldRefusals = new Map() } = {}) {
|
|
146
148
|
const qids = (Array.isArray(row?.qids) ? row.qids : []).filter(Boolean);
|
|
147
|
-
|
|
149
|
+
// — A SLICE THE FOLD REFUSED AS ALREADY-ASKED IS ANSWERED BY THE ROW IT DUPLICATES.
|
|
150
|
+
//
|
|
151
|
+
// The fold refuses a minted qid when the plan already holds a row asking the same question, and hands
|
|
152
|
+
// back the twin's qid. Such a slice can NEVER land: it was never frozen into the plan, and the reopen's
|
|
153
|
+
// own re-attempt filters it out on purpose. Requiring it to land anyway made rule 2 below fire on it and
|
|
154
|
+
// return before rules 3-5 could look at the rest, so a term whose question HAD been asked and answered
|
|
155
|
+
// was recorded `dispatch-failed` — which reads as an engine fault a re-run would fix, where the truth is
|
|
156
|
+
// that the coverage is already on the plan's own row. Measured on a delivered breadth run: one term, its
|
|
157
|
+
// twin enumerated with 71 records, the dominant-element gap left open and clamped on that basis.
|
|
158
|
+
//
|
|
159
|
+
// ONLY THE DUPLICATE KIND RESOLVES. An identity collision or a malformed term is a genuine fault and
|
|
160
|
+
// stays `dispatch-failed`: nothing else asked those questions, so nothing else answers them.
|
|
161
|
+
const resolve = (q) => {
|
|
162
|
+
const r = foldRefusals.get?.(q);
|
|
163
|
+
return r && r.kind === "duplicate-question" && r.twin ? String(r.twin) : q;
|
|
164
|
+
};
|
|
165
|
+
const landsAs = new Map(qids.map((q) => [q, resolve(q)]));
|
|
166
|
+
const blockFor = (q) => blocksByQid.get(landsAs.get(q) ?? q);
|
|
167
|
+
const ranAs = (q) => executedQids.has(landsAs.get(q) ?? q);
|
|
168
|
+
const slices = qids.map((q) => {
|
|
169
|
+
const via = landsAs.get(q);
|
|
170
|
+
const slice = sliceRow(q, blockFor(q), ranAs(q));
|
|
171
|
+
return via && via !== q ? { ...slice, answered_by: via, refused_at_fold: "duplicate-question" } : slice;
|
|
172
|
+
});
|
|
148
173
|
if (!qids.length) {
|
|
149
174
|
const reason = String(row?.dispatch_reason ?? "").trim();
|
|
150
175
|
return reason
|
|
@@ -152,16 +177,16 @@ export function classifyRemedyTerm(row, { blocksByQid = new Map(), executedQids
|
|
|
152
177
|
: { class: "unaccounted", basis: "no-mapping", reason: "the receipt records no slice and no reason for this term — the join cannot say whether it was searched", slices };
|
|
153
178
|
}
|
|
154
179
|
for (const q of qids) {
|
|
155
|
-
const b =
|
|
156
|
-
if (sliceLanded(b,
|
|
157
|
-
const why = !
|
|
180
|
+
const b = blockFor(q);
|
|
181
|
+
if (sliceLanded(b, ranAs(q))) continue;
|
|
182
|
+
const why = !ranAs(q) ? "slice-not-landed"
|
|
158
183
|
: !b ? "no-band-block"
|
|
159
184
|
: b.deferred === true ? "capability-gap-deferral"
|
|
160
185
|
: b.error === true ? "provider-error"
|
|
161
186
|
: "collapsed-slice";
|
|
162
187
|
return { class: "dispatch-failed", basis: why, reason: `${why}:${q}${b?.reason ? ` — ${clip(b.reason, 200)}` : ""}`, slices };
|
|
163
188
|
}
|
|
164
|
-
const blocks = qids.map((q) =>
|
|
189
|
+
const blocks = qids.map((q) => blockFor(q));
|
|
165
190
|
if (blocks.some(material)) return { class: "found", basis: "band-block", reason: null, slices };
|
|
166
191
|
if (blocks.every(countedZero)) return { class: "searched-empty", basis: "counted-zero", reason: null, slices };
|
|
167
192
|
const odd = blocks.find((b) => !countedZero(b));
|
|
@@ -186,14 +211,18 @@ const emptyTotals = () => {
|
|
|
186
211
|
* `out_of_scope` carries the directives that have no term unit (source-layer channels), with the reason
|
|
187
212
|
* — counted and named, never silently absent.
|
|
188
213
|
*
|
|
214
|
+
* `foldRefusals` maps a minted qid the FOLD refused to `{ kind, twin }`, as `foldSupplementalEntries`
|
|
215
|
+
* recorded it. It is how a term whose slice was refused as already-asked reads through the plan row that
|
|
216
|
+
* answered it instead of counting as a slice that failed to land.
|
|
217
|
+
*
|
|
189
218
|
* Deterministic; no timestamps (the caller stamps `ts`), no IO, no judgment. PURE.
|
|
190
219
|
*/
|
|
191
|
-
export function accountRemedyTerms({ terms = [], blocksByQid = new Map(), executedQids = new Set(), outOfScope = [] } = {}) {
|
|
220
|
+
export function accountRemedyTerms({ terms = [], blocksByQid = new Map(), executedQids = new Set(), foldRefusals = new Map(), outOfScope = [] } = {}) {
|
|
192
221
|
const totals = emptyTotals();
|
|
193
222
|
const byDirective = {};
|
|
194
223
|
const rows = [];
|
|
195
224
|
for (const t of Array.isArray(terms) ? terms : []) {
|
|
196
|
-
const c = classifyRemedyTerm(t, { blocksByQid, executedQids });
|
|
225
|
+
const c = classifyRemedyTerm(t, { blocksByQid, executedQids, foldRefusals });
|
|
197
226
|
const directive = String(t?.directive ?? "");
|
|
198
227
|
totals.terms++;
|
|
199
228
|
totals[key(c.class)]++;
|