clearotron 0.4.1 → 0.5.0-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/README.md +1 -0
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/bin/start.mjs +33 -8
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +23 -0
- package/driver/ask-ledger.mjs +86 -15
- package/driver/band-shape.mjs +61 -11
- package/driver/band-size.mjs +10 -0
- package/driver/close-verify.mjs +29 -2
- package/driver/common-law-coverage-status.mjs +1 -1
- package/driver/connotation-search.mjs +7 -9
- package/driver/contract-e3-backlog.mjs +34 -34
- package/driver/contract-e3-baseline.json +5 -5
- package/driver/contract-vocabulary.mjs +12 -12
- package/driver/corrections-delivered.mjs +113 -0
- package/driver/corrections-feedforward.mjs +16 -6
- package/driver/corrective-arm.mjs +11 -8
- package/driver/coverage-form.mjs +3 -0
- package/driver/coverage-union.mjs +11 -2
- package/driver/digest-queue.mjs +1 -1
- package/driver/disposition-call.mjs +1 -1
- package/driver/document-coverage.mjs +3 -2
- package/driver/effective-scope.mjs +2 -2
- package/driver/engine/mcp/band-server.mjs +77 -13
- package/driver/engine/mcp/dispositions-server.mjs +2 -2
- package/driver/engine/mcp/recording-server.mjs +10 -6
- package/driver/findings-model.mjs +16 -4
- package/driver/floor-duty.mjs +9 -0
- package/driver/gate-metrics.mjs +7 -1
- package/driver/gateway.mjs +28 -14
- package/driver/goods-evidence.mjs +295 -0
- package/driver/knockout-assess-record.mjs +1 -1
- package/driver/narrative-refutation-record.mjs +40 -2
- package/driver/package.json +1 -1
- package/driver/pipeline.mjs +1945 -314
- package/driver/placement-decisions.mjs +243 -0
- package/driver/placement-form.mjs +42 -8
- package/driver/placement-union.mjs +14 -6
- package/driver/provider-usage.mjs +10 -7
- package/driver/publish/audit-from-spine.mjs +5 -2
- package/driver/publish/index.mjs +19 -4
- package/driver/publish/xlsx.mjs +6 -6
- package/driver/recall-reconciliation.mjs +15 -5
- package/driver/record-carry.mjs +70 -6
- package/driver/record-discard.mjs +7 -1
- package/driver/record-fold.mjs +14 -0
- package/driver/record-goods.mjs +101 -5
- package/driver/register-plan.mjs +89 -0
- package/driver/registry-fidelity.mjs +34 -3
- package/driver/remedy-accounting.mjs +52 -14
- package/driver/repair-composers.mjs +265 -105
- package/driver/repair-scope.mjs +327 -0
- package/driver/repair-targets.mjs +92 -0
- package/driver/result-noun-fields.mjs +3 -3
- package/driver/screen-gate.mjs +96 -0
- package/driver/skills/clearance-common-law/SKILL.md +8 -0
- package/driver/skills/clearance-register/SKILL.md +5 -5
- package/driver/skills/clearance-register/digest.md +14 -14
- package/driver/skills/clearance-register/unit.md +3 -3
- package/driver/skills/clearance-search/delivery-contract.md +2 -58
- package/driver/skills/clearance-search/report-prose.md +12 -3
- package/driver/skills/clearance-search/risk-framework-triage.md +1 -1
- package/driver/skills/clearance-search/synthesis-rules.md +10 -13
- package/driver/skills/clearance-variants/SKILL.md +4 -4
- package/driver/skills/knockout-assess/SKILL.md +9 -14
- package/driver/skills/knockout-frame/SKILL.md +5 -5
- package/driver/skills/narrative-refutation/SKILL.md +1 -1
- package/driver/skills/placement-inquiry/SKILL.md +1 -1
- package/driver/skills/whatif-memo/SKILL.md +1 -1
- package/driver/stage-context.mjs +2 -1
- package/driver/stages-knockout.mjs +1 -1
- package/driver/stages.mjs +95 -60
- package/driver/suite-census.json +367 -19
- package/driver/synthesis-record.mjs +310 -8
- package/driver/terminal-clamp.mjs +53 -2
- package/driver/verify-knockout.mjs +22 -4
- package/driver/verify.mjs +23 -3
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/lib/driver.mjs +5 -0
- package/mcp-server/lib/events.mjs +25 -4
- package/mcp-server/lib/findings.mjs +4 -2
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/jx/src/core.js +3 -4
- package/providers/jx/src/judge.js +2 -2
- package/providers/jx/src/nativeread.js +1 -2
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/scripts/e2e-first-time.mjs +29 -12
|
@@ -157,7 +157,7 @@ export const E3_BACKLOG = [
|
|
|
157
157
|
},
|
|
158
158
|
// ── DISCHARGED 2026-08-17 by, and the row said this could not happen ──────────────────────────
|
|
159
159
|
//
|
|
160
|
-
// It read: stage "blind-frame", where "driver/stages.mjs:
|
|
160
|
+
// It read: stage "blind-frame", where "driver/stages.mjs:981", evidence "Emit the STRUCTURED model
|
|
161
161
|
// (dictated keys + closed enums per the skill). It is your ONLY output file — do NOT write a prose
|
|
162
162
|
// companion.", reparsedBy "verify.mjs validators.blindFrame …", removedByMove **"NOTHING ON THE PLAN
|
|
163
163
|
// REMOVES THIS"**.
|
|
@@ -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:1102 (emitted at 1891, 1920, 2100, 2320)",
|
|
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",
|
|
@@ -218,7 +218,7 @@ export const E3_BACKLOG = [
|
|
|
218
218
|
{
|
|
219
219
|
stage: "common-law",
|
|
220
220
|
kind: "dictated-line-shape",
|
|
221
|
-
where: "driver/skills/clearance-common-law/SKILL.md:192 (restated at driver/skills/clearance-search/synthesis-rules.md:
|
|
221
|
+
where: "driver/skills/clearance-common-law/SKILL.md:192 (restated at driver/skills/clearance-search/synthesis-rules.md:391)",
|
|
222
222
|
surface: "skill-file",
|
|
223
223
|
evidence: "A clean PR/connotation row MUST cite its search — add a `**Connotation-search source:** <URL | \"perplexity_research — no result\">` line.",
|
|
224
224
|
reparsedBy: "driver/connotation-search.mjs — validators.commonLaw rejects a clean claim with no such line (connotation_search_missing); the hint is re-dictated by correctionHint() in gateway.mjs",
|
|
@@ -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:1063 and driver/stages.mjs:1068 (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:2307 (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:2270",
|
|
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)",
|
|
@@ -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:4248 (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 4164 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",
|
|
@@ -292,7 +292,7 @@ export const E3_BACKLOG = [
|
|
|
292
292
|
{
|
|
293
293
|
stage: "placement-inquiry",
|
|
294
294
|
kind: "literal-json-skeleton",
|
|
295
|
-
where: "driver/stages.mjs:
|
|
295
|
+
where: "driver/stages.mjs:2359-2353",
|
|
296
296
|
surface: "stage-message",
|
|
297
297
|
evidence: "PLACEMENT FORM (MANDATORY): record every placement in <path> — {\"rows\":[…]} … · A REGISTER candidate: {\"select\":\"<one record URI it holds>\",\"tier\":\"…\",\"reason\":\"…\"} (+ optional \"borderline\":true) … · A COMMON-LAW candidate …: {\"kind\":\"seat\",\"mark\",\"owner\",\"jurisdiction\",\"records\":[],\"tier\",\"reason\"}",
|
|
298
298
|
reparsedBy: "driver/placement-form.mjs (SELECT_ROW_FIELDS at placement-form.mjs:93, the seat-row contract SEAT_ROW_CONTRACT, formRowKey/rowIsSettled/renderEntry) via validators.placement. The same field list is ALSO carried in the driver-written form's own seat_row_contract, so the shape exists twice",
|
|
@@ -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:2468",
|
|
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",
|
|
@@ -347,7 +347,7 @@ export const E3_BACKLOG = [
|
|
|
347
347
|
// (coverage-form.mjs coverageFormBrief) and the tool schema, with every row receiver-validated at
|
|
348
348
|
// call time. Same dictated vocabulary, same authored site, new route — this row's evidence follows
|
|
349
349
|
// the dictation so the row keeps describing something that exists.
|
|
350
|
-
where: "driver/stages.mjs:
|
|
350
|
+
where: "driver/stages.mjs:2678 (the digest message) + driver/coverage-form.mjs (coverageFormBrief — the dispatch block carrying the enum and the row shape)",
|
|
351
351
|
surface: "stage-message",
|
|
352
352
|
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",
|
|
353
353
|
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",
|
|
@@ -430,7 +430,7 @@ export const E3_BACKLOG = [
|
|
|
430
430
|
{
|
|
431
431
|
stage: "frame-diff",
|
|
432
432
|
kind: "exactly-these-keys",
|
|
433
|
-
where: "driver/stages.mjs:
|
|
433
|
+
where: "driver/stages.mjs:2850",
|
|
434
434
|
surface: "stage-message",
|
|
435
435
|
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).",
|
|
436
436
|
reparsedBy: "driver/verify.mjs validators.frameDiff + driver/pipeline.mjs runSupplementalSweeps (the parser REFUSES a firing variant directive that dictates nothing dispatchable)",
|
|
@@ -454,7 +454,7 @@ export const E3_BACKLOG = [
|
|
|
454
454
|
{
|
|
455
455
|
stage: "synthesis",
|
|
456
456
|
kind: "literal-json-skeleton",
|
|
457
|
-
where: "driver/stages.mjs:
|
|
457
|
+
where: "driver/stages.mjs:3232",
|
|
458
458
|
surface: "stage-message",
|
|
459
459
|
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",
|
|
460
460
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson via validators.narrative",
|
|
@@ -468,7 +468,7 @@ export const E3_BACKLOG = [
|
|
|
468
468
|
{
|
|
469
469
|
stage: "synthesis",
|
|
470
470
|
kind: "exactly-these-keys",
|
|
471
|
-
where: "driver/stages.mjs:
|
|
471
|
+
where: "driver/stages.mjs:3265",
|
|
472
472
|
surface: "stage-message",
|
|
473
473
|
evidence: "- off_field_ground (MANDATORY on every off-field finding, FORBIDDEN on every other disposition): EXACTLY one bare token of: ${OFF_FIELD_GROUNDS.join(\" / \")}",
|
|
474
474
|
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",
|
|
@@ -477,7 +477,7 @@ export const E3_BACKLOG = [
|
|
|
477
477
|
{
|
|
478
478
|
stage: "synthesis",
|
|
479
479
|
kind: "literal-json-skeleton",
|
|
480
|
-
where: "driver/stages.mjs:
|
|
480
|
+
where: "driver/stages.mjs:3266",
|
|
481
481
|
surface: "stage-message",
|
|
482
482
|
evidence: "- manageable …: {\"category\":\"<EXACTLY one of large-competitor / commercial-partner / troll / well-known-enforcer>\",\"reason\":\"<one-two lines…>\"}",
|
|
483
483
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson",
|
|
@@ -486,7 +486,7 @@ export const E3_BACKLOG = [
|
|
|
486
486
|
{
|
|
487
487
|
stage: "synthesis",
|
|
488
488
|
kind: "literal-json-skeleton",
|
|
489
|
-
where: "driver/stages.mjs:
|
|
489
|
+
where: "driver/stages.mjs:3282",
|
|
490
490
|
surface: "stage-message",
|
|
491
491
|
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",
|
|
492
492
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson; driver/verify.mjs:1171 checkFindingsSibling gates meters.*.source; finding_basis_source_missing",
|
|
@@ -495,7 +495,7 @@ export const E3_BACKLOG = [
|
|
|
495
495
|
{
|
|
496
496
|
stage: "synthesis",
|
|
497
497
|
kind: "literal-json-skeleton",
|
|
498
|
-
where: "driver/stages.mjs:
|
|
498
|
+
where: "driver/stages.mjs:3283",
|
|
499
499
|
surface: "stage-message",
|
|
500
500
|
evidence: "- quadrant: {\"x\",\"y\"} numbers in [0,1]. x = goods/services proximity (0 = distant, 1 = identical). y = mark similarity (0 = distinct, 1 = identical).",
|
|
501
501
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson",
|
|
@@ -504,7 +504,7 @@ export const E3_BACKLOG = [
|
|
|
504
504
|
{
|
|
505
505
|
stage: "synthesis",
|
|
506
506
|
kind: "literal-json-skeleton",
|
|
507
|
-
where: "driver/stages.mjs:
|
|
507
|
+
where: "driver/stages.mjs:3284",
|
|
508
508
|
surface: "stage-message",
|
|
509
509
|
evidence: "- source: {\"source_type\",\"resolved_link\"}. source_type EXACTLY one of: register-vendor / register-euipo / common-law-marketplace / common-law-web / case-law",
|
|
510
510
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson",
|
|
@@ -513,7 +513,7 @@ export const E3_BACKLOG = [
|
|
|
513
513
|
{
|
|
514
514
|
stage: "synthesis",
|
|
515
515
|
kind: "exactly-these-keys",
|
|
516
|
-
where: "driver/stages.mjs:
|
|
516
|
+
where: "driver/stages.mjs:3285",
|
|
517
517
|
surface: "stage-message",
|
|
518
518
|
evidence: "coverage[]: ONE object per coverage AREA, EXACTLY {\"area\",\"state\",\"note\"}. … state EXACTLY one of: confirmed-clean / coverage-limited / open / not-searched / note.",
|
|
519
519
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson; the render owns the coverage panel from these typed states",
|
|
@@ -522,7 +522,7 @@ export const E3_BACKLOG = [
|
|
|
522
522
|
{
|
|
523
523
|
stage: "synthesis",
|
|
524
524
|
kind: "literal-json-skeleton",
|
|
525
|
-
where: "driver/stages.mjs:
|
|
525
|
+
where: "driver/stages.mjs:3294",
|
|
526
526
|
surface: "stage-message",
|
|
527
527
|
evidence: "use_check = {\"source\",\"quality\"}: … quality: OPTIONAL, EXACTLY one of owner-site / independent / register-mirror … own_rights = {\"source\"}",
|
|
528
528
|
reparsedBy: "driver/verify.mjs checkFindingsSibling (finding_use_check_missing); driver/own-rights.mjs:19-22",
|
|
@@ -531,7 +531,7 @@ export const E3_BACKLOG = [
|
|
|
531
531
|
{
|
|
532
532
|
stage: "synthesis",
|
|
533
533
|
kind: "literal-json-skeleton",
|
|
534
|
-
where: "driver/stages.mjs:
|
|
534
|
+
where: "driver/stages.mjs:3351",
|
|
535
535
|
surface: "stage-message",
|
|
536
536
|
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>",
|
|
537
537
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson; the report collapses the rows behind toggles and the audit workbook renders them",
|
|
@@ -540,7 +540,7 @@ export const E3_BACKLOG = [
|
|
|
540
540
|
{
|
|
541
541
|
stage: "synthesis",
|
|
542
542
|
kind: "literal-json-skeleton",
|
|
543
|
-
where: "driver/stages.mjs:
|
|
543
|
+
where: "driver/stages.mjs:3357",
|
|
544
544
|
surface: "stage-message",
|
|
545
545
|
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",
|
|
546
546
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson",
|
|
@@ -549,7 +549,7 @@ export const E3_BACKLOG = [
|
|
|
549
549
|
{
|
|
550
550
|
stage: "synthesis",
|
|
551
551
|
kind: "literal-json-skeleton",
|
|
552
|
-
where: "driver/stages.mjs:
|
|
552
|
+
where: "driver/stages.mjs:3231",
|
|
553
553
|
surface: "stage-message",
|
|
554
554
|
evidence: "ACTIONS REGISTER …: emit \"actions\": [...] — ONE object per forward step …, each EXACTLY {\"id\",\"kind\",\"text\",\"ordinals\"} plus an OPTIONAL \"deadline\" and an OPTIONAL \"condition\". … kind: EXACTLY one of — consent / coexistence-agreement / territorial-delimitation / goods-amendment / mark-modification /",
|
|
555
555
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson; pipeline applyCoverageFloor legalActions arm derives the delivered disposition from the closed kind enum",
|
|
@@ -558,7 +558,7 @@ export const E3_BACKLOG = [
|
|
|
558
558
|
{
|
|
559
559
|
stage: "synthesis",
|
|
560
560
|
kind: "exactly-these-keys",
|
|
561
|
-
where: "driver/stages.mjs:
|
|
561
|
+
where: "driver/stages.mjs:3364",
|
|
562
562
|
surface: "stage-message",
|
|
563
563
|
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.",
|
|
564
564
|
reparsedBy: "driver/findings-model.mjs:848 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",
|
|
@@ -567,7 +567,7 @@ export const E3_BACKLOG = [
|
|
|
567
567
|
{
|
|
568
568
|
stage: "synthesis",
|
|
569
569
|
kind: "literal-json-skeleton",
|
|
570
|
-
where: "driver/stages.mjs:
|
|
570
|
+
where: "driver/stages.mjs:3210",
|
|
571
571
|
surface: "stage-message",
|
|
572
572
|
// RE-QUOTED, NOT PARKED. The writer's conversion reworded this dictation — the
|
|
573
573
|
// ask answers ride the findings RECORD now and the driver renders the labelled line into both the
|
|
@@ -591,7 +591,7 @@ export const E3_BACKLOG = [
|
|
|
591
591
|
{
|
|
592
592
|
stage: "synthesis",
|
|
593
593
|
kind: "dictated-line-shape",
|
|
594
|
-
where: "driver/stages.mjs:
|
|
594
|
+
where: "driver/stages.mjs:3129 (restated at driver/skills/clearance-search/synthesis-rules.md:425)",
|
|
595
595
|
surface: "stage-message",
|
|
596
596
|
evidence: "END that finding's actual-use line with a literal \"- **Use-check source:** <result URL | \"perplexity_research — no result\">\" line",
|
|
597
597
|
reparsedBy: "driver/verify.mjs validators.narrative (spec-11 hard reject); the repair hint re-dictates the literal in correctionHint() in gateway.mjs",
|
|
@@ -600,7 +600,7 @@ export const E3_BACKLOG = [
|
|
|
600
600
|
{
|
|
601
601
|
stage: "synthesis",
|
|
602
602
|
kind: "dictated-line-shape",
|
|
603
|
-
where: "driver/stages.mjs:
|
|
603
|
+
where: "driver/stages.mjs:3142 (restated at driver/skills/clearance-search/synthesis-rules.md:472)",
|
|
604
604
|
surface: "stage-message",
|
|
605
605
|
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",
|
|
606
606
|
reparsedBy: "driver/own-rights.mjs, in its header — \"This module only requires the 'Own-rights source:' line to exist\"; repair hint in correctionHint() in gateway.mjs (the `own_rights_missing` branch; re-verified 2026-08-29 — the old :1736 predated this branch and pointed into the A4 repeat-signature block)",
|
|
@@ -609,7 +609,7 @@ export const E3_BACKLOG = [
|
|
|
609
609
|
{
|
|
610
610
|
stage: "synthesis",
|
|
611
611
|
kind: "exactly-these-keys",
|
|
612
|
-
where: "driver/stages.mjs:
|
|
612
|
+
where: "driver/stages.mjs:3343",
|
|
613
613
|
surface: "stage-message",
|
|
614
614
|
evidence: "add it to the top-level \"context_notes\" array — each object EXACTLY {\"type\":\"famous-neighbour-ungrounded\",\"mark\",\"owner\",\"context\"}",
|
|
615
615
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson",
|
|
@@ -618,7 +618,7 @@ export const E3_BACKLOG = [
|
|
|
618
618
|
{
|
|
619
619
|
stage: "synthesis",
|
|
620
620
|
kind: "literal-json-skeleton",
|
|
621
|
-
where: "driver/stages.mjs:
|
|
621
|
+
where: "driver/stages.mjs:3234",
|
|
622
622
|
surface: "stage-message",
|
|
623
623
|
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",
|
|
624
624
|
reparsedBy: "driver/findings-model.mjs:848 parseFindingsJson + the record-binding join. Six of the seven keys are stated in the prompt and overwritten by code in the same sentence",
|
|
@@ -627,7 +627,7 @@ export const E3_BACKLOG = [
|
|
|
627
627
|
{
|
|
628
628
|
stage: "case-law",
|
|
629
629
|
kind: "literal-json-skeleton",
|
|
630
|
-
where: "driver/stages.mjs:
|
|
630
|
+
where: "driver/stages.mjs:3513",
|
|
631
631
|
surface: "stage-message",
|
|
632
632
|
evidence: "ALSO write the RETRIEVAL RECORD to <path> — a JSON OBJECT with EXACTLY these keys: {\"schema_version\":2,\"queries\":[{\"query\":\"<the search you dispatched, verbatim>\",\"jurisdiction\":\"…\",\"results\":<how many hits it returned, or null when the query never reached a source — never 0 for a query you could not send>}, …],\"citations\":[{\"proceeding\":\"…\",\"forum\":\"…\",\"jurisdiction\":\"…\",\"decided\":\"…\"",
|
|
633
633
|
reparsedBy: "driver/verify.mjs validators.caseLaw — the ledger arm, armed by the stage-contract marker `citations` (the case-law stage's `contract: { citations: 1 }` in stages.mjs)",
|
|
@@ -636,7 +636,7 @@ export const E3_BACKLOG = [
|
|
|
636
636
|
{
|
|
637
637
|
stage: "case-law",
|
|
638
638
|
kind: "dictated-line-shape",
|
|
639
|
-
where: "driver/stages.mjs:
|
|
639
|
+
where: "driver/stages.mjs:3521",
|
|
640
640
|
surface: "stage-message",
|
|
641
641
|
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)",
|
|
642
642
|
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:274 /^-\\s*ord:\\s*(\\d+)\\s*$/m; driver/publish/index.mjs publishReport (joinCaseLawProfiles)",
|
|
@@ -670,7 +670,7 @@ export const E3_BACKLOG = [
|
|
|
670
670
|
{
|
|
671
671
|
stage: "narrative-refutation",
|
|
672
672
|
kind: "dictated-line-shape",
|
|
673
|
-
where: "driver/stages.mjs:
|
|
673
|
+
where: "driver/stages.mjs:4797",
|
|
674
674
|
surface: "stage-message",
|
|
675
675
|
// RE-QUOTED BY CONVERSION 9. The LINE-TOKEN half is gone — no "anywhere on the line", no "[kind: …]
|
|
676
676
|
// token", no "a line with no token is treated as fact", because a kind is a typed field now and an
|
|
@@ -704,7 +704,7 @@ export const E3_BACKLOG = [
|
|
|
704
704
|
{
|
|
705
705
|
stage: "narrative-refutation",
|
|
706
706
|
kind: "dictated-line-shape",
|
|
707
|
-
where: "driver/stages.mjs:
|
|
707
|
+
where: "driver/stages.mjs:1879 and driver/skills/narrative-refutation/SKILL.md:41-50",
|
|
708
708
|
// RE-QUOTED 2026-08-16. The dictated SENTENCE survives in narrative-refutation's SKILL.md — it is the
|
|
709
709
|
// `evidence` below, verbatim. What changed is that the four kinds moved from an inline list after the
|
|
710
710
|
// colon onto their own `- [kind: …]` bullet lines. Measured, not assumed: the old anchor matched 59 of
|
|
@@ -769,7 +769,7 @@ export const E3_BACKLOG = [
|
|
|
769
769
|
{
|
|
770
770
|
stage: "report-card",
|
|
771
771
|
kind: "literal-json-skeleton",
|
|
772
|
-
where: "driver/stages.mjs:
|
|
772
|
+
where: "driver/stages.mjs:3960",
|
|
773
773
|
surface: "stage-message",
|
|
774
774
|
evidence: "The finding's OWN record — the ONLY source for this card …:\\n```json\\n<JSON.stringify(finding, null, 2)>\\n```",
|
|
775
775
|
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",
|
|
@@ -859,7 +859,7 @@ export const E3_EVIDENCE_UNRESOLVED = [
|
|
|
859
859
|
// absorbed. It was the narrative "## Answers to your instructions" row deleted above: its dictation is
|
|
860
860
|
// gone, so there is no un-anchorable quote left to be unresolved. The one that REMAINS is a live row
|
|
861
861
|
// whose quote spans a template line. Measured before deleting, not predicted — two rows shared
|
|
862
|
-
// `where: "driver/stages.mjs:
|
|
862
|
+
// `where: "driver/stages.mjs:3083"`, so which one occupied this slot could not be read off the strings.
|
|
863
863
|
"synthesis|driver/stages.mjs",
|
|
864
864
|
];
|
|
865
865
|
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"dictated-line-shape": 1
|
|
37
37
|
},
|
|
38
38
|
"register-digest": {
|
|
39
|
-
"literal-json-skeleton":
|
|
39
|
+
"literal-json-skeleton": 0,
|
|
40
40
|
"exactly-these-keys": 0,
|
|
41
|
-
"dictated-line-shape":
|
|
41
|
+
"dictated-line-shape": 1
|
|
42
42
|
},
|
|
43
43
|
"skeptic": {
|
|
44
44
|
"literal-json-skeleton": 0,
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"skills/clearance-register/SKILL.md": {
|
|
121
121
|
"literal-json-skeleton": 0,
|
|
122
122
|
"exactly-these-keys": 0,
|
|
123
|
-
"dictated-line-shape":
|
|
123
|
+
"dictated-line-shape": 3
|
|
124
124
|
},
|
|
125
125
|
"skills/clearance-register/unit.md": {
|
|
126
126
|
"literal-json-skeleton": 5,
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
}
|
|
190
190
|
},
|
|
191
191
|
"totals": {
|
|
192
|
-
"stages":
|
|
193
|
-
"files":
|
|
192
|
+
"stages": 43,
|
|
193
|
+
"files": 63
|
|
194
194
|
}
|
|
195
195
|
}
|
|
@@ -474,21 +474,21 @@ export const INNER_CODES = Object.freeze([
|
|
|
474
474
|
// `no_recorded_queries`, and the `connotation_` family row (dynamic D1) is declared against exactly
|
|
475
475
|
// that list. The four call codes are folded by a template — `connotation_${callFail.reason}` at
|
|
476
476
|
// verify.mjs — so they are namespaced by construction rather than one branch at a time.
|
|
477
|
-
{ code: "call_never_made", mints: ["driver/connotation-search.mjs:
|
|
477
|
+
{ code: "call_never_made", mints: ["driver/connotation-search.mjs:1979"], rollsUpTo: ["connotation_call_never_made"],
|
|
478
478
|
why: "CALL_AUDIT_ROWS. The typed transport's four call states, handed in by disposition-call-audit.mjs and namespaced at verify.mjs." },
|
|
479
|
-
{ code: "call_truncated", mints: ["driver/connotation-search.mjs:
|
|
479
|
+
{ code: "call_truncated", mints: ["driver/connotation-search.mjs:1980"], rollsUpTo: ["connotation_call_truncated"],
|
|
480
480
|
why: "As call_never_made — same table, same projection." },
|
|
481
|
-
{ code: "call_schema_violation", mints: ["driver/connotation-search.mjs:
|
|
481
|
+
{ code: "call_schema_violation", mints: ["driver/connotation-search.mjs:1981"], rollsUpTo: ["connotation_call_schema_violation"],
|
|
482
482
|
why: "As call_never_made — same table, same projection." },
|
|
483
|
-
{ code: "call_partial", mints: ["driver/connotation-search.mjs:
|
|
483
|
+
{ code: "call_partial", mints: ["driver/connotation-search.mjs:1982"], rollsUpTo: ["connotation_call_partial"],
|
|
484
484
|
why: "As call_never_made — same table, same projection. This is the pair #1211 cites as its worked example: the composite is covered, the bare form reaches no stage." },
|
|
485
|
-
{ code: "quote_unbound", mints: ["driver/connotation-search.mjs:
|
|
485
|
+
{ code: "quote_unbound", mints: ["driver/connotation-search.mjs:2113"], rollsUpTo: ["connotation_quote_unbound"],
|
|
486
486
|
why: "The ruled-but-unbound row. Projected as `unbound` in connotationDispositionFail() in verify.mjs, and reported only once nothing is unruled." },
|
|
487
|
-
{ code: "token_absent", mints: ["driver/connotation-search.mjs:
|
|
487
|
+
{ code: "token_absent", mints: ["driver/connotation-search.mjs:2140"], rollsUpTo: ["connotation_token_absent"],
|
|
488
488
|
why: "#592 split this out of no_ruling. Row-level only — PROGRESS_TOKENS declared in repairs.mjs says so in as many words: `never a top-level token`." },
|
|
489
|
-
{ code: "cite_absent", mints: ["driver/connotation-search.mjs:
|
|
489
|
+
{ code: "cite_absent", mints: ["driver/connotation-search.mjs:2149"], rollsUpTo: ["connotation_cite_absent"],
|
|
490
490
|
why: "#592, as token_absent. PROGRESS_TOKENS declared in repairs.mjs: `row-level only — never a top-level token`." },
|
|
491
|
-
{ code: "no_ruling", mints: ["driver/connotation-search.mjs:
|
|
491
|
+
{ code: "no_ruling", mints: ["driver/connotation-search.mjs:2156"], rollsUpTo: ["connotation_no_ruling"],
|
|
492
492
|
why: "The residual the other two split off from, and it has representatives — see the mint site. Declared as a corrective token at repairs.mjs:308 and in stages.mjs's E1 rows, always namespaced." },
|
|
493
493
|
// THE ONE THAT IS RENAMED, NOT NAMESPACED — and it is the reason this table stores the composite as
|
|
494
494
|
// data rather than deriving it. Every derivation anyone would write is `connotation_` + the code, and
|
|
@@ -496,7 +496,7 @@ export const INNER_CODES = Object.freeze([
|
|
|
496
496
|
// verify.mjs's block headed "the meaning-sweep failure tokens" states the ruling: a sweep that did not
|
|
497
497
|
// RUN is a canonical-only decision with its own token and its own remedy, so the projector,
|
|
498
498
|
// connotationDispositionFail() in verify.mjs, deliberately does not handle it.
|
|
499
|
-
{ code: "no_recorded_queries", mints: ["driver/connotation-search.mjs:
|
|
499
|
+
{ code: "no_recorded_queries", mints: ["driver/connotation-search.mjs:1997"], rollsUpTo: ["connotation_search_missing"],
|
|
500
500
|
why: "RENAMED, not namespaced: verify.mjs emits `connotation_search_missing`. It is excluded from CONNOTATION_FORM_REASONS at connotation-search.mjs for exactly this reason." },
|
|
501
501
|
|
|
502
502
|
// ── case-law-ledger.mjs — ONE token, the codes as census payload ─────────────────────────────────
|
|
@@ -530,9 +530,9 @@ export const INNER_CODES = Object.freeze([
|
|
|
530
530
|
// ONE CODE, TWO TOKENS, and the split is by `cause` rather than by code: verify.mjs peels the
|
|
531
531
|
// `axis_invalid` cause into its own family before counting the rest. A ruling naming only
|
|
532
532
|
// `coverage_no_status` would be true of most `no_status` records and false of the ones that matter most.
|
|
533
|
-
{ code: "no_status", mints: ["driver/coverage-form.mjs:
|
|
533
|
+
{ code: "no_status", mints: ["driver/coverage-form.mjs:897"], rollsUpTo: ["coverage_no_status", "coverage_form_axis_invalid"],
|
|
534
534
|
why: "Two composites, split on the record's `cause`: coverageFormFail() in verify.mjs for cause `axis_invalid`, and the same function for the rest. NOT the `@returns` annotation of findCoverageFormViolations() in coverage-form.mjs — that is the annotation, not the mint." },
|
|
535
|
-
{ code: "engine_vocabulary", mints: ["driver/coverage-form.mjs:
|
|
535
|
+
{ code: "engine_vocabulary", mints: ["driver/coverage-form.mjs:887"], rollsUpTo: ["coverage_form_engine_vocabulary"],
|
|
536
536
|
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 by coverageFormFail() in verify.mjs; the bare code names a row, never a stage." },
|
|
537
537
|
|
|
538
538
|
// ── ONE CODE, TWO MODULES ────────────────────────────────────────────────────────────────────────
|
|
@@ -544,7 +544,7 @@ export const INNER_CODES = Object.freeze([
|
|
|
544
544
|
// failure tokens" already states the rule this row records: `coverage_form_damaged`, never a bare
|
|
545
545
|
// `form_damaged`.
|
|
546
546
|
{ code: "form_damaged",
|
|
547
|
-
mints: ["driver/connotation-search.mjs:
|
|
547
|
+
mints: ["driver/connotation-search.mjs:2002", "driver/connotation-search.mjs:2078", "driver/coverage-form.mjs:879"],
|
|
548
548
|
rollsUpTo: ["connotation_form_damaged", "coverage_form_damaged"],
|
|
549
549
|
why: "Minted in two lanes and namespaced per lane: connotationDispositionFail() in verify.mjs for the meaning sweep, coverageFormFail() in verify.mjs for the register digest. The namespacing is what keeps them apart — see verify.mjs's block headed \"the register coverage-form failure tokens\"." },
|
|
550
550
|
]);
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
// corrections-delivered.mjs — whether each reviewer correction is still in the record the report is built from.
|
|
4
|
+
//
|
|
5
|
+
// corrections-applied.json records what the corrective pass changed, flag by flag, compared at the pass. It
|
|
6
|
+
// cannot say whether the change is still there when the report is assembled: later saves re-send the record
|
|
7
|
+
// (the delivery check's redo, the stale repair, a re-emit), the delivery seam folds it, and the report is built
|
|
8
|
+
// after all of them. This answers that question for the typed fields a report prints from the record — a
|
|
9
|
+
// finding's sentence (`net`) and its two positions, a coverage row's note, an action's text, and the mark
|
|
10
|
+
// assessment's distinctiveness and connotation — in two steps:
|
|
11
|
+
//
|
|
12
|
+
// correctionTargets at the corrective pass, where the records before and after it are in hand: what each
|
|
13
|
+
// flag changed in those fields, with the text the pass left (the basis);
|
|
14
|
+
// deliveredOutcomes after an assembly: whether the record the report was built from still carries it.
|
|
15
|
+
//
|
|
16
|
+
// Each flag's outcome:
|
|
17
|
+
// delivered every field it changed reads, in the delivered record, as the pass left it;
|
|
18
|
+
// not-found at least one does not: a later save or the fold did not carry it to the report;
|
|
19
|
+
// not-applicable it changed none of these fields — a narrative or structure correction, a reasoned
|
|
20
|
+
// no-change, a removal, a rating — so there is no typed text to look for. Narrative prose
|
|
21
|
+
// reaches the client only through the overview's own words, and this does not guess at it;
|
|
22
|
+
// not-checkable the records before and after the pass could not both be read, so nothing was compared.
|
|
23
|
+
//
|
|
24
|
+
// THE RECORD DECIDES, report.md IS A SECOND LOOK. Every client surface renders these fields from the delivered
|
|
25
|
+
// record — findings.json as the report was built from it, folded after the delivery seam — so the outcome is
|
|
26
|
+
// read there, joined in the model's numbering through the fold's map. report.md prints two of them itself, the
|
|
27
|
+
// finding sentence on a finding's card and an action's text, and each such change says whether report.md
|
|
28
|
+
// carries it (`inReportMd`, null for a field report.md does not print). It never decides the outcome: whether a
|
|
29
|
+
// finding has a card is the assembly's choice, not the correction's fate.
|
|
30
|
+
//
|
|
31
|
+
// PURE — no node imports. The pipeline reads and writes the files.
|
|
32
|
+
import { reportLines, linesOf } from "./corrections-feedforward.mjs";
|
|
33
|
+
|
|
34
|
+
/** The finding fields a correction can change that a report prints from the record. */
|
|
35
|
+
export const DELIVERED_FINDING_FIELDS = Object.freeze(["net", "legal_position", "practical_position"]);
|
|
36
|
+
|
|
37
|
+
const squash = (s) => String(s ?? "").replace(/\s+/g, " ").trim();
|
|
38
|
+
// The name join `buildCorrectionsApplied` uses: mark first, then owner, first one wins.
|
|
39
|
+
const norm = (s) => String(s ?? "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
40
|
+
function byName(doc) {
|
|
41
|
+
const out = new Map();
|
|
42
|
+
for (const f of doc?.findings ?? [])
|
|
43
|
+
for (const name of [f?.mark, f?.owner?.name]) { const n = norm(name); if (n && !out.has(n)) out.set(n, f); }
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
const byOrdinal = (doc) => new Map((doc?.findings ?? []).filter((f) => Number.isInteger(f?.ordinal)).map((f) => [f.ordinal, f]));
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The basis: for each row `buildCorrectionsApplied` returned, the printed fields the pass changed and the text it
|
|
50
|
+
* left in each. PURE. `preDoc` and `postDoc` are the records before and after the pass, in the model's numbering —
|
|
51
|
+
* the two the applied table was built from. A finding is the one the flag declared by ordinal, else the one it
|
|
52
|
+
* named; a line is the one the flag named or quoted, as the applied table joined it.
|
|
53
|
+
*/
|
|
54
|
+
export function correctionTargets(rows, preDoc, postDoc) {
|
|
55
|
+
const checkable = Boolean(preDoc && postDoc);
|
|
56
|
+
const preOrd = byOrdinal(preDoc), postOrd = byOrdinal(postDoc);
|
|
57
|
+
const preName = byName(preDoc), postName = byName(postDoc);
|
|
58
|
+
const preLines = reportLines(preDoc);
|
|
59
|
+
const postLines = new Map(reportLines(postDoc).map((l) => [l.key, l.text]));
|
|
60
|
+
return (Array.isArray(rows) ? rows : []).map((r) => {
|
|
61
|
+
const out = { n: r?.n ?? null, kind: r?.kind ?? null, applied: r?.outcome ?? null, changes: [] };
|
|
62
|
+
if (!checkable || r?.outcome === "not-checkable") return { ...out, why: "not-checkable" };
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
const add = (c, key) => { if (!seen.has(key)) { seen.add(key); out.changes.push(c); } };
|
|
65
|
+
if (r.outcome === "line-changed") {
|
|
66
|
+
for (const l of linesOf(r.text, preLines)) {
|
|
67
|
+
const now = postLines.get(l.key);
|
|
68
|
+
if (now !== undefined && squash(now) !== squash(l.text)) add({ line: l.key, text: now }, l.key);
|
|
69
|
+
}
|
|
70
|
+
} else if (r.outcome === "findings-changed") {
|
|
71
|
+
const declared = (Array.isArray(r.ordinals) ? r.ordinals : []).filter((o) => preOrd.has(o) || postOrd.has(o));
|
|
72
|
+
const pairs = declared.length ? declared.map((o) => [preOrd.get(o), postOrd.get(o)])
|
|
73
|
+
: (Array.isArray(r.targets) ? r.targets : []).map((t) => [preName.get(norm(t)), postName.get(norm(t))]);
|
|
74
|
+
for (const [a, b] of pairs) {
|
|
75
|
+
if (!b || !Number.isInteger(b.ordinal)) continue; // gone after the pass: nothing left to deliver
|
|
76
|
+
for (const field of DELIVERED_FINDING_FIELDS) {
|
|
77
|
+
if (squash(a?.[field]) !== squash(b[field])) add({ ordinal: b.ordinal, field, text: String(b[field] ?? "") }, `${b.ordinal}|${field}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out.changes.length ? out : { ...out, why: "no-printed-field-changed" };
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Each basis row's outcome against the delivered record. PURE. `deliveredDoc` is the record the report was built
|
|
87
|
+
* from; `foldMap` is the fold's `{modelToFolded}` when a fold is in force, else null; `reportMd` is report.md's
|
|
88
|
+
* text, or null when it could not be read. Returns null when there is no delivered record to read: a caller
|
|
89
|
+
* records that as a failure to look, never as an outcome.
|
|
90
|
+
*/
|
|
91
|
+
export function deliveredOutcomes({ basis, deliveredDoc, foldMap = null, reportMd = null } = {}) {
|
|
92
|
+
if (!Array.isArray(basis) || !deliveredDoc || typeof deliveredDoc !== "object") return null;
|
|
93
|
+
const findings = byOrdinal(deliveredDoc);
|
|
94
|
+
const lines = new Map(reportLines(deliveredDoc).map((l) => [l.key, l.text]));
|
|
95
|
+
const md = typeof reportMd === "string" ? squash(reportMd) : null;
|
|
96
|
+
const folded = (o) => { const f = foldMap?.modelToFolded?.[String(o)]; return f == null ? o : Number(f); };
|
|
97
|
+
return basis.map((b) => {
|
|
98
|
+
const head = { n: b?.n ?? null, kind: b?.kind ?? null, applied: b?.applied ?? null };
|
|
99
|
+
if (b?.why === "not-checkable") return { ...head, outcome: "not-checkable", changes: [] };
|
|
100
|
+
const changes = (Array.isArray(b?.changes) ? b.changes : []).map((c) => {
|
|
101
|
+
const onFinding = c?.ordinal != null;
|
|
102
|
+
const where = onFinding ? { ordinal: c.ordinal, deliveredAs: folded(c.ordinal), field: c.field } : { line: c?.line };
|
|
103
|
+
const inRecord = onFinding
|
|
104
|
+
? findings.has(where.deliveredAs) && squash(findings.get(where.deliveredAs)[c.field]) === squash(c.text)
|
|
105
|
+
: lines.has(c?.line) && squash(lines.get(c.line)) === squash(c.text);
|
|
106
|
+
const printedInReportMd = (onFinding && c.field === "net") || (!onFinding && String(c?.line).startsWith("action:"));
|
|
107
|
+
const inReportMd = !printedInReportMd || md == null || !squash(c?.text) ? null : md.includes(squash(c.text));
|
|
108
|
+
return { ...where, inRecord, inReportMd };
|
|
109
|
+
});
|
|
110
|
+
if (!changes.length) return { ...head, outcome: "not-applicable", why: b?.why ?? "no-printed-field-changed", changes };
|
|
111
|
+
return { ...head, outcome: changes.every((c) => c.inRecord) ? "delivered" : "not-found", changes };
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -252,7 +252,10 @@ export function scopeDrift(rows, preDoc, postDoc) {
|
|
|
252
252
|
return { scoped: true, named: [...named], moved, unbound };
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
// `sections`, optional: the flags whose target is a narrative section, as `{n, label, before, after}` with the
|
|
256
|
+
// section's text before and after the pass. The review after a delivery-check repair names sections as items,
|
|
257
|
+
// so a flag on one can be checked; with no `sections` the output is what it always was.
|
|
258
|
+
export function buildCorrectionsApplied(rows, preDoc, postDoc, { sections = null } = {}) {
|
|
256
259
|
// THE UNION OF BOTH SIDES, deliberately. Reading the post doc alone loses a finding the pass REMOVED —
|
|
257
260
|
// and a removal is exactly when the flag would stop naming anything, so the post doc alone cannot even
|
|
258
261
|
// see the event.: what that removal is EVIDENCE of is the question this module used to get wrong.
|
|
@@ -286,12 +289,15 @@ export function buildCorrectionsApplied(rows, preDoc, postDoc) {
|
|
|
286
289
|
// — A LINE THAT IS NOT A FINDING, asked before the name match. A flag that names or quotes a coverage
|
|
287
290
|
// note, an action or the mark assessment is about that line, and a mark or owner named inside the
|
|
288
291
|
// line it quotes is incidental. Asked only where no ordinal was declared: a declaration still wins.
|
|
289
|
-
const
|
|
292
|
+
const sec = !declaredOrds.length ? (sections ?? []).find((x) => x.n === r.n) ?? null : null;
|
|
293
|
+
const hit = preDoc && !declaredOrds.length && !sec ? linesOf(r.text, preLines) : [];
|
|
290
294
|
const targets = declaredOrds.length ? declaredOrds.map(label)
|
|
295
|
+
: sec ? [sec.label]
|
|
291
296
|
: hit.length ? hit.map((l) => l.label) : targetsOf(r.text, known);
|
|
292
297
|
let outcome;
|
|
293
298
|
let removed = [];
|
|
294
|
-
if (
|
|
299
|
+
if (sec) outcome = squash(sec.after ?? "") !== squash(sec.before ?? "") ? "section-changed" : "section-unchanged";
|
|
300
|
+
else if (!preDoc) outcome = "not-checkable";
|
|
295
301
|
else if (hit.length) {
|
|
296
302
|
// The same three answers a finding gets, asked of the line's own text. REMOVAL WINS, as it does
|
|
297
303
|
// for a finding: a line gone after the pass was answered by deletion, and that is the question.
|
|
@@ -374,7 +380,7 @@ export function buildCorrectionsApplied(rows, preDoc, postDoc) {
|
|
|
374
380
|
*
|
|
375
381
|
* PURE.
|
|
376
382
|
*/
|
|
377
|
-
export const RESOLVED_OUTCOMES = Object.freeze(["findings-changed", "line-changed"]);
|
|
383
|
+
export const RESOLVED_OUTCOMES = Object.freeze(["findings-changed", "line-changed", "section-changed"]);
|
|
378
384
|
|
|
379
385
|
/** The rows to print. Anything not positively resolved, including a row of an outcome nobody has met. */
|
|
380
386
|
export const unresolvedFlags = (rows) =>
|
|
@@ -387,7 +393,7 @@ export const unresolvedFlags = (rows) =>
|
|
|
387
393
|
* twelve mixed lines has to re-derive that grouping for itself every time. The raw review still rides
|
|
388
394
|
* below it in the followup: this is a better index of the same evidence, not a replacement for it. PURE.
|
|
389
395
|
*/
|
|
390
|
-
export function correctionsWorklist(rows) {
|
|
396
|
+
export function correctionsWorklist(rows, { afterRepair = false } = {}) {
|
|
391
397
|
if (!rows?.length) return "";
|
|
392
398
|
const byKind = new Map();
|
|
393
399
|
for (const r of rows) { if (!byKind.has(r.kind)) byKind.set(r.kind, []); byKind.get(r.kind).push(r); }
|
|
@@ -400,9 +406,13 @@ export function correctionsWorklist(rows) {
|
|
|
400
406
|
// it rather than correcting it: the flag went away and the report did not become true. Stated as a
|
|
401
407
|
// constraint on the worklist itself, where the moves are chosen, rather than left to the recheck to
|
|
402
408
|
// catch afterwards — the recheck that caught it did so once, and once is luck.
|
|
409
|
+
//
|
|
410
|
+
// AFTER A DELIVERY-CHECK REPAIR NO REVIEW FOLLOWS THE CORRECTION, so "standing for the reviewer" would be
|
|
411
|
+
// false there: a point left standing is recorded for the reviewing lawyer instead.
|
|
403
412
|
out.push("",
|
|
404
413
|
"REMOVING A FLAGGED FINDING DOES NOT ANSWER ITS FLAG. Correct the fact, or say why it cannot be corrected "
|
|
405
|
-
+ "and leave it standing for the
|
|
414
|
+
+ (afterRepair ? "and leave it standing; the point is recorded for the reviewing lawyer. " : "and leave it standing for the reviewer. ")
|
|
415
|
+
+ "A finding you believe should not be there is WITHDRAWN with its "
|
|
406
416
|
+ "reason recorded, never deleted — a flag that stops naming anything reads as resolved, and the driver "
|
|
407
417
|
+ "reports the deletion by name either way.");
|
|
408
418
|
return out.join("\n");
|
|
@@ -9,17 +9,20 @@
|
|
|
9
9
|
//
|
|
10
10
|
// ── WHY THIS IS NOT SIMPLY "ALLOW THE WORD" ─────────────────────────────────────────────────────────
|
|
11
11
|
//
|
|
12
|
-
// The production corrective pass is a
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
12
|
+
// The production corrective pass is a FOLLOWUP dispatch of the synthesis stage, on the run's own key with
|
|
13
|
+
// the winning attempt's model and thinking, and its message is `correctionsExtra`'s blocks. It does not
|
|
14
|
+
// resume the synthesis session: no engine resumes by session key (stages-followup.test.mjs pins it), so
|
|
15
|
+
// the followup's first attempt is a fresh turn handed the corrections and the stage's inputs to read. An
|
|
16
|
+
// experiment arm always builds a fresh key and dispatches into a sandbox — and dispatching on the REAL run's
|
|
17
|
+
// key from a sandbox was rejected outright, because "canonical run untouched" is the arm's whole contract and
|
|
18
|
+
// an arm that breaks its contract to gain fidelity is measuring with a broken instrument.
|
|
17
19
|
//
|
|
18
20
|
// So an arm carries the corrective pass's INPUTS on a COLD turn, composed by the same function production
|
|
19
|
-
// uses. That reaches the seam nothing else reaches without a two-hour run. It is NOT
|
|
20
|
-
// the receipt says so in a word rather than leaving it to be inferred
|
|
21
|
+
// uses. That reaches the seam nothing else reaches without a two-hour run. It is NOT production's
|
|
22
|
+
// dispatch, and the receipt says so in a word rather than leaving it to be inferred. The production word
|
|
23
|
+
// is a recorded value and stays as it is, though the followup it names never resumes the synthesis session:
|
|
21
24
|
//
|
|
22
|
-
// dispatched-warm production only — the
|
|
25
|
+
// dispatched-warm production only — the followup on the run's own key
|
|
23
26
|
// dispatched-cold the arm — the same blocks, a fresh turn
|
|
24
27
|
// refused-no-corrective-state neither — this run has no corrective pass to reproduce
|
|
25
28
|
//
|