clearotron 0.3.2-beta.6 → 0.3.2-beta.8
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/.env.example +24 -23
- package/INSTALL.md +142 -75
- package/README.md +3 -3
- package/bin/onboard.mjs +637 -216
- package/bin/start.mjs +133 -23
- package/bin/update.mjs +82 -11
- package/build-info.json +2 -2
- package/docs/architecture/04-configuration-reference.md +26 -11
- package/docs/architecture/05-config-governance.md +17 -7
- package/driver/CHANGELOG.md +83 -0
- package/driver/band-size.mjs +59 -0
- package/driver/config-inventory.mjs +112 -9
- package/driver/connotation-search.mjs +45 -0
- package/driver/contract-arm2-baseline.json +1 -3
- package/driver/contract-e3-backlog.mjs +29 -29
- package/driver/contract-vocabulary.mjs +59 -25
- package/driver/door-gates.mjs +41 -7
- package/driver/driver.config.mjs +272 -59
- package/driver/engine/CONTRACT.md +10 -3
- package/driver/engine/README.md +2 -2
- package/driver/engine/anthropic-agent.mjs +77 -21
- package/driver/engine/auth.mjs +129 -10
- package/driver/engine/jx-turn.mjs +7 -6
- package/driver/engine/mcp/recording-server.mjs +13 -0
- package/driver/engine/openai-agent.mjs +4 -2
- package/driver/engine/probe.mjs +110 -23
- package/driver/findings-model.mjs +1 -1
- package/driver/flag-snapshot.mjs +28 -5
- package/driver/gateway.mjs +30 -21
- package/driver/jx-lanes.mjs +21 -2
- package/driver/jx-units.mjs +6 -3
- package/driver/jx.mjs +4 -2
- package/driver/matter-frame-record.mjs +90 -1
- package/driver/named-band.mjs +34 -2
- package/driver/package.json +1 -1
- package/driver/pipeline.mjs +391 -26
- package/driver/portal-config-view.mjs +30 -1
- package/driver/portal-report.mjs +15 -1
- package/driver/portal-service.mjs +46 -6
- package/driver/predelivery-lint.mjs +12 -2
- package/driver/publish/index.mjs +46 -5
- package/driver/publish/knockout.mjs +10 -1
- package/driver/publish/render-knockout.mjs +69 -7
- package/driver/publish/render.mjs +170 -59
- package/driver/publish/report-data.mjs +4 -1
- package/driver/publish/report-topbar.mjs +58 -0
- package/driver/publish/templates/report.css +28 -2
- package/driver/publish/xlsx.mjs +13 -1
- package/driver/register-availability.mjs +2 -2
- package/driver/register-coverage.mjs +94 -1
- package/driver/register-digest-record.mjs +236 -11
- package/driver/register-plan.mjs +170 -0
- package/driver/result-noun-fields.mjs +7 -4
- package/driver/run-economics.mjs +41 -10
- package/driver/run-requirements.mjs +173 -9
- package/driver/runner.mjs +3 -3
- package/driver/stages.mjs +12 -8
- package/driver/suite-census.json +162 -72
- package/driver/systemd/README.md +7 -4
- package/driver/terminal-clamp.mjs +107 -1
- package/driver/tokens.mjs +169 -3
- package/driver/unit-environment.mjs +42 -15
- package/driver/unit-inventory.mjs +19 -2
- package/driver/verify.mjs +50 -5
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/http-server.mjs +4 -0
- package/mcp-server/lib/audit.mjs +11 -1
- package/mcp-server/lib/http-handler.mjs +6 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +16 -2
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-5UyqAyNM.js → index-6jzO9HiX.js} +155 -79
- package/portal-ui/dist/index.html +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/clarivate/src/capabilities.js +5 -5
- package/providers/clarivate/src/core.js +1 -1
- package/providers/corsearch/src/core.js +2 -2
- package/providers/jx/README.md +2 -1
- package/providers/jx/src/turn-envelope.mjs +8 -3
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/perplexity/src/core.js +3 -3
- package/providers/signa/src/capabilities.js +5 -6
- package/providers/signa/src/core.js +1 -1
- package/providers/uspto-local/README.md +1 -1
- package/scripts/authority-boundary-probe.mjs +4 -2
- package/scripts/env-audit.mjs +12 -6
- package/scripts/freeze-example-run.mjs +49 -16
- package/scripts/generated-files-are-current.mjs +69 -4
- package/scripts/release-duplicate-notes.mjs +246 -0
- package/scripts/release-publish-guard.mjs +64 -6
- package/scripts/report-print-check.mjs +194 -0
- package/scripts/settings-render-check.mjs +75 -2
- package/scripts/test-full.mjs +96 -3
- package/scripts/test-run.mjs +10 -0
- package/shared/deployment-box.mjs +7 -2
- package/shared/driver-dir.mjs +1 -1
- package/shared/names-in-force.mjs +1 -1
package/driver/pipeline.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { readFileSync, existsSync, mkdirSync, writeFileSync, renameSync, copyFil
|
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
11
|
import { join, dirname, basename, resolve } from "node:path"; // resolve: the resume line must work from any cwd
|
|
12
12
|
import { driverDir, driverRel, ensureDriverDir } from "../shared/driver-dir.mjs"; // — one definition of where `_driver/` is
|
|
13
|
-
import { terminalClampDecision, orderClausesForLede, clientConditions } from "./terminal-clamp.mjs"; // — deliver and clamp, never withhold
|
|
13
|
+
import { terminalClampDecision, orderClausesForLede, clientConditions, clauseForDefect } from "./terminal-clamp.mjs"; // — deliver and clamp, never withhold
|
|
14
14
|
import { recordSpan } from "./attributed-span.mjs"; // — driver work the decomposition can attribute
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { runStage, correctionHint, gridLedgerNameFor, draftCarryEligible, toolWrittenArtifact, selectEngine } from "./gateway.mjs";
|
|
@@ -31,7 +31,7 @@ import { buildRunContext, deriveSlug, kebab } from "./phase0.mjs";
|
|
|
31
31
|
import { paths, STAGES, axisTier, decideAxes, assertTierSanity, assertEffectiveTier, lines, AGENT_WHATSAPP, whatsappRouting,
|
|
32
32
|
chainEntries, stageOrdinal, stageInputs, stageOutputs, dependencyOrder, REGISTER_AXES, REGISTER_ENUMERATE_TOOL,
|
|
33
33
|
buildEscalationFollowup, buildEnvelopeCloseFollowup, buildFrameReopenFollowup,
|
|
34
|
-
buildFrameReopenRetryMessage, thinkingFor, composeFollowup, stampDispatchBlocks, recordEmptyReturn, nothingFound, nothingToRead, PROVIDER_META, proseRungDirective, inquiryRungDirective } from "./stages.mjs"; import { bandSizeForStage } from "./band-size.mjs";
|
|
34
|
+
buildFrameReopenRetryMessage, thinkingFor, composeFollowup, stampDispatchBlocks, recordEmptyReturn, nothingFound, nothingToRead, PROVIDER_META, proseRungDirective, inquiryRungDirective } from "./stages.mjs"; import { bandSizeForStage, derivedLimitSec, limitExceedsCeiling, ceilingRefusal } from "./band-size.mjs";
|
|
35
35
|
import { IDENTITY_FILE as REPORT_IDENTITY_FILE } from "./report-overview-record.mjs";
|
|
36
36
|
import { dispatchRows, clearedSignatures } from "./seat-attempts.mjs";
|
|
37
37
|
import { CONTEXT_DERIVATIONS, DISPATCH_EXTRAS, INLINE_CONTEXT, sandboxManifest, sandboxGaps, derivationsFor } from "./stage-context.mjs"; // — what a stage is actually handed
|
|
@@ -45,7 +45,8 @@ import { readRegisterTaint, readActiveTaintAxes } from "./register-taint.mjs";
|
|
|
45
45
|
import { parseNamedBand, mergeNamedBands, findCollapsedBands, quarantineUnknownStates, taintQuarantineCleanBlocks, bandRecords } from "./named-band.mjs";
|
|
46
46
|
import { recordOriginsFor } from "./record-origins.mjs";
|
|
47
47
|
import { REGISTER_PROVIDER } from "./driver.config.mjs";
|
|
48
|
-
import { FACTS_FILE as DIGEST_FACTS_FILE, ACCOUNTING_STAMP as DIGEST_ACCOUNTING_STAMP, recordedFindingUris
|
|
48
|
+
import { FACTS_FILE as DIGEST_FACTS_FILE, ACCOUNTING_STAMP as DIGEST_ACCOUNTING_STAMP, recordedFindingUris,
|
|
49
|
+
digestAccountingGap, digestBatchBrief, batchesOf } from "./register-digest-record.mjs"; // conversion 11 — the render's facts sidecar and the accounting era stamp
|
|
49
50
|
import { buildBandShape, dominantElementComposites, deriveRegisterPositions, floorTierByMark, floorMarkKey } from "./band-shape.mjs"; // PR-8 — the deterministic reading layer; P2-A — candidates + positions
|
|
50
51
|
import { deriveOwnerScreen, ownerScreenNegative } from "./owner-screen.mjs"; // P2-B — the owner×element screen's own receipt
|
|
51
52
|
import { reconcileRecall, parseFindingsEndings, parseCrowdRulings, readOkRecordUris,
|
|
@@ -96,8 +97,10 @@ import { publishReport, composeEmailHtml, deliverySubject } from "./publish/inde
|
|
|
96
97
|
import { parseCaseLawProfiles, joinCaseLawProfiles } from "./publish/parse.mjs";
|
|
97
98
|
import { buildAuditMd, parseSpineFindingBlocks } from "./publish/audit-from-spine.mjs";
|
|
98
99
|
import { deriveRegisterPresence } from "./publish/register-presence.mjs"; // — the audit stores every live in-scope record
|
|
99
|
-
import { lastAcceptedMatterFrame, frameIdentifiedClasses } from "./matter-frame-record.mjs"; // — the frame's inferred scope, when nothing was instructed; and the classes it judged necessary beyond the instructed ones, which the plan compile unions in
|
|
100
|
-
import { romanizedTermsFromPlan, mintSupplementalQid } from "./register-plan.mjs";
|
|
100
|
+
import { lastAcceptedMatterFrame, frameIdentifiedClasses, frameHouseElementCandidate } from "./matter-frame-record.mjs"; // — the frame's inferred scope, when nothing was instructed; and the classes it judged necessary beyond the instructed ones, which the plan compile unions in
|
|
101
|
+
import { romanizedTermsFromPlan, mintSupplementalQid } from "./register-plan.mjs";
|
|
102
|
+
import { excludeHouseElement, verifyHouseElementOwnership, resolveRegions as resolvePlanRegions, HOUSE_ELEMENT_RECEIPT } from "./register-plan.mjs"; // 647 — the client's own element leaves the conflict analysis only on a verified receipt
|
|
103
|
+
import { resolveRecordExecutor } from "./register-records.mjs"; // — the stamp the late lanes never met
|
|
101
104
|
import { slimLine, crowdLine } from "./hit-list.mjs"; // — the list the run works from; crowds ride it as a sibling array
|
|
102
105
|
import { mintCrossCheckDoubts, mintContradictionDoubts, stitchDoubts, applyClosure } from "./doubt-ledger.mjs"; // doubt-stitch + doubt-closure (2026-07-22)
|
|
103
106
|
// Conversion 6: the two line-form parsers are no longer on the live path — the seat sends typed
|
|
@@ -201,6 +204,39 @@ async function defaultRecordFetcher(uri, ctx) {
|
|
|
201
204
|
return activeProvider().recordFetch(uri, ctx);
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
/**
|
|
208
|
+
* THE ENGINE RE-ISSUES A DICTATED MEANING SEARCH ITSELF, without asking the assessing model to redo the
|
|
209
|
+
* sweep — the same shape as `defaultRecordFetcher` above, which fetches a register record in code when
|
|
210
|
+
* one was dropped unexamined rather than sending the model back for it.
|
|
211
|
+
*
|
|
212
|
+
* "Through the same tool" is literal and costs no new machinery: the research provider's grid functions
|
|
213
|
+
* are a plain module with no listener, so the component the model calls and this one call the SAME
|
|
214
|
+
* functions on the same provider. There is no second implementation to drift.
|
|
215
|
+
*
|
|
216
|
+
* NARROW BY CONSTRUCTION. The spec handed over carries only the queries that are missing, so this buys
|
|
217
|
+
* those cells rather than re-running a sweep that already cost real money and real minutes.
|
|
218
|
+
*
|
|
219
|
+
* It DECIDES nothing. The caller re-merges and the gate judges, so a re-issue that comes back empty is
|
|
220
|
+
* simply a re-merge that still fails, and is disclosed as such rather than counted as searched.
|
|
221
|
+
*/
|
|
222
|
+
async function defaultConnotationReissuer(queries, { spec, modelOverride = null } = {}) {
|
|
223
|
+
const apiKey = process.env.PERPLEXITY_API_KEY || "";
|
|
224
|
+
if (!apiKey) return { ok: false, cause: "no research credential in the driver environment", rows: [] };
|
|
225
|
+
const core = await import("../providers/perplexity/src/core.js");
|
|
226
|
+
const narrow = { ...spec, connotation: { ...(spec.connotation ?? {}), queries: queries.slice() } };
|
|
227
|
+
try {
|
|
228
|
+
const task = core.buildGridProgramTask(narrow);
|
|
229
|
+
const data = await core.callAgentAPI(apiKey,
|
|
230
|
+
core.buildRequestBody({ task, preset: "pro-search", modelOverride, enableSandbox: true }));
|
|
231
|
+
const cap = core.captureGridFromResponse(data, narrow);
|
|
232
|
+
if (!cap?.ok) return { ok: false, cause: String(cap?.error ?? "capture failed").slice(0, 160), rows: [] };
|
|
233
|
+
const rows = parsePrRiskResults(cap.ledgerJson).filter((r) => queries.includes(r.query));
|
|
234
|
+
return { ok: true, rows, requested: queries.length };
|
|
235
|
+
} catch (e) {
|
|
236
|
+
return { ok: false, cause: String(e?.message ?? e).slice(0, 160), rows: [] };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
204
240
|
// The ACTIVE provider's capability contract and the offices this DEPLOYMENT cannot reach both moved to
|
|
205
241
|
// register-unreachable.mjs, unchanged, because Depth 2's count and record lanes need the same
|
|
206
242
|
// two answers and a second binding of the member→variable lookup is how two lanes come to disagree
|
|
@@ -2225,6 +2261,69 @@ function logFullyDeferredAxes(plan, P, source) {
|
|
|
2225
2261
|
// `frozenOnly` (item): read the FROZEN plan or nothing. reconstructCtx is an introspection path —
|
|
2226
2262
|
// it must never compile and freeze a plan into an existing run as a side effect of being asked what the
|
|
2227
2263
|
// run already holds. (attachProfile / attachFramework take the same posture via `write:false`.)
|
|
2264
|
+
/**
|
|
2265
|
+
* Ask the register whether the client owns the element the frame proposed, and record the answer.
|
|
2266
|
+
*
|
|
2267
|
+
* ASYNC AND SEPARATE FROM THE COMPILE, which is why it is its own step rather than a branch inside
|
|
2268
|
+
* `attachRegisterPlan`: that function is synchronous and is called on the resume path too. The shape is
|
|
2269
|
+
* the digest's — an async driver step writes a receipt, a synchronous consumer reads it — and it buys
|
|
2270
|
+
* the property that matters here: the compile cannot accidentally exclude anything by reaching for a
|
|
2271
|
+
* promise it does not await.
|
|
2272
|
+
*
|
|
2273
|
+
* NOT A REGISTERED CONTEXT DERIVATION, and the reason is worth stating because the obvious reading is
|
|
2274
|
+
* that it should be. `stage-context.mjs` declares the derivations a sandbox can REPLAY, and its runners
|
|
2275
|
+
* are synchronous. This one asks a live register a question; there is no offline re-derivation of that,
|
|
2276
|
+
* and declaring it would promise the `--experiment` rig something no runner can deliver — which the
|
|
2277
|
+
* guard catches by name, loudly, as it should. A sandbox instead inherits the receipt with the rest of
|
|
2278
|
+
* `_driver/`, and a sandbox that has none excludes nothing, which is the same answer every other
|
|
2279
|
+
* unverified run gets.
|
|
2280
|
+
*
|
|
2281
|
+
* NEVER-KILL. Every failure writes an unverified receipt and the run plans exactly as it does today.
|
|
2282
|
+
* The element being searched in full is the safe direction and the status quo; the only thing that can
|
|
2283
|
+
* narrow a client's search is a register answer naming their own live registration.
|
|
2284
|
+
*/
|
|
2285
|
+
async function verifyAndRecordHouseElement(ctx, opts = {}) {
|
|
2286
|
+
const P = ctx.paths;
|
|
2287
|
+
try {
|
|
2288
|
+
const proposed = frameHouseElementCandidate(P.runDir);
|
|
2289
|
+
if (!proposed) return; // the ordinary case: the frame proposed nothing and there is no question
|
|
2290
|
+
|
|
2291
|
+
const classes = [...new Set([...inScopeClassList(ctx.job, ctx.profile).map(String), ...frameIdentifiedClasses(P.runDir)])];
|
|
2292
|
+
// THE CLIENT'S OWN NAMES. The profile's trading names are the curated list; the matter's customer is
|
|
2293
|
+
// the free-text one. Both, because a profile may be absent on a one-off matter and a customer string
|
|
2294
|
+
// may be a person where the filings are held by a company.
|
|
2295
|
+
const owners = [...new Set([...(ctx.profile?.selfExclusionOwners ?? []), ctx.job?.customer].map((o) => String(o ?? "").trim()).filter(Boolean))];
|
|
2296
|
+
const caps = registerCapabilities();
|
|
2297
|
+
const { regions } = resolvePlanRegions(registerJurisdictions(ctx.job, ctx.profile), caps);
|
|
2298
|
+
const rec = resolveRecordExecutor({
|
|
2299
|
+
lister: opts?.recordLister ?? null, adapter: activeProvider(),
|
|
2300
|
+
agentId: ctx.agentId ?? null, sessionKey: `prelim-${ctx.run.slug}-${ctx.run.codename}`,
|
|
2301
|
+
recordLog: runRecordLogPath(P.runDir),
|
|
2302
|
+
fixtureDir: ctx.job?.registerFixtures?.records ?? null,
|
|
2303
|
+
});
|
|
2304
|
+
// AN EXACT-NAME LISTING OF THE ELEMENT, filtered to the client's own live in-class registrations by
|
|
2305
|
+
// the verifier. The owner join is done on REAL RECORDS the register returned, which is what
|
|
2306
|
+
// "verified by owner, not asserted" means — there is no owner-predicate listing on this interface
|
|
2307
|
+
// and inventing one would be a second way to ask the same question.
|
|
2308
|
+
const lookup = typeof rec.list !== "function" ? null : async ({ element, classes: cls }) => {
|
|
2309
|
+
const r = await rec.list(element, { classes: cls, regions, limit: 50 });
|
|
2310
|
+
if (!r || r.ok === false) return { ok: false, reason: String(r?.reason ?? "the register listing did not answer") };
|
|
2311
|
+
return { ok: true, records: Array.isArray(r.records) ? r.records : [] };
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2314
|
+
const receipt = await verifyHouseElementOwnership({ element: proposed.element, classes, owners, lookup });
|
|
2315
|
+
receipt.remainder = proposed.remainder; // carried so the compile needs only this one file
|
|
2316
|
+
ensureDriverDir(P.runDir);
|
|
2317
|
+
atomicWrite(driverDir(P.runDir, HOUSE_ELEMENT_RECEIPT), JSON.stringify(receipt, null, 2) + "\n");
|
|
2318
|
+
runLog(P.runDir, { event: "house-element-ownership", verified: receipt.verified, reason: receipt.reason,
|
|
2319
|
+
records: receipt.records.length, owners: owners.length, executor: rec.source });
|
|
2320
|
+
if (!receipt.verified) note(`house element: ${proposed.element} stays in the search — ${receipt.reason}`);
|
|
2321
|
+
} catch (e) {
|
|
2322
|
+
// A THROW HERE MUST NOT COST A REPORT. No receipt means no exclusion, which is today's plan.
|
|
2323
|
+
runLog(P.runDir, { event: "house-element-ownership-failed", reason: String(e?.message ?? e).slice(0, 160) });
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2228
2327
|
function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
2229
2328
|
const P = ctx.paths;
|
|
2230
2329
|
if (frozenOnly) {
|
|
@@ -2248,7 +2347,30 @@ function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
|
2248
2347
|
ctx.registerPlan = null;
|
|
2249
2348
|
let compiled;
|
|
2250
2349
|
try {
|
|
2251
|
-
|
|
2350
|
+
let manifest = parseVariantManifestModel(readFileSync(P.variantManifestModel, "utf8"));
|
|
2351
|
+
// ── 647 — THE CLIENT'S OWN ELEMENT LEAVES THE CONFLICT ANALYSIS, ON EVIDENCE OR NOT AT ALL ─────
|
|
2352
|
+
//
|
|
2353
|
+
// The receipt is the ONLY thing that arms this. An absent, unreadable or unverified receipt plans
|
|
2354
|
+
// exactly as this compile did before the rule existed, which is the element searched in full — the
|
|
2355
|
+
// safe direction, and the one a client is never harmed by.
|
|
2356
|
+
let houseConfirmation = null;
|
|
2357
|
+
let houseReceipt = null;
|
|
2358
|
+
try { houseReceipt = JSON.parse(readFileSync(driverDir(P.runDir, HOUSE_ELEMENT_RECEIPT), "utf8")); }
|
|
2359
|
+
catch { houseReceipt = null; }
|
|
2360
|
+
if (houseReceipt?.verified === true) {
|
|
2361
|
+
const r = excludeHouseElement(manifest, { element: houseReceipt.element, remainder: houseReceipt.remainder });
|
|
2362
|
+
if (r.refused) {
|
|
2363
|
+
// VERIFIED OWNERSHIP AND STILL NOT APPLIED. The receipt answers who owns the element; the
|
|
2364
|
+
// transform answers whether this manifest's mark can survive the cut. Both must hold.
|
|
2365
|
+
runLog(P.runDir, { event: "house-element-not-applied", reason: r.refused, element: houseReceipt.element });
|
|
2366
|
+
} else {
|
|
2367
|
+
manifest = r.manifest;
|
|
2368
|
+
houseConfirmation = r.confirmation;
|
|
2369
|
+
runLog(P.runDir, { event: "house-element-excluded", element: houseReceipt.element,
|
|
2370
|
+
remainder: houseReceipt.remainder, dominant_element: manifest.dominant_element,
|
|
2371
|
+
evidence: (houseReceipt.records ?? []).length });
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2252
2374
|
let form = null;
|
|
2253
2375
|
try { form = JSON.parse(readFileSync(P.formNeighbourhood, "utf8")); } catch { /* form band optional */ }
|
|
2254
2376
|
compiled = compileRegisterPlan({
|
|
@@ -2275,6 +2397,19 @@ function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
|
2275
2397
|
// rides the plan as a disclosed deferral, never as a silently narrower search.
|
|
2276
2398
|
unavailableOffices: registerUnavailableOffices(),
|
|
2277
2399
|
});
|
|
2400
|
+
// REQUIREMENT 3 — the element is still checked ONCE, as a confirmation of the client's own live
|
|
2401
|
+
// registrations rather than as a conflict sweep, so the exclusion is evidenced on the report by a
|
|
2402
|
+
// query that ran. Appended after the compile because it is not derived from the manifest: it is the
|
|
2403
|
+
// one question about the element the plan still owes.
|
|
2404
|
+
if (houseConfirmation && compiled?.entries) {
|
|
2405
|
+
const used = new Set(compiled.entries.map((e) => e.qid));
|
|
2406
|
+
compiled.entries.push({
|
|
2407
|
+
...houseConfirmation,
|
|
2408
|
+
qid: mintSupplementalQid({ prefix: "house", term: houseConfirmation.term, used }),
|
|
2409
|
+
nice_classes: compiled.nice_classes ?? [],
|
|
2410
|
+
regions: compiled.regions ?? [],
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2278
2413
|
} catch (e) {
|
|
2279
2414
|
// NEVER-KILL at mint time: no/malformed sibling or a class-less matter degrades to the legacy
|
|
2280
2415
|
// path with a logged reason — the plan improves the run, it never turns delivery off.
|
|
@@ -3615,6 +3750,27 @@ export function digestDispatchExtra(ctx, { trigger = "fresh", willRun = true, ex
|
|
|
3615
3750
|
}
|
|
3616
3751
|
}
|
|
3617
3752
|
} catch (e) { note(`coverage-form brief skipped (non-fatal — the form is in _driver/ and the validator refuses an unsettled row): ${String(e.message).slice(0, 80)}`); }
|
|
3753
|
+
// — THE BATCH BLOCK: how the driver split this run's band, and which batches are still outstanding.
|
|
3754
|
+
//
|
|
3755
|
+
// The seat is told the split HERE rather than in the stage's dictation because the numbers are the
|
|
3756
|
+
// run's, not the contract's: a band of 40 records is one batch and a band of 1,161 is twelve, and on a
|
|
3757
|
+
// resume the outstanding set is what the killed attempt did not reach. The dictation says the rule;
|
|
3758
|
+
// this says the arithmetic.
|
|
3759
|
+
//
|
|
3760
|
+
// BEST-EFFORT, AND SAFE BY CONSTRUCTION rather than by argument — the coverage brief's precedent above,
|
|
3761
|
+
// for its reason. Nothing here enforces anything: a call naming a batch is judged against the driver's
|
|
3762
|
+
// own split whether or not this block composed, the tool's refusal re-states what is outstanding, and
|
|
3763
|
+
// the stage's exit gate refuses a document that ends fewer records than the run carried in. A brief
|
|
3764
|
+
// that fails to compose costs a corrective round; it cannot cost a record.
|
|
3765
|
+
try {
|
|
3766
|
+
const gap = digestAccountingGap(P.runDir);
|
|
3767
|
+
const block = digestBatchBrief(gap);
|
|
3768
|
+
if (block) {
|
|
3769
|
+
out = out ? `${out}\n\n${block}` : block;
|
|
3770
|
+
runLog(P.runDir, { event: "digest-batch-brief", trigger, owed: gap.owed.length,
|
|
3771
|
+
batches: batchesOf(gap.owed).length, unaccounted: gap.unaccounted.length });
|
|
3772
|
+
}
|
|
3773
|
+
} catch (e) { note(`digest batch brief skipped (non-fatal — the tool judges every batch against the driver's own split regardless): ${String(e.message).slice(0, 80)}`); }
|
|
3618
3774
|
// AD-2 A9 (E2E-R2) + the P5 review (2026-07-31): a corrective/repair pass is told NOT to re-read the
|
|
3619
3775
|
// whole placement file — the per-candidate tiers are in placements.json — but the RULINGS TAIL (band
|
|
3620
3776
|
// reconciliation, disagreements, coverage rulings, open questions) lives ONLY in the md. Leaving it to
|
|
@@ -4309,6 +4465,25 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4309
4465
|
// Evaluated on `effThinking`, not `thinking`: the anthropic gate above nulls the tier for a
|
|
4310
4466
|
// cross-provider model, and a guard that reads the nulled value would go blind on the same overrides.
|
|
4311
4467
|
assertEffectiveTier(label, { model, thinking: effThinking });
|
|
4468
|
+
// ── THE TIME LIMIT IS DERIVED FROM THE BAND THIS DISPATCH IS HANDED ─────────────────────────────
|
|
4469
|
+
//
|
|
4470
|
+
// `def.timeoutSec` was a constant chosen once against a band nobody recorded beside it, and on a
|
|
4471
|
+
// dense matter the two band-reading stages died AT their walls having written nothing. The base is
|
|
4472
|
+
// still the stage's own number; what the band decides is how far above it this dispatch may go. A
|
|
4473
|
+
// band at or under the reference returns the base untouched, so nothing already verified moves.
|
|
4474
|
+
const dispatchBand = bandSizeForStage(name, P);
|
|
4475
|
+
const derivedLimit = derivedLimitSec(def.timeoutSec, dispatchBand);
|
|
4476
|
+
// REFUSED BEFORE A SEAT IS DISPATCHED, not after the wall. A limit past the ceiling is a prediction
|
|
4477
|
+
// that this stage will be killed; starting it spends the whole prediction to arrive where the refusal
|
|
4478
|
+
// already is, and the refusal NAMES the band size, which is the finding a killed attempt never gives.
|
|
4479
|
+
if (limitExceedsCeiling(derivedLimit.sec)) {
|
|
4480
|
+
const reason = ceilingRefusal(name, derivedLimit);
|
|
4481
|
+
runLog(P.runDir, { event: "stage-input-over-ceiling", stage: name,
|
|
4482
|
+
inputBytes: derivedLimit.inputBytes, derivedLimitSec: derivedLimit.sec });
|
|
4483
|
+
throw new StageFailure(name, reason, undefined, { failClass: "deterministic" });
|
|
4484
|
+
}
|
|
4485
|
+
runLog(P.runDir, { event: "stage-limit-derived", stage: name, base: def.timeoutSec,
|
|
4486
|
+
inputBytes: derivedLimit.inputBytes, derivedLimitSec: derivedLimit.sec, basis: derivedLimit.basis });
|
|
4312
4487
|
const r = await runStage(label, {
|
|
4313
4488
|
agent: execAgent,
|
|
4314
4489
|
message,
|
|
@@ -4317,11 +4492,12 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4317
4492
|
// opts.sessionKey lets a followup RESUME the exact key a prior run won on (winning-key hardening); else
|
|
4318
4493
|
// the canonical base key. (A followup with a stale base key would resume a failed attempt — see runStage.)
|
|
4319
4494
|
sessionKey: opts.sessionKey ?? `prelim-${ctx.run.slug}-${ctx.run.codename}-${name}${keyAxis}`,
|
|
4320
|
-
timeoutSec: def.timeoutSec,
|
|
4495
|
+
timeoutSec: derivedLimit.sec ?? def.timeoutSec,
|
|
4496
|
+
derivedLimit, // recorded on every attempt row: the size it was derived from, and the number
|
|
4321
4497
|
stallSec: def.stallSec, // per-stage stall override (heavy stages); undefined → global CLEAROTRON_STALL_MS
|
|
4322
4498
|
expectFile: out,
|
|
4323
4499
|
validate: def.validate,
|
|
4324
|
-
runDir: P.runDir, bandSize:
|
|
4500
|
+
runDir: P.runDir, bandSize: dispatchBand,
|
|
4325
4501
|
// ── — THE PRESENTING SIDE, RECOMPOSED PER ATTEMPT ────────────────────────────────────────
|
|
4326
4502
|
//
|
|
4327
4503
|
// A stage that declares `refreshCtx` is saying its dispatch text depends on state the run CHANGES
|
|
@@ -7296,7 +7472,7 @@ export function fullProseOrdinals(findings) {
|
|
|
7296
7472
|
.filter((o) => o != null);
|
|
7297
7473
|
}
|
|
7298
7474
|
// ASSEMBLE report.md = the overview shell (front-matter + Actions/Coverage/Methodology) + `# Marks` + the
|
|
7299
|
-
// per-card files in render order (composite desc, ordinal asc — mirrors render.mjs
|
|
7475
|
+
// per-card files in render order (composite desc, ordinal asc — mirrors render.mjs `sortedAll`), `open: true` on the
|
|
7300
7476
|
// single top card. Findings with no card file (a failed report-card, or a secondary finding) are NOT emitted —
|
|
7301
7477
|
// render.mjs synthesizes them structured-only from findings.json, so nothing is silently dropped. Pure file IO.
|
|
7302
7478
|
// spec 64 — "### Only you can close these" is CODE-BUILT from the typed actions register (the report-
|
|
@@ -8816,6 +8992,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
8816
8992
|
must(await stage("prelim-variants", ctx), "prelim-variants");
|
|
8817
8993
|
deriveScopeLedgerJson(ctx); // frame-omission design: code-derive scope-ledger.json from the validated prose (never-kill)
|
|
8818
8994
|
deriveFormNeighbourhood(ctx); // mechanical FORM band: code-derive form-neighbourhood.json from the manifest's distinctive element(s) — the model-free form floor the register funnel searches (never-kill)
|
|
8995
|
+
await verifyAndRecordHouseElement(ctx, opts); // ask the register who owns the frame's proposed house element, BEFORE the plan is compiled from it
|
|
8819
8996
|
attachRegisterPlan(ctx); // WS2 (B3): compile/freeze/reuse the deterministic register plan (flag-gated; frozen plan wins on resume; never-kill on mint)
|
|
8820
8997
|
|
|
8821
8998
|
// spec 64 (B2) — proactive recall probes: prior-confirmed conflicts for THIS mark (the workspace
|
|
@@ -9170,6 +9347,64 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9170
9347
|
runLog(run.runDir, { event: "common-law-half-quarantined", half: h, fail: String(clHalves[h].fail).slice(0, 140) });
|
|
9171
9348
|
}
|
|
9172
9349
|
}
|
|
9350
|
+
// The engine's own re-issue, spent at most once per run — the bound spans BOTH the half-stage
|
|
9351
|
+
// hook below and the merge gate's disclosure, so a run never buys the same search twice.
|
|
9352
|
+
let clConnotationReissueSpent = false;
|
|
9353
|
+
// ── THE MEANING HALF FELL SHORT BY A SEARCH OR TWO: RE-ISSUE, THEN DELIVER ───────────────────
|
|
9354
|
+
//
|
|
9355
|
+
// This is where the clearance actually died. The half's own validator refuses when a dictated
|
|
9356
|
+
// meaning search is missing from its receipts, and every attempt refused identically — eight of
|
|
9357
|
+
// them — so the run was thrown away with fifty-nine of sixty searches done and recorded.
|
|
9358
|
+
//
|
|
9359
|
+
// Quarantine above cannot help here: that is for a half that failed wholesale on transient
|
|
9360
|
+
// infrastructure, where re-running the half converges. This half SUCCEEDED at almost everything.
|
|
9361
|
+
// So the engine re-issues the missing searches itself, through the same provider, and re-judges the
|
|
9362
|
+
// half on its own artifact. If they landed, the half is simply valid and nothing is disclosed.
|
|
9363
|
+
//
|
|
9364
|
+
// If they did not, the half is accepted WITH THE SHORTFALL RECORDED — an honest gap per unfinished
|
|
9365
|
+
// search, carrying its term, which the merge gate then discloses and the coverage row reads. That is
|
|
9366
|
+
// the owner's ruling of 2026-09-17 and it is not the false-clean the gates guard: false-clean is
|
|
9367
|
+
// silence, and this says on the client's own page which search did not complete.
|
|
9368
|
+
if (clSplit && !clHalves[MEANING_SEAT].ok && /connotation_query_unrecorded/.test(String(clHalves[MEANING_SEAT].fail ?? ""))) {
|
|
9369
|
+
const h = MEANING_SEAT;
|
|
9370
|
+
let short = [];
|
|
9371
|
+
try {
|
|
9372
|
+
const spec = JSON.parse(readFileSync(P.gridSpecHalf(h), "utf8"));
|
|
9373
|
+
const halfLedger = JSON.parse(readFileSync(P.commonLawGridHalf(h), "utf8"));
|
|
9374
|
+
short = findDroppedConnotationQueries(spec, halfLedger);
|
|
9375
|
+
if (short.length) {
|
|
9376
|
+
runLog(run.runDir, { event: "connotation-reissue", half: h, count: short.length });
|
|
9377
|
+
note(`the meaning half recorded all but ${short.length} of its dictated searches — the engine re-issues `
|
|
9378
|
+
+ `${short.length === 1 ? "it" : "them"} itself rather than asking for the sweep again`);
|
|
9379
|
+
clConnotationReissueSpent = true;
|
|
9380
|
+
const reissuer = opts.connotationReissuer ?? defaultConnotationReissuer;
|
|
9381
|
+
const r = await reissuer(short, { spec });
|
|
9382
|
+
runLog(run.runDir, { event: "connotation-reissue-result", ok: !!r?.ok,
|
|
9383
|
+
recovered: Array.isArray(r?.rows) ? r.rows.length : 0,
|
|
9384
|
+
cause: r?.ok ? null : String(r?.cause ?? "").slice(0, 160) });
|
|
9385
|
+
const rows = (r?.ok && Array.isArray(r.rows)) ? r.rows : [];
|
|
9386
|
+
halfLedger.extras = halfLedger.extras ?? {};
|
|
9387
|
+
halfLedger.extras.pr_risk = [...(Array.isArray(halfLedger.extras.pr_risk) ? halfLedger.extras.pr_risk : []), ...rows];
|
|
9388
|
+
const stillShort = findDroppedConnotationQueries(spec, halfLedger);
|
|
9389
|
+
if (stillShort.length) {
|
|
9390
|
+
halfLedger.gaps = Array.isArray(halfLedger.gaps) ? halfLedger.gaps : [];
|
|
9391
|
+
for (const q of stillShort)
|
|
9392
|
+
halfLedger.gaps.push({ term: q, platform: "connotation",
|
|
9393
|
+
error: "the search did not complete after a re-ask and a re-issue by the engine" });
|
|
9394
|
+
runLog(run.runDir, { event: "connotation-unfinished-disclosed", count: stillShort.length, half: h });
|
|
9395
|
+
note(`${stillShort.length} dictated meaning quer${stillShort.length === 1 ? "y" : "ies"} did not complete — `
|
|
9396
|
+
+ "recorded as honest gaps and DISCLOSED on the coverage row; the run delivers the rest");
|
|
9397
|
+
}
|
|
9398
|
+
atomicWrite(P.commonLawGridHalf(h), JSON.stringify(halfLedger, null, 2) + "\n");
|
|
9399
|
+
// The half is accepted on its re-judged artifact. Same shape as quarantine above: the stage is
|
|
9400
|
+
// not re-run and the model is not asked again.
|
|
9401
|
+
clHalves[h].ok = true;
|
|
9402
|
+
gatherResults[clHalves[h].i] = { ...gatherResults[clHalves[h].i], ok: true, fail: null, clHalfShortfall: stillShort.length };
|
|
9403
|
+
}
|
|
9404
|
+
} catch (err) {
|
|
9405
|
+
runLog(run.runDir, { event: "connotation-reissue-failed", error: String(err?.message ?? err).slice(0, 160) });
|
|
9406
|
+
}
|
|
9407
|
+
}
|
|
9173
9408
|
gather.forEach((g, i) => must(gatherResults[i], g.name + (g.axis ? `:${g.axis}` : "")));
|
|
9174
9409
|
// winning session key per axis → a later escalation resumes the EXACT key the unit succeeded on.
|
|
9175
9410
|
// (register units only — a common-law half's axis is its half id, tracked in clHalves, never here.)
|
|
@@ -9184,7 +9419,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9184
9419
|
// synthesis, replay) reads exactly the artifacts a single-member run would have written. Re-run after
|
|
9185
9420
|
// every routed half followup (closure / frame-reopen) — the half files stay the source of truth and
|
|
9186
9421
|
// the canonical pair is always derived, which also makes the merge resume-idempotent.
|
|
9187
|
-
const mergeCommonLawArtifacts = !clSplit ? null : () => {
|
|
9422
|
+
const mergeCommonLawArtifacts = !clSplit ? null : ({ discloseUnfinished = false } = {}) => {
|
|
9188
9423
|
// Each half's coverage truth = its plugin-written main ledger FOLDED with any plugin-written
|
|
9189
9424
|
// supplementary sibling(s) the closure lane produced. Returns the batches as an ARRAY (mergeGrids'
|
|
9190
9425
|
// batchesOf flattens it, cells union across all of them); a missing/corrupt main leaves its cells to
|
|
@@ -9355,6 +9590,40 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9355
9590
|
// behaviour was zero retries after full spend, with a re-run that failed identically; the new
|
|
9356
9591
|
// behaviour is one retry, then the same honest death.
|
|
9357
9592
|
const dropped = findDroppedConnotationQueries(fullSpec, mergedGrid);
|
|
9593
|
+
if (dropped.length && discloseUnfinished) {
|
|
9594
|
+
// ── THE LAST STEP OF THE OWNER'S RULING: a run that completed 59 of 60 dictated searches
|
|
9595
|
+
// DELIVERS, and says on the client's own page that it was partial and for which term.
|
|
9596
|
+
//
|
|
9597
|
+
// This is NOT the false-clean the note below guards against, and the difference is the whole
|
|
9598
|
+
// reason it is allowed here. False-clean is SILENCE — a hole counted as searched, nobody able to
|
|
9599
|
+
// see what was not done. This records each unfinished query as an honest gap carrying its own
|
|
9600
|
+
// term, which is the vocabulary the note above says dictated queries did not have, and that gap
|
|
9601
|
+
// is what the coverage row reads. A reader is told the investigation was partial and which term
|
|
9602
|
+
// did not complete.
|
|
9603
|
+
//
|
|
9604
|
+
// It is reached ONLY after the bounded attempts are spent: the model's single routed remedy and
|
|
9605
|
+
// the engine's own re-issue of the search. On the first pass this branch does not exist — the
|
|
9606
|
+
// gate throws exactly as before and the park loop gets its retries. Disclosure is the last
|
|
9607
|
+
// resort, never the first answer.
|
|
9608
|
+
const errored = findErroredConnotationQueries(fullSpec, mergedGrid);
|
|
9609
|
+
const explained = new Set(errored.map((e) => e.query));
|
|
9610
|
+
const gaps = Array.isArray(mergedGrid.gaps) ? mergedGrid.gaps : (mergedGrid.gaps = []);
|
|
9611
|
+
for (const q of dropped) {
|
|
9612
|
+
if (explained.has(q)) continue; // the provider already said why; its row stands
|
|
9613
|
+
gaps.push({ term: q, platform: "connotation",
|
|
9614
|
+
error: "the search did not complete after a re-ask and a re-issue by the engine" });
|
|
9615
|
+
}
|
|
9616
|
+
// THE CANONICAL LEDGER IS WRITTEN ABOVE, BEFORE THIS GATE RUNS, so mutating the merged object
|
|
9617
|
+
// here reaches nothing: the report reads the file. Re-write it with the gaps in place, or the
|
|
9618
|
+
// disclosure exists only in memory and the coverage row has nothing to read — which is the
|
|
9619
|
+
// silent half of the very defect this branch exists to prevent.
|
|
9620
|
+
atomicWrite(P.commonLawGrid, JSON.stringify(mergedGrid, null, 2) + "\n");
|
|
9621
|
+
runLog(run.runDir, { event: "connotation-unfinished-disclosed", count: dropped.length,
|
|
9622
|
+
explained: errored.length, terms: dropped.length });
|
|
9623
|
+
note(`${dropped.length} dictated meaning quer${dropped.length === 1 ? "y" : "ies"} did not complete after `
|
|
9624
|
+
+ "the bounded attempts — recorded as honest gaps and DISCLOSED on the coverage row; the run delivers");
|
|
9625
|
+
return { mergedGrid, fullSpec, connotationUnfinished: dropped.slice() };
|
|
9626
|
+
}
|
|
9358
9627
|
if (dropped.length) {
|
|
9359
9628
|
const errored = findErroredConnotationQueries(fullSpec, mergedGrid);
|
|
9360
9629
|
const allExplained = errored.length === dropped.length;
|
|
@@ -9385,6 +9654,54 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9385
9654
|
// retrying a defect buys nothing; this is one dictated repair, not a retry). Quarantined-half shapes
|
|
9386
9655
|
// keep their transient park path untouched (the remedy never runs for them).
|
|
9387
9656
|
let clConnotationRemedySpent = false;
|
|
9657
|
+
/**
|
|
9658
|
+
* THE ENGINE'S OWN ATTEMPT, THEN DELIVERY WITH THE GAP DISCLOSED.
|
|
9659
|
+
*
|
|
9660
|
+
* Reached only after the model has had its single routed repair turn. The engine re-issues the
|
|
9661
|
+
* missing searches itself through the same provider, appends whatever comes back to the half's own
|
|
9662
|
+
* ledger, and re-merges. If the searches are there now, the run is simply clean and nothing is
|
|
9663
|
+
* disclosed. If they are still missing, the run DELIVERS and the coverage row says the investigation
|
|
9664
|
+
* was partial and which term did not complete.
|
|
9665
|
+
*
|
|
9666
|
+
* A clearance that completed 59 of 60 dictated searches used to throw the whole report away. The
|
|
9667
|
+
* failure it is replacing is not a quality gate doing its job: the work was done, one search of
|
|
9668
|
+
* sixty did not land, and the client lost everything including the 59.
|
|
9669
|
+
*/
|
|
9670
|
+
const reissueThenDisclose = async (e) => {
|
|
9671
|
+
if (!clConnotationReissueSpent) {
|
|
9672
|
+
clConnotationReissueSpent = true;
|
|
9673
|
+
let missing = [];
|
|
9674
|
+
try {
|
|
9675
|
+
const spec = JSON.parse(readFileSync(P.gridSpecHalf(MEANING_SEAT), "utf8"));
|
|
9676
|
+
const merged = JSON.parse(readFileSync(P.commonLawGrid, "utf8"));
|
|
9677
|
+
missing = findDroppedConnotationQueries(spec, merged);
|
|
9678
|
+
if (missing.length) {
|
|
9679
|
+
runLog(run.runDir, { event: "connotation-reissue", count: missing.length });
|
|
9680
|
+
note(`${missing.length} dictated meaning quer${missing.length === 1 ? "y" : "ies"} missing after the `
|
|
9681
|
+
+ "model's one repair turn — the engine re-issues them itself through the same tool");
|
|
9682
|
+
const reissuer = opts.connotationReissuer ?? defaultConnotationReissuer;
|
|
9683
|
+
const r = await reissuer(missing, { spec });
|
|
9684
|
+
runLog(run.runDir, { event: "connotation-reissue-result", ok: !!r?.ok,
|
|
9685
|
+
recovered: Array.isArray(r?.rows) ? r.rows.length : 0,
|
|
9686
|
+
cause: r?.ok ? null : String(r?.cause ?? "").slice(0, 160) });
|
|
9687
|
+
if (r?.ok && r.rows?.length) {
|
|
9688
|
+
// Append into the half's OWN ledger, so the ordinary re-merge picks them up and nothing
|
|
9689
|
+
// downstream needs to know this call happened.
|
|
9690
|
+
const half = JSON.parse(readFileSync(P.commonLawGridHalf(MEANING_SEAT), "utf8"));
|
|
9691
|
+
half.extras = half.extras ?? {};
|
|
9692
|
+
half.extras.pr_risk = [...(Array.isArray(half.extras.pr_risk) ? half.extras.pr_risk : []), ...r.rows];
|
|
9693
|
+
atomicWrite(P.commonLawGridHalf(MEANING_SEAT), JSON.stringify(half, null, 2) + "\n");
|
|
9694
|
+
}
|
|
9695
|
+
}
|
|
9696
|
+
} catch (err) {
|
|
9697
|
+
// Never fatal: a re-issue that cannot even be attempted leaves the run exactly where it was,
|
|
9698
|
+
// and the disclosure below is what the ruling actually guarantees.
|
|
9699
|
+
runLog(run.runDir, { event: "connotation-reissue-failed", error: String(err?.message ?? err).slice(0, 160) });
|
|
9700
|
+
}
|
|
9701
|
+
try { return mergeCommonLawArtifacts(); } catch { /* still short — disclose below */ }
|
|
9702
|
+
}
|
|
9703
|
+
return mergeCommonLawArtifacts({ discloseUnfinished: true });
|
|
9704
|
+
};
|
|
9388
9705
|
const mergeWithConnotationRemedy = !clSplit ? null : async () => {
|
|
9389
9706
|
try { return mergeCommonLawArtifacts(); }
|
|
9390
9707
|
catch (e) {
|
|
@@ -9408,7 +9725,21 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9408
9725
|
// the token inline must keep opening this channel.
|
|
9409
9726
|
const tok = connotationRemedyToken(e);
|
|
9410
9727
|
const anyQuarantined = GRID_SEATS.some((h) => clHalves[h].quarantined);
|
|
9411
|
-
|
|
9728
|
+
// A quarantined half keeps its transient park path: that half is re-run whole and the re-merge
|
|
9729
|
+
// converges, so there is nothing here to disclose yet.
|
|
9730
|
+
// THE DROPPED-QUERY FAILURE CARRIES NO FORM TOKEN, and matching on the form tokens alone is why
|
|
9731
|
+
// this channel never opened for it: `connotationRemedyToken` reads the connotation FORM
|
|
9732
|
+
// vocabulary, and "merged half-grids dropped N dictated connotation queries" is not in it. The
|
|
9733
|
+
// run therefore died at the merge with the recovery path sitting unused beside it.
|
|
9734
|
+
const droppedQueries = /dropped \d+ dictated connotation quer/i.test(String(e?.message ?? ""));
|
|
9735
|
+
if (anyQuarantined) throw e;
|
|
9736
|
+
if (droppedQueries) return await reissueThenDisclose(e);
|
|
9737
|
+
if (!tok) throw e;
|
|
9738
|
+
// ── ONE RE-ASK OF THE MODEL, THEN THE ENGINE TAKES OVER ──────────────────────────────────
|
|
9739
|
+
// The loop stops being a loop. The model gets exactly one routed repair turn; after that the
|
|
9740
|
+
// engine re-issues the search itself and, failing that, the run delivers with the gap disclosed.
|
|
9741
|
+
// Before this, a second arrival here re-threw and the clearance died after a full paid gather.
|
|
9742
|
+
if (clConnotationRemedySpent) return await reissueThenDisclose(e);
|
|
9412
9743
|
clConnotationRemedySpent = true;
|
|
9413
9744
|
runLog(run.runDir, { event: "connotation-remedy", detail: tok.slice(0, 200) });
|
|
9414
9745
|
// — WHICH HALVES OWE ROWS, READ FROM THE FORM. This used to re-derive the violation set here,
|
|
@@ -9442,7 +9773,8 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9442
9773
|
const r = await stage("common-law-half", { ...ctx, axis: h }, { force: true, followup, sessionKey: clHalves[h].sessionKey, trigger: "connotation-remedy" });
|
|
9443
9774
|
if (!r.ok) note(`connotation remedy (half ${h}) failed mechanically (${r.fail}) — the re-merge below decides`);
|
|
9444
9775
|
});
|
|
9445
|
-
return mergeCommonLawArtifacts();
|
|
9776
|
+
try { return mergeCommonLawArtifacts(); }
|
|
9777
|
+
catch (e2) { return await reissueThenDisclose(e2); }
|
|
9446
9778
|
}
|
|
9447
9779
|
};
|
|
9448
9780
|
if (clSplit) await mergeWithConnotationRemedy();
|
|
@@ -12684,7 +13016,11 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12684
13016
|
reason: `synthesis_unaccounted_delivered:${duty.unaccounted.length} of ${duty.totals.owed} record(s) reached the findings surface and the delivered document accounts for none of them — neither a finding that names them nor a declination with a ground: ${sample}${duty.unaccounted.length > 4 ? " …" : ""}. The report ships with this named rather than being withheld; these records are open points a reader must weigh.`,
|
|
12685
13017
|
// — the READER's sentence: what is open, in a lawyer's nouns. Counts survive;
|
|
12686
13018
|
// the token, the record ids and the engine's nouns stay in `reason` and the run record.
|
|
12687
|
-
|
|
13019
|
+
// COMPOSED BY THE AUTHORITY, NOT SPELLED HERE. The same sentence has to be reachable from a
|
|
13020
|
+
// run that stored no clause, where these two objects are long gone and only the reason's own
|
|
13021
|
+
// counts survive; `terminal-clamp.mjs` composes it from the counts either way, so the fresh
|
|
13022
|
+
// run and the republished archive cannot drift apart.
|
|
13023
|
+
clause: clauseForDefect("synthesis_unaccounted_delivered", duty.unaccounted.length, duty.totals.owed),
|
|
12688
13024
|
});
|
|
12689
13025
|
}
|
|
12690
13026
|
}
|
|
@@ -12710,7 +13046,9 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12710
13046
|
// — the READER's sentence. "Floor row" is an engine noun; what the fact IS for a
|
|
12711
13047
|
// lawyer: live registrations identical or near-identical to the mark that the report does not
|
|
12712
13048
|
// individually address. Counts survive; token, ids and engine nouns stay in `reason`.
|
|
12713
|
-
|
|
13049
|
+
// Composed by the authority in `terminal-clamp.mjs` for the reason the sibling site above
|
|
13050
|
+
// gives: a republished pre-split run has to reach the same sentence from the counts alone.
|
|
13051
|
+
clause: clauseForDefect("floor_duty_undischarged", block.undischarged, block.floors),
|
|
12714
13052
|
});
|
|
12715
13053
|
}
|
|
12716
13054
|
}
|
|
@@ -12752,7 +13090,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12752
13090
|
for (const c of fresh) clampClauses.push(conditionClauses[conditions.indexOf(c)] ?? c);
|
|
12753
13091
|
clampReasons.push(...fresh);
|
|
12754
13092
|
if (verdict === "CLEAR") {
|
|
12755
|
-
runLog(run.runDir, { event: "coverage-floor-clamp", from: "CLEAR", to: "CONDITIONAL", legalActions: conditions.length });
|
|
13093
|
+
runLog(run.runDir, { event: "coverage-floor-clamp", cause: "forward-actions", from: "CLEAR", to: "CONDITIONAL", legalActions: conditions.length });
|
|
12756
13094
|
note(`deliver-conditional floor: the opinion names ${conditions.length} forward legal action(s) a human must take (${conditions[0].slice(0, 140)}${conditions.length > 1 ? `; +${conditions.length - 1} more` : ""}) — clamping CLEAR→CONDITIONAL (spec 64: the disposition is derived from the findings' named actions).`);
|
|
12757
13095
|
verdict = "CONDITIONAL";
|
|
12758
13096
|
writeRunStatus(ctx, { verdict });
|
|
@@ -12791,7 +13129,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12791
13129
|
// The CLAMP is outside that guard on purpose: a re-ask can flip a clamped verdict back to CLEAR,
|
|
12792
13130
|
// and it must meet this floor again with the reason already recorded.
|
|
12793
13131
|
if (droppedConditions.length && verdict === "CLEAR") {
|
|
12794
|
-
runLog(run.runDir, { event: "coverage-floor-clamp", from: "CLEAR", to: "CONDITIONAL", actionsDropped: droppedConditions.length });
|
|
13132
|
+
runLog(run.runDir, { event: "coverage-floor-clamp", cause: "dropped-actions", from: "CLEAR", to: "CONDITIONAL", actionsDropped: droppedConditions.length });
|
|
12795
13133
|
verdict = "CONDITIONAL";
|
|
12796
13134
|
writeRunStatus(ctx, { verdict });
|
|
12797
13135
|
}
|
|
@@ -12882,22 +13220,49 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12882
13220
|
// could-not-examine record, and an unfinished register slice — CONDITIONAL carries
|
|
12883
13221
|
// lawyer-judged/disclosed residue only.
|
|
12884
13222
|
if (coverageInsufficient || frameResidual || screenGateGap || seniorGap || registerGap || deadlineGap) {
|
|
13223
|
+
// THESE SENTENCES REACH A CLIENT AND THEY ARE NOT OURS TO WRITE (owner, 2026-09-17). One of
|
|
13224
|
+
// them — the screen-gate line, which says a mark "could not be record_fetched" — carries an
|
|
13225
|
+
// engine identifier into the list a client reads as the conditions on their result, by a route
|
|
13226
|
+
// `terminalClampDecision` guards and this one does not. A reader's sentence for it was written
|
|
13227
|
+
// here and refused: the objection was the class, not the wording. It is on the owner's design
|
|
13228
|
+
// table as audit item 25, and until he rules, this stays exactly as it was rather than carrying
|
|
13229
|
+
// a caveat a developer composed.
|
|
12885
13230
|
const reasons = [];
|
|
12886
|
-
|
|
12887
|
-
if (
|
|
12888
|
-
|
|
12889
|
-
if (
|
|
12890
|
-
if (
|
|
13231
|
+
const machinery = (reason) => reasons.push(reason);
|
|
13232
|
+
if (coverageInsufficient) machinery(`the lawyer judged a material slice not fully cleared: ${coverageJudgment.reason || "register coverage gap"}`);
|
|
13233
|
+
if (frameGap) machinery("the blind frame-diff flagged a dominant-element omission the reopen pass did not close");
|
|
13234
|
+
else if (frameDeferrals.length) machinery(`follow-ups left open this run: ${frameDeferrals.map((d) => plainDirective(d.directive)).slice(0, 3).join(", ")}`);
|
|
13235
|
+
if (screenGateGap) machinery(`${sgUnresolved.length} in-scope mark(s) dropped on goods could not be record_fetched (unverified): ${sgUnresolved.map((g) => g.mark).join(", ")}`);
|
|
13236
|
+
if (seniorGap) machinery(`the oldest registration in a verdict-driving family could not be retrieved (policy: clamp): ${(ctx.seniorRights?.rows ?? []).filter((r) => r.applicable && !r.verified).map((r) => r.mark).join(", ")}`);
|
|
12891
13237
|
if (registerGap) {
|
|
12892
|
-
if (regGap.deferred.length)
|
|
12893
|
-
if (regGap.taintAxes.length)
|
|
13238
|
+
if (regGap.deferred.length) machinery(`register coverage deferred on ${[...new Set(regGap.deferred.map((g) => g.axis))].join(", ")} — the search did not finish and must be re-run before this can be relied on`);
|
|
13239
|
+
if (regGap.taintAxes.length) machinery(`the ${regGap.taintAxes.join(", ")} register pass was cut down at the timeout wall and its self-reported coverage is unverified`);
|
|
12894
13240
|
// Named regressions (2026-07-22): `<MARK> (<owner> — <canonical uri>)` — a bare mark name
|
|
12895
13241
|
// shipped "ION, ION, ION" (three indistinguishable strings); the identity is front-loaded
|
|
12896
13242
|
// because the delivered statement truncates from the tail.
|
|
12897
|
-
if (regGap.recallRegressions.length)
|
|
13243
|
+
if (regGap.recallRegressions.length) machinery(`a prior-confirmed live conflict was neither carried nor justified this run: ${regGap.recallRegressions.slice(0, 3).map(formatRecallRegression).join(", ")}`);
|
|
12898
13244
|
}
|
|
12899
|
-
if (deadlineGap)
|
|
12900
|
-
|
|
13245
|
+
if (deadlineGap) machinery(`a recorded opposition deadline was delivered without its date: ${ctx.deadlineCarryMaterial.map((v) => `${v.mark_text ?? v.uri} (window closes ${v.opposition_end})`).slice(0, 3).join(", ")}`);
|
|
13246
|
+
// ── THE THIRD CLAMP SITE NAMES ITSELF, AND NAMES WHICH OF ITS SEVEN INPUTS FIRED ────────────────
|
|
13247
|
+
//
|
|
13248
|
+
// All three clamp sites emitted this event under one name with the same from/to, distinguishable
|
|
13249
|
+
// only by which optional payload key happened to be present. Two of them fired three milliseconds
|
|
13250
|
+
// apart on a production run — the first without `frameDeferred`, the second with it — which reads
|
|
13251
|
+
// as one decision logged twice. It was two different decisions wearing one name, and the test lane
|
|
13252
|
+
// reasonably discounted one of them. `cause` makes the event self-describing.
|
|
13253
|
+
//
|
|
13254
|
+
// THE EVENT NAME IS DELIBERATELY NOT SPLIT. It is a true statement about the effect — the verdict
|
|
13255
|
+
// was clamped — and something downstream may already count clamps in aggregate. A discriminator is
|
|
13256
|
+
// additive; three names would not be.
|
|
13257
|
+
//
|
|
13258
|
+
// This site is itself seven causes under one name, so it also lists WHICH fired rather than
|
|
13259
|
+
// leaving a reader to key on field presence and guess.
|
|
13260
|
+
const clampInputs = Object.entries({
|
|
13261
|
+
coverageInsufficient, frameGap, frameDeferred: frameDeferrals.length,
|
|
13262
|
+
screenGate: screenGateGap ? sgUnresolved.length : 0, seniorRight: seniorGap,
|
|
13263
|
+
registerGap, deadlineCarry: deadlineGap ? ctx.deadlineCarryMaterial.length : 0,
|
|
13264
|
+
}).filter(([, v]) => Boolean(v)).map(([k]) => k);
|
|
13265
|
+
runLog(run.runDir, { event: "coverage-floor-clamp", cause: "coverage", causes: clampInputs, from: "CLEAR", to: "CONDITIONAL", coverageInsufficient: coverageInsufficient || undefined, frameGap: frameGap || undefined, frameDeferred: frameDeferrals.length || undefined, screenGate: screenGateGap ? sgUnresolved.length : undefined, seniorRight: seniorGap || undefined, registerGap: registerGap ? { deferred: regGap.deferred.length, taint: regGap.taintAxes.length, recall: regGap.recallRegressions.length } : undefined, deadlineCarry: deadlineGap ? ctx.deadlineCarryMaterial.length : undefined });
|
|
12901
13266
|
note(`deliver-conditional floor: ${reasons.join("; ")} — clamping CLEAR→CONDITIONAL so the delivered status carries the gap (never withheld, never halted).`);
|
|
12902
13267
|
verdict = "CONDITIONAL";
|
|
12903
13268
|
// APPEND (dedup by exact text) — the legalActions arm may already have recorded conditions,
|