clearotron 0.3.2 → 0.3.3-beta.0
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/CONTRIBUTING.md +12 -0
- package/INSTALL.md +8 -0
- package/bin/onboard.mjs +29 -7
- package/bin/start.mjs +1 -1
- package/build-info.json +2 -2
- package/demo/MANIFEST.json +27 -0
- package/docs/INTAKE.md +8 -0
- package/docs/architecture/04-configuration-reference.md +1 -1
- package/driver/CHANGELOG.md +37 -0
- package/driver/clearance-variants-record.mjs +12 -1
- package/driver/common-law-coverage-status.mjs +113 -0
- package/driver/contract-audit.mjs +1 -1
- package/driver/contract-e3-backlog.mjs +37 -37
- package/driver/contract-vocabulary.mjs +8 -8
- package/driver/coverage-form-io.mjs +3 -1
- package/driver/coverage-form.mjs +38 -11
- package/driver/coverage-ledger.mjs +37 -7
- package/driver/coverage-union.mjs +2 -2
- package/driver/crowd-context.mjs +19 -6
- package/driver/drainer-identity.mjs +1 -1
- package/driver/engine/mcp/clarivate-server.mjs +4 -2
- package/driver/engine/mcp/corsearch-server.mjs +3 -1
- package/driver/engine/mcp/coverage-server.mjs +1 -1
- package/driver/engine/mcp/dispositions-server.mjs +47 -5
- package/driver/engine/mcp/euipo-server.mjs +2 -0
- package/driver/engine/mcp/free-tier-server.mjs +2 -0
- package/driver/engine/mcp/gather-config.mjs +8 -2
- package/driver/engine/mcp/probe-server.mjs +28 -0
- package/driver/engine/mcp/proposal-fields.mjs +45 -0
- package/driver/engine/mcp/recording-server.mjs +30 -0
- package/driver/engine/mcp/signa-server.mjs +2 -0
- package/driver/engine/mcp/supplemental.mjs +89 -12
- package/driver/engine/mcp/unit-note-server.mjs +50 -0
- package/driver/engine/mcp/uspto-local-server.mjs +2 -0
- package/driver/engine/openai-agent.mjs +7 -0
- package/driver/engine/probe.mjs +67 -14
- package/driver/engine/tool-refusal.mjs +16 -0
- package/driver/enqueue-schema.mjs +2 -2
- package/driver/envelope-settle.mjs +82 -13
- package/driver/findings-model.mjs +4 -4
- package/driver/gateway.mjs +18 -2
- package/driver/manager-groups-verdict.mjs +1 -1
- package/driver/matter-frame-record.mjs +24 -7
- package/driver/named-band.mjs +1 -1
- package/driver/package.json +1 -1
- package/driver/partial-payload-baseline.json +12 -3
- package/driver/pipeline.mjs +141 -37
- package/driver/publish/index.mjs +40 -25
- package/driver/publish/xlsx.mjs +26 -4
- package/driver/queue-markers.mjs +44 -0
- package/driver/queue-watch-verdict.mjs +2 -2
- package/driver/register-availability.mjs +2 -2
- package/driver/register-plan.mjs +313 -21
- package/driver/roster-verdict.mjs +1 -1
- package/driver/runner.mjs +26 -2
- package/driver/skills/clearance-common-law/SKILL.md +2 -0
- package/driver/skills/clearance-register/SKILL.md +44 -3
- package/driver/skills/clearance-register/digest.md +5 -5
- package/driver/skills/clearance-register/providers/clarivate.md +1 -1
- package/driver/skills/clearance-register/unit.md +39 -0
- package/driver/skills/clearance-variants/SKILL.md +1 -1
- package/driver/skills/matter-frame/SKILL.md +4 -2
- package/driver/stages.mjs +12 -5
- package/driver/status-snapshot.mjs +1 -1
- package/driver/suite-census.json +236 -14
- package/driver/synthesis-record.mjs +80 -2
- package/driver/unit-file-drift.mjs +3 -3
- package/driver/unit-inventory.mjs +2 -2
- package/driver/unit-state-verdict.mjs +1 -1
- package/driver/updater-identity.mjs +2 -3
- package/driver/variant-manifest-model.mjs +11 -1
- package/driver/verify.mjs +5 -5
- package/driver/withheld-families.mjs +104 -0
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/lib/brief.mjs +5 -7
- package/mcp-server/lib/runs.mjs +1 -1
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +3 -2
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-DMthc7PQ.js → index-GBbbyQxc.js} +22 -4
- package/portal-ui/dist/index.html +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/_shared/count.mjs +2 -2
- package/providers/_shared/enumerate.mjs +15 -2
- package/providers/_shared/execute-plan.mjs +19 -1
- package/providers/_shared/plan-guards.mjs +40 -0
- package/providers/clarivate/src/capabilities.js +15 -5
- package/providers/clarivate/src/core.js +41 -5
- package/providers/corsearch/src/capabilities.js +4 -0
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/signa/src/capabilities.js +22 -8
- package/providers/signa/src/core.js +12 -1
- package/scripts/demo-evidence.mjs +114 -0
- package/scripts/engine-probe.mjs +6 -5
- package/scripts/env-audit.mjs +1 -1
- package/scripts/freeze-example-run.mjs +3 -3
- package/scripts/live-surface-check.mjs +26 -19
- package/scripts/mint-suite-census.mjs +66 -0
- package/scripts/package-size-budget.mjs +117 -0
- package/scripts/register-plan-shape.mjs +259 -0
- package/scripts/release-note-required.mjs +38 -1
- package/scripts/settings-render-check.mjs +36 -0
- package/scripts/travelling-predicates.mjs +1 -1
- package/shared/identifier-scan.mjs +22 -5
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
// rows before this shape was chosen, and each is wrong on a live row: reading the surface off the file
|
|
63
63
|
// that CONTAINS the anchor calls common-law-half a driver module (connotation-search.mjs authors it;
|
|
64
64
|
// perplexity-server.mjs delivers it), and an MCP import closure calls register-digest's brief a tool
|
|
65
|
-
// response through coverage-server → coverage-tool → coverage-form, when pipeline.mjs
|
|
65
|
+
// response through coverage-server → coverage-tool → coverage-form, when pipeline.mjs coverageFormBrief appends it to
|
|
66
66
|
// the stage message. The finding is precisely that authorship and delivery come apart, so no function of
|
|
67
67
|
// the authored path can decide the answer.
|
|
68
68
|
//
|
|
@@ -106,7 +106,7 @@ export const E3_BACKLOG = [
|
|
|
106
106
|
// Two `literal-json-skeleton` and two `exactly-these-keys`, all four stamped "NOTHING ON THE PLAN
|
|
107
107
|
// REMOVES THIS". The conversion removed them:
|
|
108
108
|
//
|
|
109
|
-
// stages.mjs:
|
|
109
|
+
// stages.mjs:923 the dispatch dictated variant-manifest.json key by key and enum by enum;
|
|
110
110
|
// `record_clearance_variants`'s schema IS that shape now, so the key-set and
|
|
111
111
|
// enum families are unreachable from a typed call rather than caught after
|
|
112
112
|
// the file is written.
|
|
@@ -171,7 +171,7 @@ export const E3_BACKLOG = [
|
|
|
171
171
|
{
|
|
172
172
|
stage: "common-law",
|
|
173
173
|
kind: "dictated-line-shape",
|
|
174
|
-
where: "driver/stages.mjs:
|
|
174
|
+
where: "driver/stages.mjs:1082 (emitted at 1032, 1061, 1147, 1276)",
|
|
175
175
|
surface: "stage-message",
|
|
176
176
|
evidence: "CROSS-CHECK HAND-OFF: … record it on its OWN line in EXACTLY the form \"CROSS-CHECK REQUIRED: <what> — <why>\" (that exact prefix; an em-dash between what and why; name the mark in CAPS in <what>). The driver parses ONLY this exact line shape…",
|
|
177
177
|
reparsedBy: "driver/doubt-ledger.mjs:183 CROSS_CHECK_RE = /^(?:[-*]\\s+)?CROSS-CHECK REQUIRED:\\s*(.+?)\\s+—\\s+(.+?)\\s*$/ → mintCrossCheckDoubts",
|
|
@@ -227,7 +227,7 @@ export const E3_BACKLOG = [
|
|
|
227
227
|
{
|
|
228
228
|
stage: "common-law",
|
|
229
229
|
kind: "dictated-line-shape",
|
|
230
|
-
where: "driver/stages.mjs:
|
|
230
|
+
where: "driver/stages.mjs:1055 and driver/stages.mjs:1060 (the no-grid-spec legacy branch)",
|
|
231
231
|
surface: "stage-message",
|
|
232
232
|
evidence: "GRID KEYS (the validator checks EXACTLY these N terms — use each VERBATIM as its Negative-results matrix key…) … MACHINE RECEIPTS (MANDATORY): save the grid call's stdout JSON VERBATIM … the single stdout object, or a JSON ARRAY of the per-batch stdout objects in batch order when batched.",
|
|
233
233
|
reparsedBy: "driver/common-law-receipts.mjs — the receipts gate's exact identity join on the dictated key list; validators.commonLaw grid-completeness arm",
|
|
@@ -256,7 +256,7 @@ export const E3_BACKLOG = [
|
|
|
256
256
|
{
|
|
257
257
|
stage: "register-unit",
|
|
258
258
|
kind: "literal-json-skeleton",
|
|
259
|
-
where: "driver/stages.mjs:
|
|
259
|
+
where: "driver/stages.mjs:2260 (the non-supplemental-lane branch)",
|
|
260
260
|
surface: "stage-message",
|
|
261
261
|
evidence: "BAND ARTIFACT (MANDATORY): ALSO write the COMPLETE NAMED BAND for this axis to <path> — a JSON ARRAY, one block per register_enumerate / count-probe call, in the named-band contract: {\"state\":\"enumerated\",\"query\":\"<what was searched>\",\"total_hits\":N,\"records\":[{record_id, mark_text, classes, status,",
|
|
262
262
|
reparsedBy: "driver/named-band.mjs parseNamedBand / bandRecords / bandCrowds / mergeNamedBands (named in driver/skills/clearance-register/unit.md:60-62); validators.registerUnit",
|
|
@@ -265,7 +265,7 @@ export const E3_BACKLOG = [
|
|
|
265
265
|
{
|
|
266
266
|
stage: "register-unit",
|
|
267
267
|
kind: "exactly-these-keys",
|
|
268
|
-
where: "driver/stages.mjs:
|
|
268
|
+
where: "driver/stages.mjs:2224",
|
|
269
269
|
surface: "stage-message",
|
|
270
270
|
evidence: "Every block you append MUST carry \"state\":\"enumerated\" (ONLY if you paged it to has_more:false) or \"state\":\"incomplete\" — EXACTLY those two strings; there is no \"verified\"/\"checked\"/\"complete\"/\"clean\" state, and any other value fails the stage.",
|
|
271
271
|
reparsedBy: "driver/named-band.mjs parseNamedBand (off-enum state fails validators.registerUnit)",
|
|
@@ -274,7 +274,7 @@ export const E3_BACKLOG = [
|
|
|
274
274
|
{
|
|
275
275
|
stage: "register-unit",
|
|
276
276
|
kind: "literal-json-skeleton",
|
|
277
|
-
where: "driver/skills/clearance-register/unit.md:
|
|
277
|
+
where: "driver/skills/clearance-register/unit.md:122-136",
|
|
278
278
|
surface: "skill-file",
|
|
279
279
|
evidence: "Two block shapes (no third):\\n```json\\n[\\n { \"state\":\"enumerated\", \"query\":\"…\", \"total_hits\": 12, \"records\": [ { \"record_id\":\"/mark/eu/018…\", \"mark_text\":\"…\", … } ] },\\n { \"state\":\"incomplete\", \"query\":\"…\", \"total_hits\": 2416, \"fetched\": 1, \"sample\":[ … ], \"reason\":\"…\" }\\n]\\n```",
|
|
280
280
|
reparsedBy: "driver/named-band.mjs parseNamedBand — named in the skill file itself at unit.md:60-62",
|
|
@@ -283,7 +283,7 @@ export const E3_BACKLOG = [
|
|
|
283
283
|
{
|
|
284
284
|
stage: "register-unit",
|
|
285
285
|
kind: "exactly-these-keys",
|
|
286
|
-
where: "driver/stages.mjs:
|
|
286
|
+
where: "driver/stages.mjs:4202 (the frame-reopen / scoped-retry message builder). A second number stood here and had been stale for some time: it pointed at a contract-element description rather than a builder, at its old line and at every mechanical shift of it. Two candidate builders sit beside 4157 and picking one would be a guess, so the wrong pointer is removed rather than moved a third time — one accurate citation beats one accurate and one invented.",
|
|
287
287
|
surface: "stage-message",
|
|
288
288
|
evidence: "Every block you append MUST carry \"state\":\"enumerated\" (ONLY if paged to has_more:false) or \"state\":\"incomplete\" — EXACTLY those two strings … (re-dispatch builders, which REPLACE def.message)",
|
|
289
289
|
reparsedBy: "driver/named-band.mjs parseNamedBand. Scope warning: these builders replace def.message on every escalation / envelope-close / frame-reopen dispatch, so an E3 lint that walks STAGES[*].message only never sees them",
|
|
@@ -301,7 +301,7 @@ export const E3_BACKLOG = [
|
|
|
301
301
|
{
|
|
302
302
|
stage: "placement-inquiry",
|
|
303
303
|
kind: "exactly-these-keys",
|
|
304
|
-
where: "driver/stages.mjs:
|
|
304
|
+
where: "driver/stages.mjs:2420",
|
|
305
305
|
surface: "stage-message",
|
|
306
306
|
evidence: "· tier EXACTLY one of headline-candidate / sheet-2 / watchlist-annex / out-of-scope-filtered.",
|
|
307
307
|
reparsedBy: "driver/placement-form.mjs / driver/placement-model.mjs via validators.placement",
|
|
@@ -313,7 +313,7 @@ export const E3_BACKLOG = [
|
|
|
313
313
|
where: "driver/skills/placement-inquiry/SKILL.md:58-66",
|
|
314
314
|
surface: "skill-file",
|
|
315
315
|
evidence: "**2. The structured mirror** `…/placements.json` … `{\"schema_version\":1,\"placements\":[...]}`, ONE object per placed candidate, keys EXACTLY `{\"mark\",\"owner\",\"jurisdiction\",\"records\",\"tier\",\"reason\"}` plus the optional `\"borderline\"` … `tier` — EXACTLY one of `headline-candidate` / `sheet-2` / `watch",
|
|
316
|
-
reparsedBy: "driver/placement-model.mjs. AND IT IS STALE: #562 made placements.json driver-rendered, and stages.mjs:
|
|
316
|
+
reparsedBy: "driver/placement-model.mjs. AND IT IS STALE: #562 made placements.json driver-rendered, and stages.mjs:2416 says \"DO NOT WRITE placements.json (the driver renders it from this form)\" — the skill file the stage is ordered to \"read and follow exactly\" dictates the key set of a file the message forbids it to write. Two contracts in one dispatch",
|
|
317
317
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
318
318
|
},
|
|
319
319
|
{
|
|
@@ -346,7 +346,7 @@ export const E3_BACKLOG = [
|
|
|
346
346
|
// (coverage-form.mjs coverageFormBrief) and the tool schema, with every row receiver-validated at
|
|
347
347
|
// call time. Same dictated vocabulary, same authored site, new route — this row's evidence follows
|
|
348
348
|
// the dictation so the row keeps describing something that exists.
|
|
349
|
-
where: "driver/stages.mjs:
|
|
349
|
+
where: "driver/stages.mjs:2629 (the digest message) + driver/coverage-form.mjs (coverageFormBrief — the dispatch block carrying the enum and the row shape)",
|
|
350
350
|
surface: "stage-message",
|
|
351
351
|
evidence: "Record a \"status\" and a \"reason\" on EVERY row ONLY by calling the … tool — the driver validates each row as it arrives, holds the record itself, and renders both the ## Coverage ledger table and the coverage JSON from it",
|
|
352
352
|
reparsedBy: "driver/coverage-call.mjs validateCoverageCall (receiver-validated at call time, the same predicates the gate judges with) + driver/coverage-form.mjs rowIsSettled via validators.registerFindings over the _driver/ accumulator",
|
|
@@ -401,16 +401,16 @@ export const E3_BACKLOG = [
|
|
|
401
401
|
// values, receiver-validated as they arrive.
|
|
402
402
|
where: "driver/skills/clearance-register/digest.md:216-218",
|
|
403
403
|
surface: "skill-file",
|
|
404
|
-
evidence: "- `status` — EXACTLY one bare token: `confirmed-clean` / `coverage-limited` / `deferred`. Qualifiers never go in the status; they go in the reason.",
|
|
404
|
+
evidence: "- `status` — EXACTLY one bare token: `confirmed-clean` / `coverage-limited` / `deferred` / `withheld-by-judgment`. Qualifiers never go in the status; they go in the reason.",
|
|
405
405
|
reparsedBy: "driver/coverage-call.mjs validateCoverageCall (status_invalid at call time) + driver/coverage-form.mjs rowIsSettled via validators.registerFindings; the archived-era prose-table reader (coverage-ledger.mjs parseCoverageLedgerFull) survives for replay only",
|
|
406
406
|
removedByMove: "M6 LANDED and removed the no-form arm this row originally described (both sites). The surviving status-vocabulary dictation (as a typed call) is removed by NOTHING on the #850 plan",
|
|
407
407
|
},
|
|
408
408
|
{
|
|
409
409
|
stage: "register-digest",
|
|
410
410
|
kind: "exactly-these-keys",
|
|
411
|
-
where: "driver/skills/clearance-register/SKILL.md:
|
|
411
|
+
where: "driver/skills/clearance-register/SKILL.md:237-238",
|
|
412
412
|
surface: "skill-file",
|
|
413
|
-
evidence: "**The status vocabulary is CLOSED: EXACTLY one bare token of: `confirmed-clean` / `coverage-limited` / `deferred`.** Qualifiers never go in a status cell; they go in the reason.",
|
|
413
|
+
evidence: "**The status vocabulary is CLOSED: EXACTLY one bare token of: `confirmed-clean` / `coverage-limited` / `deferred` / `withheld-by-judgment`.** Qualifiers never go in a status cell; they go in the reason.",
|
|
414
414
|
reparsedBy: "driver/coverage-form.mjs / driver/coverage-ledger.mjs. Since the typed-transport conversion the STAGE MESSAGE no longer restates the enum; the surviving copies are SKILL.md (here), digest.md:207, the dispatch brief (coverage-form.mjs coverageFormBrief), the record_coverage schema (coverage-server.mjs) and gateway.mjs's repair hints — still one enum spelled at five sites",
|
|
415
415
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
416
416
|
},
|
|
@@ -429,7 +429,7 @@ export const E3_BACKLOG = [
|
|
|
429
429
|
{
|
|
430
430
|
stage: "frame-diff",
|
|
431
431
|
kind: "exactly-these-keys",
|
|
432
|
-
where: "driver/stages.mjs:
|
|
432
|
+
where: "driver/stages.mjs:2809",
|
|
433
433
|
surface: "stage-message",
|
|
434
434
|
evidence: "For each blind-model variant / field / source the run did NOT scope or search, emit one directive {layer, item, observation, severity} … severity = dominant-element (the omission is ON the spine) | material (a real omission worth a targeted sweep) | minor (already covered, or presentation only).",
|
|
435
435
|
reparsedBy: "driver/verify.mjs validators.frameDiff + driver/pipeline.mjs runSupplementalSweeps (the parser REFUSES a firing variant directive that dictates nothing dispatchable)",
|
|
@@ -447,13 +447,13 @@ export const E3_BACKLOG = [
|
|
|
447
447
|
where: "driver/skills/frame-diff/SKILL.md:44-48",
|
|
448
448
|
surface: "skill-file",
|
|
449
449
|
evidence: "A directive may carry a structured `remedy`:\\n```json\\n\"remedy\": { \"terms\": [\"TROPICAL TIKI\", \"ISLAND TIKI\"], \"nice_classes\": [\"5\", \"32\"], \"regions\": [] }\\n```",
|
|
450
|
-
reparsedBy: "driver/pipeline.mjs runSupplementalSweeps — the remedy lint refuses a label-shaped term; stages.mjs:
|
|
450
|
+
reparsedBy: "driver/pipeline.mjs runSupplementalSweeps — the remedy lint refuses a label-shaped term; stages.mjs:2809 restates the same shape in the message (\"THE ASK CONTRACT, stated at BOTH levels\")",
|
|
451
451
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
452
452
|
},
|
|
453
453
|
{
|
|
454
454
|
stage: "synthesis",
|
|
455
455
|
kind: "literal-json-skeleton",
|
|
456
|
-
where: "driver/stages.mjs:
|
|
456
|
+
where: "driver/stages.mjs:3189",
|
|
457
457
|
surface: "stage-message",
|
|
458
458
|
evidence: "MACHINE FINDINGS (MANDATORY): … a JSON OBJECT {\"schema_version\":<FINDINGS_SCHEMA_VERSION>,\"rated_under_framework\":\"…\",\"findings\":[...],\"coverage\":[...],\"context_notes\":[...],\"actions\":[...],\"ask_answers\":[...]} … Each finding object has EXACTLY these keys: {\"ordinal\",\"mark\",\"owner\",\"band\",\"net\",\"bor",
|
|
459
459
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson via validators.narrative",
|
|
@@ -467,7 +467,7 @@ export const E3_BACKLOG = [
|
|
|
467
467
|
{
|
|
468
468
|
stage: "synthesis",
|
|
469
469
|
kind: "exactly-these-keys",
|
|
470
|
-
where: "driver/stages.mjs:
|
|
470
|
+
where: "driver/stages.mjs:3226",
|
|
471
471
|
surface: "stage-message",
|
|
472
472
|
evidence: "- off_field_ground (MANDATORY on every off-field finding, FORBIDDEN on every other disposition): EXACTLY one bare token of: ${OFF_FIELD_GROUNDS.join(\" / \")}",
|
|
473
473
|
reparsedBy: "driver/findings-model.mjs validateOffFieldGround — the enum is imported from findings-model.mjs and interpolated back into the prompt, so code already holds the list it asks the model to type",
|
|
@@ -476,7 +476,7 @@ export const E3_BACKLOG = [
|
|
|
476
476
|
{
|
|
477
477
|
stage: "synthesis",
|
|
478
478
|
kind: "literal-json-skeleton",
|
|
479
|
-
where: "driver/stages.mjs:
|
|
479
|
+
where: "driver/stages.mjs:3226",
|
|
480
480
|
surface: "stage-message",
|
|
481
481
|
evidence: "- manageable …: {\"category\":\"<EXACTLY one of large-competitor / commercial-partner / troll / well-known-enforcer>\",\"reason\":\"<one-two lines…>\"}",
|
|
482
482
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
@@ -485,7 +485,7 @@ export const E3_BACKLOG = [
|
|
|
485
485
|
{
|
|
486
486
|
stage: "synthesis",
|
|
487
487
|
kind: "literal-json-skeleton",
|
|
488
|
-
where: "driver/stages.mjs:
|
|
488
|
+
where: "driver/stages.mjs:3243",
|
|
489
489
|
surface: "stage-message",
|
|
490
490
|
evidence: "- meters: {\"mark_similarity\":{...},\"goods_proximity\":{...},\"use\":{...},\"enforcer\":{...}} — all four present, each {\"token\",\"basis\",\"source\"}. … mark_similarity = high | medium | low. goods_proximity = high | medium | low. enforcer = high | medium | low | unknown. use = confirmed | not-confirmed | un",
|
|
491
491
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; driver/verify.mjs:1171 checkFindingsSibling gates meters.*.source; finding_basis_source_missing",
|
|
@@ -494,7 +494,7 @@ export const E3_BACKLOG = [
|
|
|
494
494
|
{
|
|
495
495
|
stage: "synthesis",
|
|
496
496
|
kind: "literal-json-skeleton",
|
|
497
|
-
where: "driver/stages.mjs:
|
|
497
|
+
where: "driver/stages.mjs:3243",
|
|
498
498
|
surface: "stage-message",
|
|
499
499
|
evidence: "- quadrant: {\"x\",\"y\"} numbers in [0,1]. x = goods/services proximity (0 = distant, 1 = identical). y = mark similarity (0 = distinct, 1 = identical).",
|
|
500
500
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
@@ -503,7 +503,7 @@ export const E3_BACKLOG = [
|
|
|
503
503
|
{
|
|
504
504
|
stage: "synthesis",
|
|
505
505
|
kind: "literal-json-skeleton",
|
|
506
|
-
where: "driver/stages.mjs:
|
|
506
|
+
where: "driver/stages.mjs:3245",
|
|
507
507
|
surface: "stage-message",
|
|
508
508
|
evidence: "- source: {\"source_type\",\"resolved_link\"}. source_type EXACTLY one of: register-vendor / register-euipo / common-law-marketplace / common-law-web / case-law",
|
|
509
509
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
@@ -512,7 +512,7 @@ export const E3_BACKLOG = [
|
|
|
512
512
|
{
|
|
513
513
|
stage: "synthesis",
|
|
514
514
|
kind: "exactly-these-keys",
|
|
515
|
-
where: "driver/stages.mjs:
|
|
515
|
+
where: "driver/stages.mjs:3245",
|
|
516
516
|
surface: "stage-message",
|
|
517
517
|
evidence: "coverage[]: ONE object per coverage AREA, EXACTLY {\"area\",\"state\",\"note\"}. … state EXACTLY one of: confirmed-clean / coverage-limited / open / not-searched / note.",
|
|
518
518
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; the render owns the coverage panel from these typed states",
|
|
@@ -521,7 +521,7 @@ export const E3_BACKLOG = [
|
|
|
521
521
|
{
|
|
522
522
|
stage: "synthesis",
|
|
523
523
|
kind: "literal-json-skeleton",
|
|
524
|
-
where: "driver/stages.mjs:
|
|
524
|
+
where: "driver/stages.mjs:3254",
|
|
525
525
|
surface: "stage-message",
|
|
526
526
|
evidence: "use_check = {\"source\",\"quality\"}: … quality: OPTIONAL, EXACTLY one of owner-site / independent / register-mirror … own_rights = {\"source\"}",
|
|
527
527
|
reparsedBy: "driver/verify.mjs:988 checkFindingsSibling (finding_use_check_missing); driver/own-rights.mjs:19-22",
|
|
@@ -530,7 +530,7 @@ export const E3_BACKLOG = [
|
|
|
530
530
|
{
|
|
531
531
|
stage: "synthesis",
|
|
532
532
|
kind: "literal-json-skeleton",
|
|
533
|
-
where: "driver/stages.mjs:
|
|
533
|
+
where: "driver/stages.mjs:3311",
|
|
534
534
|
surface: "stage-message",
|
|
535
535
|
evidence: "MARK ASSESSMENT … STRUCTURED FORM …: either field may instead be an OBJECT {\"read\":\"…\",\"spectrum\":\"…\",\"per_class\":[{\"class\":\"5\",\"note\":\"…\"}],\"per_market\":[{\"market\":\"CN\",\"note\":\"…\"}],\"counter_registrations\":[{\"mark\":\"…\",\"uri\":\"/mark/…\",\"note\":\"…\"}],\"acquired\":\"<optional>\",\"note\":\"<optional residual>",
|
|
536
536
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; the report collapses the rows behind toggles and the audit workbook renders them",
|
|
@@ -539,7 +539,7 @@ export const E3_BACKLOG = [
|
|
|
539
539
|
{
|
|
540
540
|
stage: "synthesis",
|
|
541
541
|
kind: "literal-json-skeleton",
|
|
542
|
-
where: "driver/stages.mjs:
|
|
542
|
+
where: "driver/stages.mjs:3317",
|
|
543
543
|
surface: "stage-message",
|
|
544
544
|
evidence: "FOUR ANSWERS …: \"four_answers\": {\"third_party_rights\":{...},\"objection_likelihood\":{...},\"registrability\":{...},\"client_enforceability\":{...}} … Each answer … is {\"read\":\"…\",\"token\":\"…\",\"basis\":\"…\",\"ordinals\":[…]}. Tokens (closed enums …): third_party_rights = strong|moderate|weak; objection_likelih",
|
|
545
545
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
@@ -557,7 +557,7 @@ export const E3_BACKLOG = [
|
|
|
557
557
|
{
|
|
558
558
|
stage: "synthesis",
|
|
559
559
|
kind: "exactly-these-keys",
|
|
560
|
-
where: "driver/stages.mjs:
|
|
560
|
+
where: "driver/stages.mjs:3324",
|
|
561
561
|
surface: "stage-message",
|
|
562
562
|
evidence: "COVERAGE JUDGMENT …: emit \"coverage_judgment\": {\"sufficient\":<bool>, \"reason\":\"<one line…>\"} — EXACTLY those two keys. Do NOT emit \"rows\": the driver writes that register itself … anything you type there is replaced wholesale.",
|
|
563
563
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson. The \"Do NOT emit rows\" clause is the purest E3 case in the tree — the prompt names a field, dictates its shape and states in the same breath that code overwrites it",
|
|
@@ -566,7 +566,7 @@ export const E3_BACKLOG = [
|
|
|
566
566
|
{
|
|
567
567
|
stage: "synthesis",
|
|
568
568
|
kind: "literal-json-skeleton",
|
|
569
|
-
where: "driver/stages.mjs:
|
|
569
|
+
where: "driver/stages.mjs:3167",
|
|
570
570
|
surface: "stage-message",
|
|
571
571
|
// RE-QUOTED, NOT PARKED. The writer's conversion reworded this dictation — the
|
|
572
572
|
// ask answers ride the findings RECORD now and the driver renders the labelled line into both the
|
|
@@ -590,7 +590,7 @@ export const E3_BACKLOG = [
|
|
|
590
590
|
{
|
|
591
591
|
stage: "synthesis",
|
|
592
592
|
kind: "dictated-line-shape",
|
|
593
|
-
where: "driver/stages.mjs:
|
|
593
|
+
where: "driver/stages.mjs:3088 (restated at driver/skills/clearance-search/synthesis-rules.md:428)",
|
|
594
594
|
surface: "stage-message",
|
|
595
595
|
evidence: "END that finding's actual-use line with a literal \"- **Use-check source:** <result URL | \"perplexity_research — no result\">\" line",
|
|
596
596
|
reparsedBy: "driver/verify.mjs validators.narrative (spec-11 hard reject); the repair hint re-dictates the literal at driver/gateway.mjs:2153",
|
|
@@ -599,7 +599,7 @@ export const E3_BACKLOG = [
|
|
|
599
599
|
{
|
|
600
600
|
stage: "synthesis",
|
|
601
601
|
kind: "dictated-line-shape",
|
|
602
|
-
where: "driver/stages.mjs:
|
|
602
|
+
where: "driver/stages.mjs:3101 (restated at driver/skills/clearance-search/synthesis-rules.md:475)",
|
|
603
603
|
surface: "stage-message",
|
|
604
604
|
evidence: "END that finding's reasoning with a literal \"- **Own-rights source:** <record URI(s) | \"no applicant-owned registrations in the searched register material\">\" line",
|
|
605
605
|
reparsedBy: "driver/own-rights.mjs:19-22 — \"This module only requires the 'Own-rights source:' line to exist\"; repair hint at driver/gateway.mjs:2361 (the `own_rights_missing` branch; re-verified 2026-08-29 — the old :1736 predated this branch and pointed into the A4 repeat-signature block)",
|
|
@@ -608,7 +608,7 @@ export const E3_BACKLOG = [
|
|
|
608
608
|
{
|
|
609
609
|
stage: "synthesis",
|
|
610
610
|
kind: "exactly-these-keys",
|
|
611
|
-
where: "driver/stages.mjs:
|
|
611
|
+
where: "driver/stages.mjs:3303",
|
|
612
612
|
surface: "stage-message",
|
|
613
613
|
evidence: "add it to the top-level \"context_notes\" array — each object EXACTLY {\"type\":\"famous-neighbour-ungrounded\",\"mark\",\"owner\",\"context\"}",
|
|
614
614
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
@@ -617,7 +617,7 @@ export const E3_BACKLOG = [
|
|
|
617
617
|
{
|
|
618
618
|
stage: "synthesis",
|
|
619
619
|
kind: "literal-json-skeleton",
|
|
620
|
-
where: "driver/stages.mjs:
|
|
620
|
+
where: "driver/stages.mjs:3191",
|
|
621
621
|
surface: "stage-message",
|
|
622
622
|
evidence: "- owner: {\"name\",\"country\",\"registrations\":[...]}. … Each registration: {\"uri\", optionally \"classes\":[\"9\",\"41\"],\"status\",\"filed\",\"expiry\",\"jurisdiction\"}. The \"uri\" is the ONLY field that matters: the driver BINDS classes/status/filed/expiry/jurisdiction AND the owner name from the FETCHED record ke",
|
|
623
623
|
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson + the record-binding join. Six of the seven keys are stated in the prompt and overwritten by code in the same sentence",
|
|
@@ -626,7 +626,7 @@ export const E3_BACKLOG = [
|
|
|
626
626
|
{
|
|
627
627
|
stage: "case-law",
|
|
628
628
|
kind: "literal-json-skeleton",
|
|
629
|
-
where: "driver/stages.mjs:
|
|
629
|
+
where: "driver/stages.mjs:3473",
|
|
630
630
|
surface: "stage-message",
|
|
631
631
|
evidence: "ALSO write the RETRIEVAL RECORD to <path> — a JSON OBJECT with EXACTLY these keys: {\"schema_version\":1,\"queries\":[{\"query\":\"<the search you dispatched, verbatim>\",\"jurisdiction\":\"…\",\"results\":<how many hits it returned>}, …],\"citations\":[{\"proceeding\":\"…\",\"forum\":\"…\",\"jurisdiction\":\"…\",\"decided\":\"…\"",
|
|
632
632
|
reparsedBy: "driver/verify.mjs validators.caseLaw — the ledger arm, armed by the stage-contract marker `citations` (stages.mjs:1820)",
|
|
@@ -635,7 +635,7 @@ export const E3_BACKLOG = [
|
|
|
635
635
|
{
|
|
636
636
|
stage: "case-law",
|
|
637
637
|
kind: "dictated-line-shape",
|
|
638
|
-
where: "driver/stages.mjs:
|
|
638
|
+
where: "driver/stages.mjs:3481",
|
|
639
639
|
surface: "stage-message",
|
|
640
640
|
evidence: "EVERY \"Grounded profile\" section MUST start its body with the line \"- ord: <N>\" naming which finding it grounds (use the ordinal from this list; a profile that grounds no listed finding omits the line)",
|
|
641
641
|
reparsedBy: "driver/publish/parse.mjs:339, parseCaseLawProfiles() in parse.mjs (\"the optional '- ord: <N>' first body line … gives an EXACT join\"); driver/findings-model.mjs:273 /^-\\s*ord:\\s*(\\d+)\\s*$/m; driver/publish/index.mjs:778 runOrigins",
|
|
@@ -669,7 +669,7 @@ export const E3_BACKLOG = [
|
|
|
669
669
|
{
|
|
670
670
|
stage: "narrative-refutation",
|
|
671
671
|
kind: "dictated-line-shape",
|
|
672
|
-
where: "driver/stages.mjs:
|
|
672
|
+
where: "driver/stages.mjs:3613",
|
|
673
673
|
surface: "stage-message",
|
|
674
674
|
// RE-QUOTED BY CONVERSION 9. The LINE-TOKEN half is gone — no "anywhere on the line", no "[kind: …]
|
|
675
675
|
// token", no "a line with no token is treated as fact", because a kind is a typed field now and an
|
|
@@ -768,7 +768,7 @@ export const E3_BACKLOG = [
|
|
|
768
768
|
{
|
|
769
769
|
stage: "report-card",
|
|
770
770
|
kind: "literal-json-skeleton",
|
|
771
|
-
where: "driver/stages.mjs:
|
|
771
|
+
where: "driver/stages.mjs:3922",
|
|
772
772
|
surface: "stage-message",
|
|
773
773
|
evidence: "The finding's OWN record — the ONLY source for this card …:\\n```json\\n<JSON.stringify(finding, null, 2)>\\n```",
|
|
774
774
|
reparsedBy: "none — this is the INPUT side, and that is why it belongs in the survey: a full JSON object rendered into the prompt is exactly the mechanism #850 proves produced R-RECEIPT (the model pattern-matches a shown shape). E3's clause 1 as written (\"a code fence or inline example showing the exact object shape the model must emit\") does not reach an injected record, so the lint needs an explicit rule for shown-but-not-owed structure",
|
|
@@ -879,7 +879,7 @@ export const E3_EVIDENCE_UNRESOLVED = [
|
|
|
879
879
|
* multi-witness rows were settled by the site their anchor resolves at (narrative-refutation to its
|
|
880
880
|
* SKILL.md, two synthesis rows to stages.mjs). Two were decided by reading the delivering call site:
|
|
881
881
|
* common-law-half is `tool-response` (perplexity-server.mjs:111), register-digest is `stage-message`
|
|
882
|
-
* (pipeline.mjs
|
|
882
|
+
* (pipeline.mjs, where coverageFormBrief is appended to the dispatch).
|
|
883
883
|
*/
|
|
884
884
|
// 35 -> 32 stage-message. The three rows that left were the send stages' dictated line shapes
|
|
885
885
|
// — `notify`'s verbatim-HTML instruction and the two chat pings' EXACTLY-this-text lines. They were not
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// D4 verify.mjs parseCoverageLedgerJson, same shape
|
|
22
22
|
// D5 verify.mjs:1504 fail(`${unaccounted[0].token}:…`) — token minted in a DATA ROW
|
|
23
23
|
// D6 verify.mjs:1567 fail(`${violations[0].token}…`) — validatePlanFeasibility in register-plan.mjs
|
|
24
|
-
// D7 verify.mjs:1558 fail(`${v2[0].token}${detail}…`) — register-plan.mjs
|
|
24
|
+
// D7 verify.mjs:1558 fail(`${v2[0].token}${detail}…`) — register-plan.mjs disclosureTextByAxis
|
|
25
25
|
// D8 verify.mjs:2470 caseLawLedgerFail fail(caseLawLedgerFail(…)) — token built in case-law-ledger.mjs:195 caseLawLedgerFail
|
|
26
26
|
//
|
|
27
27
|
// A partition built on the 60 tokens a regex CAN see would run green while blind to the rest, which is
|
|
@@ -139,10 +139,10 @@ export const VOCABULARY = [
|
|
|
139
139
|
{ token: "coverage_form_missing", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
140
140
|
{ token: "coverage_form_empty", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
141
141
|
{ token: "coverage_status_offenum", stages: ["register-digest"], site: "driver/verify.mjs:2050" },
|
|
142
|
-
{ token: "coverage_deferred_unaccounted", stages: ["register-digest"], site: "driver/verify.mjs coverageFormFail", family: "driver/register-plan.mjs
|
|
143
|
-
{ token: "coverage_clean_unexecuted", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs
|
|
144
|
-
{ token: "coverage_clean_skipped", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs
|
|
145
|
-
{ token: "coverage_clean_unverified_incomplete", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs
|
|
142
|
+
{ token: "coverage_deferred_unaccounted", stages: ["register-digest"], site: "driver/verify.mjs coverageFormFail", family: "driver/register-plan.mjs PROVIDER_HARD_ERROR_PREFIX — token on a data row", dynamic: "D5" },
|
|
143
|
+
{ token: "coverage_clean_unexecuted", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs validatePlanFeasibility", dynamic: "D6" },
|
|
144
|
+
{ token: "coverage_clean_skipped", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs searchedJurisdictionsFromPlan", dynamic: "D6" },
|
|
145
|
+
{ token: "coverage_clean_unverified_incomplete", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs disclosureTextByAxis", dynamic: "D7" },
|
|
146
146
|
{ token: "coverage_clean_tainted", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
147
147
|
{ token: "coverage_ledger_", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs (parseCoverageLedgerJson token-first throws)", dynamic: "D4" },
|
|
148
148
|
{ token: "coverage_key_unknown", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs", dynamic: "D4" },
|
|
@@ -509,9 +509,9 @@ export const INNER_CODES = Object.freeze([
|
|
|
509
509
|
// ONE CODE, TWO TOKENS, and the split is by `cause` rather than by code: verify.mjs peels the
|
|
510
510
|
// `axis_invalid` cause into its own family before counting the rest. A ruling naming only
|
|
511
511
|
// `coverage_no_status` would be true of most `no_status` records and false of the ones that matter most.
|
|
512
|
-
{ code: "no_status", mints: ["driver/coverage-form.mjs:
|
|
512
|
+
{ code: "no_status", mints: ["driver/coverage-form.mjs:856"], rollsUpTo: ["coverage_no_status", "coverage_form_axis_invalid"],
|
|
513
513
|
why: "Two composites, split on the record's `cause`: `checkFindingsSibling()` in verify.mjs for cause `axis_invalid`, `dispositionForm()` there for the rest. NOT the `@returns` annotation above `COVERAGE_CAUSES` in coverage-form.mjs — that is the annotation, not the mint." },
|
|
514
|
-
{ code: "engine_vocabulary", mints: ["driver/coverage-form.mjs:
|
|
514
|
+
{ code: "engine_vocabulary", mints: ["driver/coverage-form.mjs:846"], rollsUpTo: ["coverage_form_engine_vocabulary"],
|
|
515
515
|
why: "#669 — the seat wrote an engine token into the `reason` sentence that reaches the reader's page. Checked on settled rows too, because a row the seat considers finished is exactly the one whose sentence gets printed. Namespaced at verify.mjs:1165; the bare code names a row, never a stage." },
|
|
516
516
|
|
|
517
517
|
// ── ONE CODE, TWO MODULES ────────────────────────────────────────────────────────────────────────
|
|
@@ -522,7 +522,7 @@ export const INNER_CODES = Object.freeze([
|
|
|
522
522
|
// the census output would have been half a ruling. verify.mjs:1075 already states the rule this row
|
|
523
523
|
// records: `coverage_form_damaged`, never a bare `form_damaged`.
|
|
524
524
|
{ code: "form_damaged",
|
|
525
|
-
mints: ["driver/connotation-search.mjs:2075", "driver/connotation-search.mjs:2151", "driver/coverage-form.mjs:
|
|
525
|
+
mints: ["driver/connotation-search.mjs:2075", "driver/connotation-search.mjs:2151", "driver/coverage-form.mjs:838"],
|
|
526
526
|
rollsUpTo: ["connotation_form_damaged", "coverage_form_damaged"],
|
|
527
527
|
why: "Minted in two lanes and namespaced per lane: verify.mjs:1019 for the meaning sweep, verify.mjs:1119 for the register digest. The namespacing is what keeps them apart — see verify.mjs:1075." },
|
|
528
528
|
]);
|
|
@@ -31,6 +31,7 @@ import { driverDir } from "../shared/driver-dir.mjs"; //
|
|
|
31
31
|
import { COVERAGE_STATUSES, COVERAGE_FORM_NAME, REGISTER_AXES } from "./coverage-ledger.mjs";
|
|
32
32
|
import { coverageFormSidecarName, parseCoverageForm } from "./coverage-form.mjs";
|
|
33
33
|
import { capabilitiesFor } from "./register-capabilities.mjs";
|
|
34
|
+
import { readWithheldFamilies } from "./withheld-families.mjs";
|
|
34
35
|
|
|
35
36
|
export { COVERAGE_FORM_NAME };
|
|
36
37
|
|
|
@@ -147,7 +148,8 @@ export function coverageFormInput(runDir) {
|
|
|
147
148
|
catch { capabilities = null; } // an unknown provider id reads as unestablished, which discloses
|
|
148
149
|
const orderedTerritories = Array.isArray(plan.ordered_jurisdictions) ? plan.ordered_jurisdictions : [];
|
|
149
150
|
return { input: { skeleton: exec.skeleton, plan, bandBlocksByAxis, deferredReasons, activeAxes,
|
|
150
|
-
bandsUnreadable, orderedTerritories, capabilities, unknownAxisUnits
|
|
151
|
+
bandsUnreadable, orderedTerritories, capabilities, unknownAxisUnits,
|
|
152
|
+
awaiting: Array.isArray(exec.awaiting) ? exec.awaiting : [], withheld: readWithheldFamilies(runDir) }, absent: null };
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
/**
|
package/driver/coverage-form.mjs
CHANGED
|
@@ -32,10 +32,10 @@ import { REGISTER_AXES, COVERAGE_STATUSES, normalizeAxis, CROWD_RULING_TOKEN,
|
|
|
32
32
|
CROWD_RULING_UNIT_GRAMMAR } from "./coverage-ledger.mjs";
|
|
33
33
|
import { shortId } from "./connotation-search.mjs";
|
|
34
34
|
import { openBlocksByAxis } from "./register-plan.mjs";
|
|
35
|
-
import { territoryLayerReport, unsearchedLayerReason } from "./binding-layers.mjs";
|
|
35
|
+
import { territoryLayerReport, unsearchedLayerReason } from "./binding-layers.mjs"; import { goodsTermsList } from "../providers/_shared/term-shape.mjs";
|
|
36
36
|
|
|
37
37
|
const STATUS_SET = new Set(COVERAGE_STATUSES);
|
|
38
|
-
const DRIVER_KINDS = new Set(["axis", "block", "deferred"]);
|
|
38
|
+
const DRIVER_KINDS = new Set(["axis", "block", "deferred", "family"]);
|
|
39
39
|
|
|
40
40
|
// ── SEAT ROWS — WHAT THE FORM DOES NOT TAKE AWAY ────────────────────────────────────────────────────
|
|
41
41
|
//
|
|
@@ -238,7 +238,7 @@ export function seatRows(rows, driverKeys) {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
// The coverage unit label, composed by the machine from the plan entry it is about — never a string the
|
|
241
|
-
// seat invents and never one it has to reproduce. `<axis> / <predicate>: <term(s)> [cl <classes>]
|
|
241
|
+
// seat invents and never one it has to reproduce. `<axis> / <predicate>: <term(s)> [cl <classes>] goods: <words>`, the
|
|
242
242
|
// same left-of-slash-is-the-axis shape every downstream coverage consumer keys on (coverage-ledger.mjs
|
|
243
243
|
// normalizeAxis, scope-facts, the taint join). Terms are bounded so an OR-stack of forty cannot make one
|
|
244
244
|
// table cell unreadable; the qid rides its own column, so nothing identifying is lost to the cut.
|
|
@@ -256,7 +256,7 @@ function unitLabel(axis, entry) {
|
|
|
256
256
|
String(entry.predicate ?? "").trim(),
|
|
257
257
|
shown.length ? `${shown.join(" OR ")}${more}` : "",
|
|
258
258
|
].filter(Boolean).join(": ");
|
|
259
|
-
return `${axis} / ${scope || String(entry.qid ?? "")}${cls}`;
|
|
259
|
+
return `${axis} / ${scope || String(entry.qid ?? "")}${cls}${goodsTermsList(entry).length ? ` goods: ${goodsTermsList(entry).join(" OR ")}` : ""}`; // the goods words are part of the question (ruled for the client's table 2026-09-22): a goods slice shares predicate, term and classes with the identical one
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
/**
|
|
@@ -304,7 +304,7 @@ function unitLabel(axis, entry) {
|
|
|
304
304
|
*/
|
|
305
305
|
export function coverageFormRows({ skeleton = [], activeAxes = null, plan = null,
|
|
306
306
|
bandBlocksByAxis = {}, deferredReasons = {}, bandsUnreadable = [],
|
|
307
|
-
orderedTerritories = [], capabilities = null } = {}) {
|
|
307
|
+
orderedTerritories = [], capabilities = null, awaiting = [], withheld = {} } = {}) {
|
|
308
308
|
const skel = Array.isArray(skeleton) ? skeleton.filter((s) => s && typeof s === "object") : [];
|
|
309
309
|
const entriesByQid = new Map((plan?.entries ?? []).map((e) => [e.qid, e]));
|
|
310
310
|
const open = openBlocksByAxis(skel, bandBlocksByAxis, plan);
|
|
@@ -376,6 +376,26 @@ export function coverageFormRows({ skeleton = [], activeAxes = null, plan = null
|
|
|
376
376
|
status: null, reason: null,
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
|
+
// ── A WAITING FAMILY THE READING TURN DID NOT ASK (withheld-families.mjs) ─────────────────────
|
|
380
|
+
//
|
|
381
|
+
// It was never searched, and the one honest judgment of it is the turn's: withheld, with the reason
|
|
382
|
+
// it was not asked. The row arrives settled when the turn recorded that, and open when nobody did,
|
|
383
|
+
// so an unjudged family is an obligation the digest's gate refuses to pass. `family` rows stay out of
|
|
384
|
+
// the ledger the report is built from; the reason is the run's record and the audit workbook's.
|
|
385
|
+
for (const f of (Array.isArray(awaiting) ? awaiting : [])) {
|
|
386
|
+
const qid = String(f?.qid ?? "").trim();
|
|
387
|
+
if (!qid || String(f?.axis ?? "").trim() !== axis) continue;
|
|
388
|
+
const w = withheld?.[qid];
|
|
389
|
+
rows.push({
|
|
390
|
+
row_id: shortId("CF", `family:${qid}`),
|
|
391
|
+
axis, kind: "family",
|
|
392
|
+
unit: unitLabel(axis, entriesByQid.get(qid)),
|
|
393
|
+
qid,
|
|
394
|
+
open: true,
|
|
395
|
+
open_because: "a waiting family the reading turn did not ask — it was never searched, and its only judgment is withheld-by-judgment with the reason it was not asked",
|
|
396
|
+
status: w?.reason ? "withheld-by-judgment" : null, reason: w?.reason ? String(w.reason) : null,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
379
399
|
// ── AN OFFICE THIS DEPLOYMENT COULD NOT REACH ────────────────────────────────────────
|
|
380
400
|
//
|
|
381
401
|
// The one deferral shape that arrives WITHOUT A QID, which is why every row above missed it.
|
|
@@ -681,14 +701,14 @@ export function rowIsSettled(row, canonical) {
|
|
|
681
701
|
// qid and blockIsDisclosed' qid-or-hit-count — because the driver's own row for that obligation IS the
|
|
682
702
|
// naming, so requiring THAT row to be non-clean asks for precisely what "named by a non-clean row on
|
|
683
703
|
// its own axis" asked for. Per row, never per axis: a sibling row's status discharges nothing.
|
|
684
|
-
if (canonical.open === true && status === "confirmed-clean") return false;
|
|
704
|
+
if (canonical.open === true && status === "confirmed-clean") return false; if (canonical.kind === "family" && status !== "withheld-by-judgment") return false; // a waiting family never ran: withheld is its only judgment
|
|
685
705
|
return true;
|
|
686
706
|
}
|
|
687
707
|
|
|
688
708
|
/** The settled rows of a form, in the shape every coverage consumer reads: {axis, status, unit, reason}. */
|
|
689
709
|
export function formLedgerRows(rows) {
|
|
690
710
|
return (rows ?? [])
|
|
691
|
-
.filter((r) => r && STATUS_SET.has(String(r.status ?? "").trim().toLowerCase()))
|
|
711
|
+
.filter((r) => r && r.kind !== "family" && STATUS_SET.has(String(r.status ?? "").trim().toLowerCase()))
|
|
692
712
|
.map((r) => ({
|
|
693
713
|
axis: String(r.axis ?? "").trim().toLowerCase(),
|
|
694
714
|
status: String(r.status).trim().toLowerCase(),
|
|
@@ -860,7 +880,7 @@ const cell = (s) => String(s ?? "").replace(/\\/g, "\\\\").replace(/\|/g, "\\|")
|
|
|
860
880
|
|
|
861
881
|
/** The `## Coverage ledger` section, rendered from the form's rows. "" when nothing is settled. PURE. */
|
|
862
882
|
export function renderCoverageLedgerSection(rows) {
|
|
863
|
-
const usable = (rows ?? []).filter((r) => r && STATUS_SET.has(String(r.status ?? "").trim().toLowerCase()));
|
|
883
|
+
const usable = (rows ?? []).filter((r) => r && r.kind !== "family" && STATUS_SET.has(String(r.status ?? "").trim().toLowerCase()));
|
|
864
884
|
if (!usable.length) return "";
|
|
865
885
|
return [
|
|
866
886
|
"## Coverage ledger",
|
|
@@ -936,7 +956,7 @@ export function spliceCoverageLedger(md, section) {
|
|
|
936
956
|
* @returns {string} a JSON ARRAY string that round-trips through parseCoverageLedgerJson
|
|
937
957
|
*/
|
|
938
958
|
export function renderCoverageLedgerJsonFromForm(rows, classTokens) {
|
|
939
|
-
const usable = (rows ?? []).filter((r) => r && STATUS_SET.has(String(r.status ?? "").trim().toLowerCase()));
|
|
959
|
+
const usable = (rows ?? []).filter((r) => r && r.kind !== "family" && STATUS_SET.has(String(r.status ?? "").trim().toLowerCase()));
|
|
940
960
|
return JSON.stringify(usable.map((r) => {
|
|
941
961
|
const unit = String(r.unit ?? r.axis ?? "");
|
|
942
962
|
const i = unit.indexOf("/");
|
|
@@ -995,19 +1015,26 @@ export function coverageFormBrief(form) {
|
|
|
995
1015
|
"row as it arrives, holds the record itself, and renders the table and the coverage JSON from it —",
|
|
996
1016
|
"nothing you write into any file is read.",
|
|
997
1017
|
"",
|
|
998
|
-
`There are ${rows.length} row(s) — one per axis, one per unaccounted crowd block, one per deferred slice —`,
|
|
1018
|
+
`There are ${rows.length} row(s) — one per axis, one per unaccounted crowd block, one per deferred slice, one per waiting family the reading turn did not ask —`,
|
|
999
1019
|
"and every identifier is computed: the coverage unit, the query id, the hit count, the unaccounted",
|
|
1000
1020
|
"classes and terms, and each deferred slice's own receipt reason. This is the complete list: nothing is",
|
|
1001
1021
|
"abbreviated, truncated or elided, and there is nothing owed that is not a row below.",
|
|
1002
1022
|
"",
|
|
1003
1023
|
"For EVERY row, call `record_coverage` with `row_id` (as listed), `status` — exactly one bare token of",
|
|
1004
|
-
"confirmed-clean / coverage-limited / deferred — and `reason`, the sentence the lawyer reads: a",
|
|
1024
|
+
"confirmed-clean / coverage-limited / deferred / withheld-by-judgment — and `reason`, the sentence the lawyer reads: a",
|
|
1005
1025
|
"lawyer's words, never the engine's (a reason naming primary-sweep, saturation-probe,",
|
|
1006
1026
|
"transliteration-numeric, incumbent-class or crowd-context is REFUSED, because the coverage unit",
|
|
1007
1027
|
"already carries the identifier and your sentence is printed on the client's report). A call carries a",
|
|
1008
1028
|
"batch; refused rows name what to change and the rest of the call is KEPT; statuses accumulate across",
|
|
1009
1029
|
"attempts, so a row settled once stays settled. The answer lists every obligation still outstanding.",
|
|
1010
1030
|
"",
|
|
1031
|
+
...(rows.some((r) => r.kind === "family") ? [
|
|
1032
|
+
"A `family` row is a waiting family the reading turn did not ask. It was never searched, so its only",
|
|
1033
|
+
"status is withheld-by-judgment. Most arrive settled with the reading turn's reason; for any that did",
|
|
1034
|
+
"not, record withheld-by-judgment and why it was not asked. That reason goes into the audit workbook, not",
|
|
1035
|
+
"the report.",
|
|
1036
|
+
"",
|
|
1037
|
+
] : []),
|
|
1011
1038
|
"THE ROWS:",
|
|
1012
1039
|
...rows.map(briefRow),
|
|
1013
1040
|
"",
|
|
@@ -72,7 +72,22 @@ export function coverageUnitLabel(unit) {
|
|
|
72
72
|
// The closed status set. BARE tokens only — the prose table may carry suffixed statuses like
|
|
73
73
|
// `coverage-limited (count-only, saturated)` (digest.md teaches one); in the JSON the qualifier
|
|
74
74
|
// moves into `reason`, and the dictation block + correction hint both say so.
|
|
75
|
-
|
|
75
|
+
// ── `withheld-by-judgment` — A FAMILY NOBODY ASKED FOR, ON PURPOSE ───────────────────────────────
|
|
76
|
+
//
|
|
77
|
+
// The crowded-field doctrine tells the reading turn to stop widening once the readable list already
|
|
78
|
+
// holds conflicts in the client's field: do not open scripts, neighbours, compounds or guessed owners
|
|
79
|
+
// for this mark, and write which questions you did not ask and why. That is a DECISION, and it needs a
|
|
80
|
+
// word of its own.
|
|
81
|
+
//
|
|
82
|
+
// It is not `confirmed-clean`: nobody searched it, and a clean over an unsearched family is the false
|
|
83
|
+
// clean this ledger exists to make impossible. It is not `coverage-limited` either — that says the
|
|
84
|
+
// engine tried and could not finish, which is a different fact and reads to a lawyer as a gap in the
|
|
85
|
+
// work rather than a choice about where the work was best spent. And it is not `deferred`, which says
|
|
86
|
+
// the provider could not express the question at all.
|
|
87
|
+
//
|
|
88
|
+
// So a fourth status, carrying the model's written reason. The clean-claim gates treat it exactly as
|
|
89
|
+
// they treat the other two non-clean states: it can never stand in for a search.
|
|
90
|
+
export const COVERAGE_STATUSES = ["confirmed-clean", "coverage-limited", "deferred", "withheld-by-judgment"];
|
|
76
91
|
|
|
77
92
|
// — the seat-facing coverage form's file name, owned HERE with the rest of the coverage vocabulary
|
|
78
93
|
// so the four places that need it (paths(), coverage-form-io, the gateway's repair routing and the
|
|
@@ -254,22 +269,37 @@ export const isCapabilityGapReason = (reason) => CAPABILITY_GAP_REASON_RE.test(S
|
|
|
254
269
|
* @param capabilityGapAxes axes the plan-execution receipt says carry >=1 deterministic deferral
|
|
255
270
|
* @param opts.fullyDeferred the plan says EVERY entry on this axis is unsupported (fullyDeferredAxes) —
|
|
256
271
|
* then there is nothing on the axis a re-run could reach, whatever a row's prose says
|
|
272
|
+
* @param opts.heldUnits ledgerUnitKey()s of rows this run already accepted as capability gaps (the
|
|
273
|
+
* sticky set, matched through the coverage form's qid) — held whatever the seat wrote in the reason
|
|
257
274
|
* @returns {{closeable: rows[], held: rows[]}} — `held` rows stay `deferred` and stay disclosed:
|
|
258
275
|
* the coverage floor keeps its right to hold on them (computeOpenFloors → envelope_note,
|
|
259
276
|
* the registerGap clamp stays armed). They are simply never re-run and never closed by time.
|
|
260
277
|
* PURE.
|
|
261
278
|
*/
|
|
262
|
-
export function splitDeferredByCloseability(rows, axis, capabilityGapAxes, { fullyDeferred = false } = {}) {
|
|
279
|
+
export function splitDeferredByCloseability(rows, axis, capabilityGapAxes, { fullyDeferred = false, heldUnits = null } = {}) {
|
|
263
280
|
const ax = String(axis ?? "").toLowerCase();
|
|
264
281
|
const indicted = new Set([...(capabilityGapAxes ?? [])].map((a) => String(a).toLowerCase()));
|
|
265
282
|
const owned = (rows ?? []).filter((r) => r && r.status === "deferred" && String(r.axis ?? "").toLowerCase() === ax);
|
|
266
283
|
if (fullyDeferred) return { closeable: [], held: owned };
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
return { closeable, held };
|
|
284
|
+
const sticky = (r) => Boolean(heldUnits?.has(ledgerUnitKey(r.axis, r.scope)));
|
|
285
|
+
if (!indicted.has(ax) && !owned.some(sticky)) return { closeable: owned, held: [] };
|
|
286
|
+
const isHeld = (r) => isCapabilityGapReason(r.reason) || sticky(r);
|
|
287
|
+
return { closeable: owned.filter((r) => !isHeld(r)), held: owned.filter(isHeld) };
|
|
271
288
|
}
|
|
272
289
|
|
|
290
|
+
/**
|
|
291
|
+
* One coverage unit's identity across the form and the ledger: its axis and its scope, the part of the
|
|
292
|
+
* form's `unit` after the first `/`, which is exactly what renderCoverageLedgerJsonFromForm writes as the
|
|
293
|
+
* ledger row's `scope`. Case and runs of whitespace do not distinguish two units. PURE.
|
|
294
|
+
*/
|
|
295
|
+
export const ledgerUnitKey = (axis, scope) =>
|
|
296
|
+
`${String(axis ?? "").trim().toLowerCase()}|${String(scope ?? "").replace(/\s+/g, " ").trim().toLowerCase()}`;
|
|
297
|
+
export const formRowUnitKey = (r) => {
|
|
298
|
+
const unit = String(r?.unit ?? r?.axis ?? "");
|
|
299
|
+
const i = unit.indexOf("/");
|
|
300
|
+
return ledgerUnitKey(r?.axis, i >= 0 ? unit.slice(i + 1) : "");
|
|
301
|
+
};
|
|
302
|
+
|
|
273
303
|
// ── THE LEDGER AS A TABLE, WRITTEN ONCE ────────────────────────────────────────────────────────────
|
|
274
304
|
//
|
|
275
305
|
// Two dispatches hand a judgment seat the machine ledger as ROWS rather than as a path: the skeptic
|
|
@@ -403,7 +433,7 @@ export function parseCoverageLedgerFull(md) {
|
|
|
403
433
|
// normalize-then-validate at the parse boundary (B): repair markdown/qualifier noise on the
|
|
404
434
|
// left-of-slash axis; if that isn't a known token, scan the whole cell for a transposed axis.
|
|
405
435
|
const axis = normalizeAxis(unit.split("/")[0], unit);
|
|
406
|
-
const m = cells[1].match(/coverage-limited|deferred|confirmed-clean/i);
|
|
436
|
+
const m = cells[1].match(/withheld-by-judgment|coverage-limited|deferred|confirmed-clean/i);
|
|
407
437
|
if (!axis || !m) {
|
|
408
438
|
if (axis && !m && REGISTER_AXES.includes(axis)) offEnum.push({ axis, unit, status: cells[1] });
|
|
409
439
|
dropped.push(ln.trim().replace(/\s+/g, " ").slice(0, 120));
|
|
@@ -102,10 +102,10 @@ export function unionCoverageForm(prior, submitted, input, { parkedIds = null }
|
|
|
102
102
|
let settled = 0, carried = 0, parked = 0;
|
|
103
103
|
for (const row of form.rows) {
|
|
104
104
|
const p = findPrior(row), s = findSubmitted(row);
|
|
105
|
-
const sOk = s && rowIsSettled(s, row), pOk = p && rowIsSettled(p, row);
|
|
105
|
+
const sOk = s && rowIsSettled(s, row), pOk = p && rowIsSettled(p, row), dOk = rowIsSettled(row, row);
|
|
106
106
|
let fields;
|
|
107
107
|
if (sOk) fields = seatFields(s);
|
|
108
|
-
else if (pOk) fields = seatFields(p);
|
|
108
|
+
else if (pOk) fields = seatFields(p); else if (dOk) fields = seatFields(row); // a judgment the driver row arrived with (a family the reading turn withheld)
|
|
109
109
|
else {
|
|
110
110
|
const sf = seatFields(s ?? {}), pf = seatFields(p ?? {});
|
|
111
111
|
fields = { status: sf.status || pf.status, reason: sf.reason || pf.reason };
|