clearotron 0.3.2-beta.7 → 0.3.2-beta.9
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 +58 -26
- package/CONTRIBUTING.md +8 -8
- package/INSTALL.md +148 -81
- package/README.md +3 -3
- package/SECURITY.md +3 -3
- package/bin/brandowner.mjs +3 -3
- package/bin/framework-preflight.mjs +1 -1
- package/bin/onboard.mjs +637 -216
- package/bin/start.mjs +151 -27
- package/bin/update.mjs +58 -11
- package/build-info.json +2 -2
- package/docs/DELIVERY.md +2 -1
- package/docs/INTAKE.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/architecture/03-run-lifecycle.md +6 -6
- package/docs/architecture/04-configuration-reference.md +32 -14
- package/docs/architecture/05-config-governance.md +23 -8
- package/docs/architecture/05-customer-profiles.md +2 -2
- package/docs/architecture/06-operations-runbook.md +3 -3
- package/docs/architecture/08-development-guide.md +6 -6
- package/docs/configuration.md +5 -5
- package/docs/decisions/0003-credential-model.md +1 -1
- package/docs/writing-standard.md +4 -0
- package/driver/CHANGELOG.md +124 -0
- package/driver/README.md +3 -3
- package/driver/band-size.mjs +59 -0
- package/driver/binding-layers.mjs +1 -1
- package/driver/citation-census.json +3 -3
- package/driver/{prelim-variants-record.mjs → clearance-variants-record.mjs} +24 -24
- package/driver/common-law-receipts.mjs +2 -2
- package/driver/company-bundle.mjs +3 -3
- package/driver/compose-read.mjs +8 -14
- package/driver/config-inventory.mjs +112 -9
- package/driver/consumption-ledger.mjs +2 -2
- package/driver/contract-arm2-baseline.json +2 -5
- package/driver/contract-dictation-registry.mjs +19 -19
- package/driver/contract-e3-backlog.mjs +43 -43
- package/driver/contract-e3-baseline.json +14 -14
- package/driver/contract-vocabulary.mjs +68 -27
- package/driver/deliver-trigger.sh +16 -16
- package/driver/demo-container.mjs +3 -3
- package/driver/dev-portal.mjs +3 -3
- package/driver/disposition-call.mjs +1 -1
- package/driver/door-gates.mjs +41 -7
- package/driver/doubt-ledger.mjs +2 -2
- package/driver/drainer-identity.mjs +34 -8
- package/driver/driver.config.mjs +367 -104
- 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/README.md +1 -1
- package/driver/engine/mcp/dispositions-server.mjs +3 -3
- package/driver/engine/mcp/gather-config.mjs +9 -9
- package/driver/engine/mcp/perplexity-server.mjs +2 -2
- package/driver/engine/mcp/recording-server.mjs +18 -5
- package/driver/engine/openai-agent.mjs +4 -2
- package/driver/engine/probe.mjs +110 -23
- package/driver/enqueue-schema.mjs +6 -2
- package/driver/findings-model.mjs +6 -3
- package/driver/flag-snapshot.mjs +34 -8
- package/driver/form-neighbourhood.mjs +54 -7
- package/driver/framework.mjs +4 -4
- package/driver/gateway.mjs +36 -24
- package/driver/jx-lanes.mjs +23 -4
- package/driver/jx-units.mjs +7 -4
- package/driver/jx.mjs +34 -4
- package/driver/knockout-review-record.mjs +56 -4
- package/driver/known-conflicts.mjs +1 -1
- package/driver/matter-frame-record.mjs +90 -1
- package/driver/named-band.mjs +1 -1
- package/driver/ordinary-words.mjs +51 -0
- package/driver/outbox-backoff.mjs +31 -16
- package/driver/package.json +1 -1
- package/driver/partial-payload-baseline.json +2 -2
- package/driver/phase0.mjs +3 -3
- package/driver/pipeline-knockout.mjs +5 -5
- package/driver/pipeline.mjs +396 -81
- package/driver/placement-form.mjs +77 -1
- package/driver/placement-model.mjs +1 -1
- package/driver/portal-config-view.mjs +30 -1
- package/driver/portal-report.mjs +107 -6
- package/driver/portal-service.mjs +80 -14
- package/driver/portal-upstream.mjs +1 -1
- package/driver/predelivery-lint.mjs +12 -2
- package/driver/preserve-merge.mjs +3 -3
- package/driver/product-rows.mjs +2 -2
- package/driver/products.mjs +1 -1
- package/driver/profiles/README.md +3 -3
- package/driver/profiles/demo-brand-owner.json +2 -2
- package/driver/profiles.mjs +55 -17
- package/driver/progress.mjs +18 -8
- package/driver/provider-usage.mjs +8 -8
- package/driver/publish/index.mjs +154 -8
- package/driver/publish/knockout.mjs +39 -5
- package/driver/publish/pool-admin.mjs +1 -1
- package/driver/publish/publish-inputs.mjs +18 -2
- package/driver/publish/render-knockout.mjs +184 -31
- package/driver/publish/render.mjs +323 -93
- package/driver/publish/report-data.mjs +4 -1
- package/driver/publish/report-topbar.mjs +58 -0
- package/driver/publish/search-depth.mjs +133 -4
- package/driver/publish/templates/report.css +78 -4
- package/driver/publish/xlsx.mjs +20 -1
- package/driver/queue-order.mjs +2 -2
- package/driver/recording-agreement.mjs +1 -1
- package/driver/reference-score.mjs +1 -1
- package/driver/register-availability.mjs +2 -2
- package/driver/register-count.mjs +50 -5
- package/driver/register-coverage.mjs +161 -1
- package/driver/register-digest-record.mjs +236 -11
- package/driver/register-grant-vocabulary.mjs +1 -1
- package/driver/register-plan.mjs +189 -2
- package/driver/registry-fidelity.mjs +3 -3
- package/driver/repair-composers.mjs +1 -1
- package/driver/repair-contract.mjs +1 -1
- package/driver/replay-archive.mjs +6 -6
- package/driver/report-overview-record.mjs +2 -2
- package/driver/result-noun-fields.mjs +2 -2
- package/driver/run-economics.mjs +41 -10
- package/driver/run-requirements.mjs +173 -9
- package/driver/runner.mjs +5 -5
- package/driver/scope-facts.mjs +20 -5
- package/driver/scope-ledger.mjs +5 -5
- package/driver/search-policy.mjs +22 -12
- package/driver/skills/README.md +15 -15
- package/driver/skills/blind-frame/SKILL.md +2 -2
- package/driver/skills/case-law-citation/SKILL.md +4 -4
- package/driver/skills/case-law-citation/sources/eurlex.md +1 -1
- package/driver/skills/{prelim-common-law → clearance-common-law}/SKILL.md +22 -22
- package/driver/skills/{prelim-common-law → clearance-common-law}/perplexity-prompts.md +1 -1
- package/driver/skills/{prelim-register → clearance-register}/SKILL.md +10 -10
- package/driver/skills/{prelim-register → clearance-register}/digest.md +2 -2
- package/driver/skills/{prelim-register → clearance-register}/providers/README.md +1 -1
- package/driver/skills/{prelim-register → clearance-register}/providers/clarivate.md +37 -35
- package/driver/skills/{prelim-register → clearance-register}/providers/corsearch.md +20 -11
- package/driver/skills/{prelim-register → clearance-register}/providers/signa.md +5 -5
- package/driver/skills/{prelim-register → clearance-register}/register-recipes.md +3 -3
- package/driver/skills/{prelim-register → clearance-register}/status-rules.md +2 -2
- package/driver/skills/{prelim-register → clearance-register}/stealth-filer-indicators.md +1 -1
- package/driver/skills/{prelim-register → clearance-register}/unit.md +2 -2
- package/driver/skills/{prelim-search → clearance-search}/SKILL.md +31 -31
- package/driver/skills/{prelim-search → clearance-search}/delivery-contract.md +1 -1
- package/driver/skills/{prelim-search → clearance-search}/phase2-execution.md +18 -18
- package/driver/skills/{prelim-search → clearance-search}/synthesis-rules.md +7 -7
- package/driver/skills/{prelim-variants → clearance-variants}/SKILL.md +18 -18
- package/driver/skills/{prelim-variants → clearance-variants}/transliteration-scripts.md +5 -5
- package/driver/skills/frame-diff/SKILL.md +1 -1
- package/driver/skills/knockout-assess/SKILL.md +10 -7
- package/driver/skills/matter-frame/SKILL.md +3 -3
- package/driver/skills/narrative-refutation/SKILL.md +9 -9
- package/driver/skills/placement-inquiry/SKILL.md +5 -5
- package/driver/stage-context.mjs +1 -1
- package/driver/stages-knockout.mjs +4 -4
- package/driver/stages.mjs +65 -61
- package/driver/status-snapshot.mjs +2 -2
- package/driver/suite-census.json +340 -136
- package/driver/surface-exit-verdict.mjs +58 -0
- package/driver/systemd/README.md +9 -6
- package/driver/systemd/clearotron-worker.service +1 -1
- package/driver/terminal-clamp.mjs +109 -1
- package/driver/tokens.mjs +169 -3
- package/driver/unit-environment.mjs +42 -15
- package/driver/unit-inventory.mjs +19 -2
- package/driver/usage-ledger.mjs +1 -1
- package/driver/variant-manifest-model.mjs +4 -4
- package/driver/verify-knockout.mjs +27 -0
- package/driver/verify.mjs +94 -6
- package/driver/whatif-queue.mjs +1 -1
- package/driver/wordlists/en.txt +63906 -0
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/README.md +1 -1
- package/mcp-server/lib/README.md +1 -1
- package/mcp-server/lib/options.mjs +8 -7
- package/mcp-server/lib/plan.mjs +18 -2
- package/mcp-server/lib/runs.mjs +1 -1
- package/mcp-server/lib/usage.mjs +3 -3
- package/mcp-server/lib/whatif.mjs +1 -1
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +18 -1
- package/package.json +12 -11
- package/portal-ui/dist/assets/{index-CVOIvdhc.css → index-CtvwLCti.css} +207 -3
- package/portal-ui/dist/assets/{index-5UyqAyNM.js → index-EVaSo5-g.js} +1580 -527
- package/portal-ui/dist/index.html +2 -2
- package/portal-ui/package.json +1 -1
- package/providers/README.md +1 -1
- package/providers/_shared/enumerate.mjs +6 -6
- package/providers/_shared/execute-plan.mjs +3 -3
- package/providers/_shared/ledger.mjs +119 -5
- package/providers/_shared/provider-text.mjs +2 -2
- package/providers/_shared/screen.mjs +2 -2
- package/providers/_shared/script-form.mjs +3 -3
- package/providers/_shared/territory-codes.mjs +23 -3
- package/providers/clarivate/README.md +1 -1
- package/providers/clarivate/src/capabilities.js +12 -12
- package/providers/clarivate/src/core.js +37 -43
- package/providers/corsearch/README.md +1 -1
- package/providers/corsearch/src/capabilities.js +5 -5
- package/providers/corsearch/src/core.js +3 -3
- 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 +1 -1
- package/providers/signa/README.md +1 -1
- package/providers/signa/src/capabilities.js +42 -49
- package/providers/signa/src/core.js +106 -29
- package/providers/uspto-local/README.md +1 -1
- package/providers/uspto-local/src/sync.js +1 -1
- package/scripts/README.md +1 -0
- package/scripts/ask-ai-render-check.mjs +127 -1
- package/scripts/authority-boundary-probe.mjs +8 -6
- package/scripts/backfill-started-at.mjs +2 -2
- package/scripts/census-merge-driver.mjs +33 -2
- package/scripts/citation-anchor-report.mjs +181 -0
- package/scripts/dead-names.mjs +1 -1
- package/scripts/deprecate-below.mjs +66 -8
- package/scripts/drain-preflight.mjs +1 -1
- package/scripts/e2e.mjs +174 -0
- package/scripts/env-audit.mjs +39 -6
- package/scripts/env-classify.mjs +20 -2
- package/scripts/freeze-example-run.mjs +61 -18
- package/scripts/generated-files-are-current.mjs +69 -4
- package/scripts/live-surface-check.mjs +124 -41
- package/scripts/markdown-link-check.mjs +1 -1
- package/scripts/merge-shape-check.mjs +242 -0
- package/scripts/mint-names-in-force.mjs +5 -5
- package/scripts/mint-offered-territories.mjs +72 -0
- package/scripts/mint-public-residue.mjs +2 -2
- package/scripts/mint-reference-strip-backlog.mjs +2 -2
- package/scripts/mint-suite-census.mjs +75 -2
- package/scripts/mint-writing-standard-backlog.mjs +2 -2
- package/scripts/purge-runs.mjs +7 -7
- package/scripts/reconcile-runs.mjs +2 -2
- package/scripts/release-approve-parked.mjs +20 -2
- package/scripts/release-await-cut.mjs +120 -1
- package/scripts/release-note-required.mjs +76 -8
- package/scripts/report-header-render-check.mjs +164 -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/brand.mjs +27 -0
- package/shared/connect-clients.mjs +39 -11
- package/shared/deployment-box.mjs +7 -2
- package/shared/driver-dir.mjs +1 -1
- package/shared/env-aliases.mjs +1 -1
- package/shared/identifier-scan.mjs +65 -9
- package/shared/identifier-sentinels.mjs +22 -0
- package/shared/names-in-force.mjs +4 -2
- package/shared/offered-territories.json +738 -0
- package/shared/pre-rename-spellings.mjs +53 -0
- package/shared/reference-guard-classes.mjs +40 -2
- package/shared/stdio-connect.mjs +39 -4
- package/shared/tree-commit.mjs +48 -0
- /package/driver/skills/{prelim-register → clearance-register}/providers/euipo.md +0 -0
- /package/driver/skills/{prelim-register → clearance-register}/providers/free-tier.md +0 -0
- /package/driver/skills/{prelim-register → clearance-register}/providers/uspto-local.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/field-doctrine-pharma.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/firm-wide-reasoning.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/report-prose.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.manifest.json +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.manifest.json +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework.manifest.json +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/template-formatting.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/templates/email/generic.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/templates/search-request-form.html +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/worked-examples-demo.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/worked-examples.md +0 -0
package/driver/pipeline.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
2
|
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
|
-
// pipeline.mjs — the deterministic driver: sequences every
|
|
3
|
+
// pipeline.mjs — the deterministic driver: sequences every clearance-search stage as a blocking engine
|
|
4
4
|
// turn, joins the fan-out in code (the fan-in barrier), gates on the refutation
|
|
5
5
|
// verdict, and never parks (there is no LLM continuation decision anywhere). CLI: `node pipeline.mjs --job <file.json>`.
|
|
6
6
|
|
|
@@ -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,
|
|
@@ -79,7 +80,7 @@ import { resolveSearchPolicy, gateResolvedPolicy, loadRecipes, policyFor, isRegi
|
|
|
79
80
|
import { profileOrdinals } from "./profile-selection.mjs"; // lever 3 — driver selection
|
|
80
81
|
// THE OFFERING'S own sentence about where the native-language investigation can be bought. It reaches a
|
|
81
82
|
// CLIENT REPORT through the script-scope coverage row, which is why it is imported rather than typed:
|
|
82
|
-
// the row used to name "Depth 5 (
|
|
83
|
+
// the row used to name "Depth 5 (clearance-jx)" — a retired key on a retired ladder — as its remedy.
|
|
83
84
|
import { NATIVE_LANGUAGE_REMEDY } from "./products.mjs";
|
|
84
85
|
import { resolveTerritories, defaultTerritoryState } from "./effective-scope.mjs"; // the ONE territory ladder (the geography stamp included) + the stored-defaults reading
|
|
85
86
|
import { acquireSlot, releaseSlot } from "./slot-lock.mjs";
|
|
@@ -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
|
|
@@ -167,10 +170,10 @@ import { JURISDICTION_ADAPTERS, LANGUAGE_LANES, scopeJurisdictions as jxScopeJur
|
|
|
167
170
|
// into boundDominantElementFrom, so the driver's bind and the record tool's bind are ONE body. Neither
|
|
168
171
|
// parseBlindFrameModel nor dominantElementFromManifest is called from this file any more.
|
|
169
172
|
import { boundDominantElementFrom } from "./frame-diff-record.mjs";
|
|
170
|
-
import { recordedScopeLedgerRows } from "./
|
|
173
|
+
import { recordedScopeLedgerRows } from "./clearance-variants-record.mjs";
|
|
171
174
|
import { parseFrameDiff, applyDominantBackstop, firingDirectives, reopenKey, alreadyAttemptedReopen, partitionFiring, frameResidualGaps, jurisdictionScopeFlags, deriveDirectiveRemedy, firingDirectivesLenient } from "./frame-diff-model.mjs";
|
|
172
175
|
import { verifyRegisterDirectiveClose } from "./close-verify.mjs";
|
|
173
|
-
import { renderFormNeighbourhoodJson, parseFormNeighbourhoodJson, dispatchedQueriesFromBand, formGapDirectives, markText } from "./form-neighbourhood.mjs";
|
|
176
|
+
import { renderFormNeighbourhoodJson, parseFormNeighbourhoodJson, dispatchedQueriesFromBand, formGapDirectives, markText } from "./form-neighbourhood.mjs"; import { loadOrdinaryWords } from "./ordinary-words.mjs";
|
|
174
177
|
import { findRecallFloorViolations, findReviewFreshnessViolation, findSeedNeutralityViolations, findProbativeGradingViolations, findStatusHonestyViolation, findMatrixCeilingViolations, findDeadlineUrgencyMiss, findUnresolvedDisagreements, findOrphanVerificationFlags, findUncrossCheckedDemotions, findRecallRegressionViolations, findDeadlineCarryViolations, formatRecallRegression } from "./reasoning-tripwires.mjs";
|
|
175
178
|
import { findRuleShapeFlags } from "./rule-shape.mjs";
|
|
176
179
|
import { failureSignature, classifyFailureReason, decideRecovery, createRepairLedger, countTrailingStageStrikes, countRecoveryLanes, weatherCeilingFor, TRANSIENT_RE, REFUSAL_TERMINAL_KIND, fanInMissingEvidence, retryCannotHelpWith, unnamedStructuredFailure, classificationSource, isCapPark, capParkSchedule, capWaitFrom, humanWait } from "./repairs.mjs";
|
|
@@ -936,7 +939,7 @@ function deriveGridSpec(ctx) {
|
|
|
936
939
|
// 2026-08-11 codex R2 run (no grid spec authored at all), and it is the branch that emits NO event
|
|
937
940
|
// today, which is why absence was the only signal and absence is not a record.
|
|
938
941
|
// The three inputs are recorded, not just their verdict. Working hit exactly this wall: the
|
|
939
|
-
// 2026-08-11 run authored no spec, `
|
|
942
|
+
// 2026-08-11 run authored no spec, `clearance-variants` had succeeded, and WHICH of the three conjuncts
|
|
940
943
|
// was false could not be recovered from any artifact — so the round could name the path but not the
|
|
941
944
|
// cause. A record that says "no spec" and not "no spec BECAUSE the profile carried no platforms" just
|
|
942
945
|
// moves the same unanswerable question one step along.
|
|
@@ -1717,7 +1720,7 @@ export function attachSearchPolicy(ctx, job, { write = true } = {}) { // @inte
|
|
|
1717
1720
|
// a clearance (review 2026-07-17 — the crash-reclaim/reconstruct window).
|
|
1718
1721
|
if (!noSelector)
|
|
1719
1722
|
throw new Error(`run dir has no frozen search policy but the job names a selector (product=${JSON.stringify(job?.product ?? null)}, recipeKey=${JSON.stringify(job?.recipeKey ?? null)}) — refusing the legacy-implicit clearotron assumption; re-dispatch cold so the policy is minted`);
|
|
1720
|
-
ctx.searchPolicy = { schema: 1, level: "
|
|
1723
|
+
ctx.searchPolicy = { schema: 1, level: "clearance", pipeline: "clearance", stageLabel: "Depth 4", components: {}, recipe: null, origins: { level: "legacy-implicit" } };
|
|
1721
1724
|
} else {
|
|
1722
1725
|
let resolved;
|
|
1723
1726
|
// force:true — the recipe store is written by a LIVE service; a long-lived driver process must see a
|
|
@@ -1902,7 +1905,7 @@ function renderDocumentCoverageFromRecords(ctx, trigger) {
|
|
|
1902
1905
|
// The set the GATES consume, not `_records/` alone: a run whose bodies live only in the ledger would
|
|
1903
1906
|
// otherwise be described as one that fetched nothing — the defect this renders against, re-created by
|
|
1904
1907
|
// reading a narrower artifact than the one that holds the answer.
|
|
1905
|
-
const { records } = assembleRunRecords(P.runDir, `
|
|
1908
|
+
const { records } = assembleRunRecords(P.runDir, `clearance-${ctx.run.slug}-${ctx.run.codename}-`);
|
|
1906
1909
|
const summary = documentCoverage({ records, findings: safeReadJson(P.findings) });
|
|
1907
1910
|
const section = renderDocumentCoverageSection(summary);
|
|
1908
1911
|
if (!section) return;
|
|
@@ -2100,7 +2103,7 @@ function deriveCoverageLedgerJson(ctx, trigger) {
|
|
|
2100
2103
|
}
|
|
2101
2104
|
}
|
|
2102
2105
|
|
|
2103
|
-
// Frame-omission design — CODE-DERIVE scope-ledger.json from the validated
|
|
2106
|
+
// Frame-omission design — CODE-DERIVE scope-ledger.json from the validated clearance-variants prose
|
|
2104
2107
|
// `### Scope ledger` table (so the JSON is authored by the driver, not the model, and matches the prose
|
|
2105
2108
|
// by construction — exactly the deriveCoverageLedgerJson pattern). NEVER-KILL: a manifest with no/unparseable
|
|
2106
2109
|
// Scope ledger (legacy artifacts, a terse run) logs a note and skips the write; the frame-diff then reads the
|
|
@@ -2129,7 +2132,7 @@ function deriveScopeLedgerJson(ctx) {
|
|
|
2129
2132
|
// are different facts about a run, and a reader of an archived ledger must be able to tell which.
|
|
2130
2133
|
runLog(P.runDir, { event: "scope-ledger-derived", source: recordedRows ? "typed-call" : "prose-parse" });
|
|
2131
2134
|
} catch (e) {
|
|
2132
|
-
note(`
|
|
2135
|
+
note(`clearance-variants: scope-ledger derivation skipped (${String(e.message).slice(0, 100)}) — frame-diff reads the manifest prose`);
|
|
2133
2136
|
runLog(P.runDir, { event: "scope-ledger-skipped", reason: `derive_failed:${String(e.message).slice(0, 80)}` });
|
|
2134
2137
|
}
|
|
2135
2138
|
}
|
|
@@ -2176,14 +2179,14 @@ function deriveFormNeighbourhood(ctx) {
|
|
|
2176
2179
|
// The MARK is the floor's fallback seed and the only input to it that is not model output —
|
|
2177
2180
|
// resolved exactly as every other job-mark consumer resolves it (stages.mjs blind-frame).
|
|
2178
2181
|
const mark = ctx.job.marks ?? ctx.job.markName ?? ctx.job.name ?? "";
|
|
2179
|
-
const json = renderFormNeighbourhoodJson(manifestMd, { markets, droppedAxes, model, mark });
|
|
2182
|
+
const ow = loadOrdinaryWords("en"), json = renderFormNeighbourhoodJson(manifestMd, { markets, droppedAxes, model, mark, ordinaryWords: ow.words }); // an unloadable word list removes nothing
|
|
2180
2183
|
const tmp = P.formNeighbourhood + ".tmp";
|
|
2181
2184
|
writeFileSync(tmp, json);
|
|
2182
2185
|
renameSync(tmp, P.formNeighbourhood);
|
|
2183
|
-
let exact = 0, floor = 0, added = 0, seededFrom = "", seeds = [];
|
|
2186
|
+
let exact = 0, floor = 0, added = 0, seededFrom = "", seeds = [], notSearched = 0;
|
|
2184
2187
|
try {
|
|
2185
2188
|
const o = JSON.parse(json);
|
|
2186
|
-
exact = o.elements.reduce((a, e) => a + (e.band?.exactQueries?.length ?? 0), 0);
|
|
2189
|
+
exact = o.elements.reduce((a, e) => a + (e.band?.exactQueries?.length ?? 0), 0); notSearched = o.elements.reduce((a, e) => a + (e.band?.ordinaryWordDifferentSound?.length ?? 0), 0);
|
|
2187
2190
|
floor = o.variant_floor?.counts?.floor ?? 0;
|
|
2188
2191
|
added = o.variant_floor?.counts?.model_added ?? 0;
|
|
2189
2192
|
seededFrom = String(o.seeded_from ?? "");
|
|
@@ -2191,11 +2194,11 @@ function deriveFormNeighbourhood(ctx) {
|
|
|
2191
2194
|
} catch { /* counts are telemetry only */ }
|
|
2192
2195
|
// The SEED SET is logged by name, because it is the input that decides whether two runs of the same
|
|
2193
2196
|
// matter can produce the same floor — a floor count alone cannot tell a stable run from a re-anchored one.
|
|
2194
|
-
runLog(P.runDir, { event: "form-neighbourhood-derived", exact, floor, modelAdded: added, droppedAxes, seeds, seededFrom });
|
|
2197
|
+
runLog(P.runDir, { event: "form-neighbourhood-derived", exact, floor, modelAdded: added, droppedAxes, seeds, seededFrom, ordinaryWordNotSearched: notSearched, ...(ow.error ? { ordinaryWordListError: ow.error } : {}) });
|
|
2195
2198
|
// The fallback is not a quiet degrade: it means this run's variant floor rests on the mark alone
|
|
2196
2199
|
// because the stage named no usable element. Loud, so the absence is a finding and not a shrug.
|
|
2197
2200
|
if (seededFrom.startsWith("job mark"))
|
|
2198
|
-
note(`
|
|
2201
|
+
note(`clearance-variants: variant floor seeded from the JOB MARK — the manifest named no usable distinctive element (${floor} floor terms generated; the floor is complete, its seed is not the model's)`);
|
|
2199
2202
|
} catch (e) {
|
|
2200
2203
|
// The reason is carried WHOLE. It used to be truncated to 80 characters into the runLog and
|
|
2201
2204
|
// 100 into the note, which cut the part that says WHICH cause fired — and the empty floor this path
|
|
@@ -2203,7 +2206,7 @@ function deriveFormNeighbourhood(ctx) {
|
|
|
2203
2206
|
// compiles and freezes with no floor entries and no error, looking healthy. This line is the only
|
|
2204
2207
|
// record that the mechanical floor is missing from the run, so it says the whole of why.
|
|
2205
2208
|
const why = String(e.message);
|
|
2206
|
-
note(`
|
|
2209
|
+
note(`clearance-variants: form-neighbourhood derivation skipped (${why}) — register falls back to the manifest variants, so this run has NO mechanical form floor`);
|
|
2207
2210
|
runLog(P.runDir, { event: "form-neighbourhood-skipped", reason: `derive_failed:${why}`, mark: markText(ctx.job.marks ?? ctx.job.markName ?? ctx.job.name ?? "") });
|
|
2208
2211
|
}
|
|
2209
2212
|
}
|
|
@@ -2258,6 +2261,69 @@ function logFullyDeferredAxes(plan, P, source) {
|
|
|
2258
2261
|
// `frozenOnly` (item): read the FROZEN plan or nothing. reconstructCtx is an introspection path —
|
|
2259
2262
|
// it must never compile and freeze a plan into an existing run as a side effect of being asked what the
|
|
2260
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: `clearance-${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
|
+
|
|
2261
2327
|
function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
2262
2328
|
const P = ctx.paths;
|
|
2263
2329
|
if (frozenOnly) {
|
|
@@ -2281,7 +2347,30 @@ function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
|
2281
2347
|
ctx.registerPlan = null;
|
|
2282
2348
|
let compiled;
|
|
2283
2349
|
try {
|
|
2284
|
-
|
|
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
|
+
}
|
|
2285
2374
|
let form = null;
|
|
2286
2375
|
try { form = JSON.parse(readFileSync(P.formNeighbourhood, "utf8")); } catch { /* form band optional */ }
|
|
2287
2376
|
compiled = compileRegisterPlan({
|
|
@@ -2296,7 +2385,12 @@ function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
|
2296
2385
|
// sweep); fatal on a provider whose regions[] is mandatory, where every entry then errored on its
|
|
2297
2386
|
// count probe and the whole plan joined MISSING at fan-in (review finding 11).
|
|
2298
2387
|
job: { jobKey: ctx.run.slug, classes: [...new Set([...inScopeClassList(ctx.job, ctx.profile).map(String), ...frameIdentifiedClasses(P.runDir)])], jurisdictions: registerJurisdictions(ctx.job, ctx.profile) },
|
|
2299
|
-
form, skillVersion: "
|
|
2388
|
+
form, skillVersion: "clearance-register@spec48",
|
|
2389
|
+
// — WHICH ELEMENT THE EXCLUSION TOOK OUT, so the compile can make its form band unreachable
|
|
2390
|
+
// rather than merely unasked-for. Null unless the ownership receipt verified, which is the same
|
|
2391
|
+
// condition the exclusion itself runs under: an unverified receipt excludes nothing, and it must
|
|
2392
|
+
// not remove a band either.
|
|
2393
|
+
houseElement: houseConfirmation ? houseReceipt?.element : null,
|
|
2300
2394
|
// phase 3 — the plan is compiled AGAINST THE ACTIVE PROVIDER's declared capabilities, so the
|
|
2301
2395
|
// frozen artifact is executable by construction: the OR-stack split uses that provider's width,
|
|
2302
2396
|
// jurisdictions are translated into its office vocabulary (EU→EM on Compumark), and a predicate
|
|
@@ -2308,6 +2402,19 @@ function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
|
2308
2402
|
// rides the plan as a disclosed deferral, never as a silently narrower search.
|
|
2309
2403
|
unavailableOffices: registerUnavailableOffices(),
|
|
2310
2404
|
});
|
|
2405
|
+
// REQUIREMENT 3 — the element is still checked ONCE, as a confirmation of the client's own live
|
|
2406
|
+
// registrations rather than as a conflict sweep, so the exclusion is evidenced on the report by a
|
|
2407
|
+
// query that ran. Appended after the compile because it is not derived from the manifest: it is the
|
|
2408
|
+
// one question about the element the plan still owes.
|
|
2409
|
+
if (houseConfirmation && compiled?.entries) {
|
|
2410
|
+
const used = new Set(compiled.entries.map((e) => e.qid));
|
|
2411
|
+
compiled.entries.push({
|
|
2412
|
+
...houseConfirmation,
|
|
2413
|
+
qid: mintSupplementalQid({ prefix: "house", term: houseConfirmation.term, used }),
|
|
2414
|
+
nice_classes: compiled.nice_classes ?? [],
|
|
2415
|
+
regions: compiled.regions ?? [],
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2311
2418
|
} catch (e) {
|
|
2312
2419
|
// NEVER-KILL at mint time: no/malformed sibling or a class-less matter degrades to the legacy
|
|
2313
2420
|
// path with a logged reason — the plan improves the run, it never turns delivery off.
|
|
@@ -3648,6 +3755,27 @@ export function digestDispatchExtra(ctx, { trigger = "fresh", willRun = true, ex
|
|
|
3648
3755
|
}
|
|
3649
3756
|
}
|
|
3650
3757
|
} 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)}`); }
|
|
3758
|
+
// — THE BATCH BLOCK: how the driver split this run's band, and which batches are still outstanding.
|
|
3759
|
+
//
|
|
3760
|
+
// The seat is told the split HERE rather than in the stage's dictation because the numbers are the
|
|
3761
|
+
// 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
|
|
3762
|
+
// resume the outstanding set is what the killed attempt did not reach. The dictation says the rule;
|
|
3763
|
+
// this says the arithmetic.
|
|
3764
|
+
//
|
|
3765
|
+
// BEST-EFFORT, AND SAFE BY CONSTRUCTION rather than by argument — the coverage brief's precedent above,
|
|
3766
|
+
// for its reason. Nothing here enforces anything: a call naming a batch is judged against the driver's
|
|
3767
|
+
// own split whether or not this block composed, the tool's refusal re-states what is outstanding, and
|
|
3768
|
+
// the stage's exit gate refuses a document that ends fewer records than the run carried in. A brief
|
|
3769
|
+
// that fails to compose costs a corrective round; it cannot cost a record.
|
|
3770
|
+
try {
|
|
3771
|
+
const gap = digestAccountingGap(P.runDir);
|
|
3772
|
+
const block = digestBatchBrief(gap);
|
|
3773
|
+
if (block) {
|
|
3774
|
+
out = out ? `${out}\n\n${block}` : block;
|
|
3775
|
+
runLog(P.runDir, { event: "digest-batch-brief", trigger, owed: gap.owed.length,
|
|
3776
|
+
batches: batchesOf(gap.owed).length, unaccounted: gap.unaccounted.length });
|
|
3777
|
+
}
|
|
3778
|
+
} 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)}`); }
|
|
3651
3779
|
// AD-2 A9 (E2E-R2) + the P5 review (2026-07-31): a corrective/repair pass is told NOT to re-read the
|
|
3652
3780
|
// whole placement file — the per-candidate tiers are in placements.json — but the RULINGS TAIL (band
|
|
3653
3781
|
// reconciliation, disagreements, coverage rulings, open questions) lives ONLY in the md. Leaving it to
|
|
@@ -3710,7 +3838,7 @@ export function digestDispatchExtra(ctx, { trigger = "fresh", willRun = true, ex
|
|
|
3710
3838
|
// The old `findFloorBreaches` read ⭐-marked lines out of the manifest TEXT, tokenised them, dropped
|
|
3711
3839
|
// generic words and matched the remainder against coverage-row text. That join is why it needed free text
|
|
3712
3840
|
// at all, and it is what conversion 3 made unreachable. This one reads `search_floor` — axis names, closed
|
|
3713
|
-
// against REGISTER_AXES, designated by
|
|
3841
|
+
// against REGISTER_AXES, designated by clearance-variants in an earlier turn — and joins on `axis`, which
|
|
3714
3842
|
// both sides type. No tokens, no matching, nothing to go quietly wrong.
|
|
3715
3843
|
//
|
|
3716
3844
|
// A BREACH IS: an axis this mark's floor obliges, carrying a row labelled `coverage-limited`. That label
|
|
@@ -3736,7 +3864,7 @@ export function findFloorBreaches(ledger, floorAxes) { // @internal
|
|
|
3736
3864
|
* a pass, because the whole mechanism is opt-in — a run with no designation owes no floor, and a run whose
|
|
3737
3865
|
* manifest cannot be read has no designation to honour. The refusal for an absent or unparseable manifest
|
|
3738
3866
|
* belongs to the stage that writes it and already exists there — verify.mjs:1335 runs the same parser
|
|
3739
|
-
* through `checkSiblingJson` and fails
|
|
3867
|
+
* through `checkSiblingJson` and fails clearance-variants with `variantmodel_missing`. Checked, because
|
|
3740
3868
|
* "something else refuses it" is exactly the assumption that turns a swallowed error into a silent pass.
|
|
3741
3869
|
*/
|
|
3742
3870
|
export function readFloorAxes(paths) { // @internal
|
|
@@ -3745,7 +3873,7 @@ export function readFloorAxes(paths) { // @internal
|
|
|
3745
3873
|
}
|
|
3746
3874
|
|
|
3747
3875
|
// A never-active axis self-writes a harmless "not applicable" digest — the skill MANDATES the exact
|
|
3748
|
-
// scope wording (skills/
|
|
3876
|
+
// scope wording (skills/clearance-register/unit.md: `query:"<axis> not applicable"`,
|
|
3749
3877
|
// `reason:"not applicable — <why>"`), so the coverage-ledger row lands as `<axis> / axis not applicable`.
|
|
3750
3878
|
// It is `deferred` only because a non-search must not be dressed as a clean — it is NOT floor work left
|
|
3751
3879
|
// open. Keyed on the self-digest scope (deterministic, skill-dictated), never a genuine floor's
|
|
@@ -3936,7 +4064,7 @@ export async function runSaturationProbeCodeSide(ctx, planExec) { // @internal
|
|
|
3936
4064
|
const label = `register-unit:${a}`;
|
|
3937
4065
|
const out = P.registerUnit(a);
|
|
3938
4066
|
const bandPath = P.registerBand(a);
|
|
3939
|
-
const sessionKey = `
|
|
4067
|
+
const sessionKey = `clearance-${ctx.run.slug}-${ctx.run.codename}-register-unit-${a}`;
|
|
3940
4068
|
// crash-resume idempotency — mirrors stageOnce's skip: output present + valid ⇒ never re-execute.
|
|
3941
4069
|
const forced = forceFromActive(ctx, "register-unit");
|
|
3942
4070
|
// — hoisted above the skip return. Below it, this member's dispatch row carries boundaries and its
|
|
@@ -4056,7 +4184,7 @@ function writeSatProbeAuditNote({ ctx, out, bandPath, entries }) {
|
|
|
4056
4184
|
|
|
4057
4185
|
// ── stage contracts (2026-07-30 review round) ──────────────────────────────────────────────────────
|
|
4058
4186
|
// _driver/stage-contracts.json records, PER STAGE, the output contract the prompt THIS driver
|
|
4059
|
-
// dispatched holds the artifact to (declared on the stage def, e.g.
|
|
4187
|
+
// dispatched holds the artifact to (declared on the stage def, e.g. clearance-variants
|
|
4060
4188
|
// `contract:{romanization:1}`). Written at DISPATCH time only — a skipped stage never gains one — so
|
|
4061
4189
|
// it is evidence of PROMPT VINTAGE, which is the only honest gate for a new validation rule:
|
|
4062
4190
|
// - crash-resume: the skip check re-validates the completed output BEFORE any dispatch, marker
|
|
@@ -4220,7 +4348,7 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4220
4348
|
// skips — i.e. on the babysit surface, in the middle of the shift where someone is watching it.
|
|
4221
4349
|
runLog(P.runDir, { event: "skip", stage: label, trigger: "skip", model: won?.model ?? resolveModel(model), output: outputMeta(out), ...stageWallFields(tDispatch) });
|
|
4222
4350
|
(ctx.skippedStages ??= []).push(label); // V4-1: the lint receipt states which artifacts this delivery inherited
|
|
4223
|
-
recordTransition(ctx, name); // advance the displayed step on resume too (monotonic, no
|
|
4351
|
+
recordTransition(ctx, name); // advance the displayed step on resume too (monotonic; an unmapped stage records what it is, and moves no step)
|
|
4224
4352
|
// `won ? won.key : …` (NOT `won?.key ?? …`): a recovered CODE-SIDE winner carries key:null on purpose —
|
|
4225
4353
|
// falling back to opts.sessionKey there would re-mint the phantom session the null exists to suppress.
|
|
4226
4354
|
return { ok: true, skipped: true, sessionKey: won ? won.key : opts.sessionKey, model: won?.model ?? model };
|
|
@@ -4231,6 +4359,25 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4231
4359
|
// output under the rules it is being minted under. See recordStageContract's doc block.
|
|
4232
4360
|
if (def.contract) recordStageContract(P.runDir, name, def.contract);
|
|
4233
4361
|
|
|
4362
|
+
// ── AND RECORD THAT THE RUN IS IN IT, AT THE SAME MOMENT ───────────────────────────────────────────
|
|
4363
|
+
//
|
|
4364
|
+
// The displayed step advanced only where a stage SUCCEEDED, so a run inside a stage that takes minutes
|
|
4365
|
+
// read as being in the previous one for the whole of it — a resumed run entered placement at 10:05Z and
|
|
4366
|
+
// still showed "Register sweeps" at 10:10Z, which reads as the run having gone backwards. Nothing
|
|
4367
|
+
// durable said a stage had STARTED: every stage record is written at completion, and the one start-time
|
|
4368
|
+
// signal goes to stderr and never reaches the trace. So no reader could derive it either.
|
|
4369
|
+
//
|
|
4370
|
+
// Here rather than at the dispatch below, because this is the line that already declares the stage is
|
|
4371
|
+
// dispatching, and every mutating path — corrective, followup, escalation, frame-reopen, stale-repair,
|
|
4372
|
+
// verdict-recheck — funnels through it. One declaration of entry, for the contract record and the
|
|
4373
|
+
// status alike.
|
|
4374
|
+
//
|
|
4375
|
+
// Safe against the two things that look like it should not be. `writeRunStatus` keeps the furthest step
|
|
4376
|
+
// ever reached, so an escalation re-run mapping to an earlier stage still cannot pull the stepper back;
|
|
4377
|
+
// and the bare `name` is passed exactly as the success path passes it, so a fan-out's axes go on
|
|
4378
|
+
// collapsing to one display step.
|
|
4379
|
+
recordTransition(ctx, name);
|
|
4380
|
+
|
|
4234
4381
|
// — THE PLACEMENT FORM IS ON DISK BEFORE THE SEAT IS ASKED ANYTHING, and it is written at the SAME
|
|
4235
4382
|
// choke point every dispatch funnels through: the main path, the stale-repair path and the frame-reopen
|
|
4236
4383
|
// path all reach the seat through here, and three separate pre-dispatch writes would be three chances
|
|
@@ -4342,6 +4489,25 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4342
4489
|
// Evaluated on `effThinking`, not `thinking`: the anthropic gate above nulls the tier for a
|
|
4343
4490
|
// cross-provider model, and a guard that reads the nulled value would go blind on the same overrides.
|
|
4344
4491
|
assertEffectiveTier(label, { model, thinking: effThinking });
|
|
4492
|
+
// ── THE TIME LIMIT IS DERIVED FROM THE BAND THIS DISPATCH IS HANDED ─────────────────────────────
|
|
4493
|
+
//
|
|
4494
|
+
// `def.timeoutSec` was a constant chosen once against a band nobody recorded beside it, and on a
|
|
4495
|
+
// dense matter the two band-reading stages died AT their walls having written nothing. The base is
|
|
4496
|
+
// still the stage's own number; what the band decides is how far above it this dispatch may go. A
|
|
4497
|
+
// band at or under the reference returns the base untouched, so nothing already verified moves.
|
|
4498
|
+
const dispatchBand = bandSizeForStage(name, P);
|
|
4499
|
+
const derivedLimit = derivedLimitSec(def.timeoutSec, dispatchBand);
|
|
4500
|
+
// REFUSED BEFORE A SEAT IS DISPATCHED, not after the wall. A limit past the ceiling is a prediction
|
|
4501
|
+
// that this stage will be killed; starting it spends the whole prediction to arrive where the refusal
|
|
4502
|
+
// already is, and the refusal NAMES the band size, which is the finding a killed attempt never gives.
|
|
4503
|
+
if (limitExceedsCeiling(derivedLimit.sec)) {
|
|
4504
|
+
const reason = ceilingRefusal(name, derivedLimit);
|
|
4505
|
+
runLog(P.runDir, { event: "stage-input-over-ceiling", stage: name,
|
|
4506
|
+
inputBytes: derivedLimit.inputBytes, derivedLimitSec: derivedLimit.sec });
|
|
4507
|
+
throw new StageFailure(name, reason, undefined, { failClass: "deterministic" });
|
|
4508
|
+
}
|
|
4509
|
+
runLog(P.runDir, { event: "stage-limit-derived", stage: name, base: def.timeoutSec,
|
|
4510
|
+
inputBytes: derivedLimit.inputBytes, derivedLimitSec: derivedLimit.sec, basis: derivedLimit.basis });
|
|
4345
4511
|
const r = await runStage(label, {
|
|
4346
4512
|
agent: execAgent,
|
|
4347
4513
|
message,
|
|
@@ -4349,12 +4515,13 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4349
4515
|
thinking,
|
|
4350
4516
|
// opts.sessionKey lets a followup RESUME the exact key a prior run won on (winning-key hardening); else
|
|
4351
4517
|
// the canonical base key. (A followup with a stale base key would resume a failed attempt — see runStage.)
|
|
4352
|
-
sessionKey: opts.sessionKey ?? `
|
|
4353
|
-
timeoutSec: def.timeoutSec,
|
|
4518
|
+
sessionKey: opts.sessionKey ?? `clearance-${ctx.run.slug}-${ctx.run.codename}-${name}${keyAxis}`,
|
|
4519
|
+
timeoutSec: derivedLimit.sec ?? def.timeoutSec,
|
|
4520
|
+
derivedLimit, // recorded on every attempt row: the size it was derived from, and the number
|
|
4354
4521
|
stallSec: def.stallSec, // per-stage stall override (heavy stages); undefined → global CLEAROTRON_STALL_MS
|
|
4355
4522
|
expectFile: out,
|
|
4356
4523
|
validate: def.validate,
|
|
4357
|
-
runDir: P.runDir, bandSize:
|
|
4524
|
+
runDir: P.runDir, bandSize: dispatchBand,
|
|
4358
4525
|
// ── — THE PRESENTING SIDE, RECOMPOSED PER ATTEMPT ────────────────────────────────────────
|
|
4359
4526
|
//
|
|
4360
4527
|
// A stage that declares `refreshCtx` is saying its dispatch text depends on state the run CHANGES
|
|
@@ -4437,6 +4604,8 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4437
4604
|
});
|
|
4438
4605
|
// P2 — record WHAT this output was produced from, so a later resume can tell whether it still holds.
|
|
4439
4606
|
if (r.ok && out) writeStamp(P.runDir, label, stageInputs(name, P, { axes: ctx.axes, axis: ctx.axis, registerOnly: ctx.registerOnly }), { project: projectStageInput });
|
|
4607
|
+
// Re-asserted on success, and no longer the only advance: entry records the same transition above, so
|
|
4608
|
+
// this is what re-states the stage after a fan-out's concurrent axes have each written their own.
|
|
4440
4609
|
if (r.ok) recordTransition(ctx, name); // bare name → fan-out axes collapse to one display step (monotonic)
|
|
4441
4610
|
// PR-4 — document-growth tripwire, keyed on TRIGGER so it covers BOTH growth loops (the lint-repair
|
|
4442
4611
|
// "re-emit the COMPLETE updated file" cycle AND the skeptic/refutation corrective loop — the 2026-07-28 postmortem run's
|
|
@@ -4627,7 +4796,7 @@ function must(r, name) {
|
|
|
4627
4796
|
* absence reads as an empty cause — which is the whole defect, one level along. PURE.
|
|
4628
4797
|
*
|
|
4629
4798
|
* — VISIBLE IN THE STRING, not only in a sibling key. `reason` was a bare slice, so a cut sentence
|
|
4630
|
-
* read as a finished one. Three failed runs of 2026-08-19 ended theirs mid-path — ".../studio/
|
|
4799
|
+
* read as a finished one. Three failed runs of 2026-08-19 ended theirs mid-path — ".../studio/clearance-se"
|
|
4631
4800
|
* — and the reader who stat'd that directory got ENOENT and diagnosed a working mechanism as a broken
|
|
4632
4801
|
* one. Nothing had been lost: `reasonFull` carried the whole path on all three and `reasonTruncated` was
|
|
4633
4802
|
* true on all three. The field that gets read FIRST simply never said it was short. `abbrev` is this
|
|
@@ -5247,7 +5416,7 @@ export function recordConnotationAudit(run, P) { // @internal
|
|
|
5247
5416
|
// ── — DOES A CHARGED RATING CARRY ITS GROUNDS? RECORDED, NOT ENFORCED ───────────────────────
|
|
5248
5417
|
//
|
|
5249
5418
|
// Doctrine already asks for this in as many words: "rule `loaded` and use the note to state plainly
|
|
5250
|
-
// what you could not establish and what a human should look at" (
|
|
5419
|
+
// what you could not establish and what a human should look at" (clearance-common-law/SKILL.md). The
|
|
5251
5420
|
// seat does not do it — e2e READ all 24 `loaded` notes in the corpus (not keyword-probed them) and
|
|
5252
5421
|
// every one describes the material instead. `grounds-grammar.mjs` was built to measure exactly that,
|
|
5253
5422
|
// proven against those real notes and against a planted violation, AND CALLED BY NOTHING. A check
|
|
@@ -6111,7 +6280,7 @@ function refuteRegistryCheckExtra(ctx) {
|
|
|
6111
6280
|
const P = ctx.paths;
|
|
6112
6281
|
const narrative = existsSync(P.narrative) ? readFileSync(P.narrative, "utf8") : "";
|
|
6113
6282
|
if (!narrative) return "";
|
|
6114
|
-
const recordsByUri = assembleRunRecords(P.runDir, `
|
|
6283
|
+
const recordsByUri = assembleRunRecords(P.runDir, `clearance-${ctx.run.slug}-${ctx.run.codename}-`).records;
|
|
6115
6284
|
const probs = [
|
|
6116
6285
|
...findRegistryArithmeticIssues(narrative).map((a) => `${a.block}: ${a.detail}`),
|
|
6117
6286
|
...findRegistryViolations(narrative, recordsByUri).filter((v) => v.kind === "mismatch")
|
|
@@ -6984,7 +7153,7 @@ function injectDeferralCoverage(P, runDir, note) {
|
|
|
6984
7153
|
// ── THE REMEDY CLAUSE NAMES A PRODUCT, AND FOR A WHILE IT NAMED A DELETED ONE ───────────────────────
|
|
6985
7154
|
//
|
|
6986
7155
|
// This row reaches a CLIENT REPORT. It read "…not searched at this level — available at Depth 5
|
|
6987
|
-
// (
|
|
7156
|
+
// (clearance-jx)": an internal product key and a rung on the depth ladder, both retired by, offered as
|
|
6988
7157
|
// the remedy for a coverage limit the same sentence had just disclosed. `injectScriptScopeCoverage`
|
|
6989
7158
|
// calls the decision on EVERY run, and it was driven — it fires for a worldwide scope, a CN-named scope
|
|
6990
7159
|
// and the ja lane — so this was delivered prose telling a reader to buy something that does not exist.
|
|
@@ -6998,7 +7167,7 @@ const WORLDWIDE_SCOPE_RE = /^(worldwide|global|all|all[- ]jurisdictions)$/i;
|
|
|
6998
7167
|
/** What a synthesis-authored Stage-1.5 coverage row looks like when it has ALREADY made this
|
|
6999
7168
|
* disclosure — see the suppression in injectScriptScopeCoverage. Both the current vocabulary and the
|
|
7000
7169
|
* retired one, because a resumed run carries whichever was current when its synthesis ran. */
|
|
7001
|
-
const SCRIPT_SCOPE_RECOMMENDATION_TOKENS = Object.freeze(["native-language investigation", "
|
|
7170
|
+
const SCRIPT_SCOPE_RECOMMENDATION_TOKENS = Object.freeze(["native-language investigation", "clearance-jx"]);
|
|
7002
7171
|
|
|
7003
7172
|
/** The disclosure vocabulary for ONE candidate lane, derived (never tabulated). Returns
|
|
7004
7173
|
* `{area, note, territories, marker}` — `marker` is the lowercased script prefix the suppression
|
|
@@ -7122,14 +7291,14 @@ export function injectScriptScopeCoverage(P, runDir, note, { searchPolicy = null
|
|
|
7122
7291
|
const rowText = (c) => `${c?.area ?? ""} ${c?.note ?? ""}`.toLowerCase();
|
|
7123
7292
|
const added = [];
|
|
7124
7293
|
for (const p of pending) {
|
|
7125
|
-
// Suppression is PER LANE. The old check treated any coverage row mentioning "
|
|
7294
|
+
// Suppression is PER LANE. The old check treated any coverage row mentioning "clearance-jx" as
|
|
7126
7295
|
// covering the disclosure, which was harmless while zh was the only lane and is a silent row-
|
|
7127
7296
|
// eater the moment there are two: on a CN+JP scope the zh row lands first, its own note ended in
|
|
7128
|
-
// "(
|
|
7297
|
+
// "(clearance-jx)", and the ja row was then swallowed by a match on the sibling's text. So the
|
|
7129
7298
|
// deference to a synthesis-authored Stage-1.5 row requires the row to be about THIS lane.
|
|
7130
7299
|
//
|
|
7131
7300
|
// TWO VOCABULARIES ON PURPOSE. `native-language investigation` is what the offering calls it now
|
|
7132
|
-
// and what synthesis reads in its own prompt; `
|
|
7301
|
+
// and what synthesis reads in its own prompt; `clearance-jx` is the retired key, kept because a run
|
|
7133
7302
|
// RESUMED from before carries a synthesis row written in it — dropping the old token would
|
|
7134
7303
|
// put a second, duplicate disclosure into a report that already makes the same one.
|
|
7135
7304
|
const already = doc.coverage.some((c) => {
|
|
@@ -7329,7 +7498,7 @@ export function fullProseOrdinals(findings) {
|
|
|
7329
7498
|
.filter((o) => o != null);
|
|
7330
7499
|
}
|
|
7331
7500
|
// ASSEMBLE report.md = the overview shell (front-matter + Actions/Coverage/Methodology) + `# Marks` + the
|
|
7332
|
-
// per-card files in render order (composite desc, ordinal asc — mirrors render.mjs
|
|
7501
|
+
// per-card files in render order (composite desc, ordinal asc — mirrors render.mjs `sortedAll`), `open: true` on the
|
|
7333
7502
|
// single top card. Findings with no card file (a failed report-card, or a secondary finding) are NOT emitted —
|
|
7334
7503
|
// render.mjs synthesizes them structured-only from findings.json, so nothing is silently dropped. Pure file IO.
|
|
7335
7504
|
// spec 64 — "### Only you can close these" is CODE-BUILT from the typed actions register (the report-
|
|
@@ -7540,7 +7709,7 @@ export function buildOnlyYouSection(actions, findings, { nowMs = Date.now(), wit
|
|
|
7540
7709
|
// blocking review goes." That REVERSES T3, which retired "delivered-with-open-questions" and
|
|
7541
7710
|
// is itself recorded as an owner-approved decision — both are his, and this is the standing one.
|
|
7542
7711
|
//
|
|
7543
|
-
// The section the reviewer's concerns land in is not new. `driver/skills/
|
|
7712
|
+
// The section the reviewer's concerns land in is not new. `driver/skills/clearance-search/SKILL.md`
|
|
7544
7713
|
// has described it all along — "delivered … as a prominent Reviewer's open questions section at the
|
|
7545
7714
|
// top of the body (the driver passes them in)" — and `:295` lists it as a required section with
|
|
7546
7715
|
// "Never omit it to look more finished." deleted the driver's half and left that text
|
|
@@ -7870,7 +8039,7 @@ export function assembleReportMd(P, findings, cardOrdinals, { grouped = [], byRi
|
|
|
7870
8039
|
} catch { /* never-kill: a malformed findings.json leaves the overview untouched (its own gates own that) */ }
|
|
7871
8040
|
// ── T3a — THE REVIEWER'S OPEN POINTS, AT THE TOP OF THE BODY ──────────────────────────────────────
|
|
7872
8041
|
//
|
|
7873
|
-
// `driver/skills/
|
|
8042
|
+
// `driver/skills/clearance-search/SKILL.md` says where: "a prominent Reviewer's open questions section at the TOP of the body
|
|
7874
8043
|
// (the driver passes them in)". That sentence has been true of the contract and false of the code
|
|
7875
8044
|
// since deleted the driver's half; this is the half coming back.
|
|
7876
8045
|
//
|
|
@@ -8846,9 +9015,10 @@ async function pipelineInner(job, opts = {}) {
|
|
|
8846
9015
|
// Phase 0 done in code (slug/codename/run-dir/customer). Phase 1+2 stages:
|
|
8847
9016
|
must(await stage("matter-frame", ctx), "matter-frame");
|
|
8848
9017
|
await deriveIntakeAsks(ctx); // A6: freeze the intake-ask register (one save-only followup if the section is missing)
|
|
8849
|
-
must(await stage("
|
|
9018
|
+
must(await stage("clearance-variants", ctx), "clearance-variants");
|
|
8850
9019
|
deriveScopeLedgerJson(ctx); // frame-omission design: code-derive scope-ledger.json from the validated prose (never-kill)
|
|
8851
9020
|
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)
|
|
9021
|
+
await verifyAndRecordHouseElement(ctx, opts); // ask the register who owns the frame's proposed house element, BEFORE the plan is compiled from it
|
|
8852
9022
|
attachRegisterPlan(ctx); // WS2 (B3): compile/freeze/reuse the deterministic register plan (flag-gated; frozen plan wins on resume; never-kill on mint)
|
|
8853
9023
|
|
|
8854
9024
|
// spec 64 (B2) — proactive recall probes: prior-confirmed conflicts for THIS mark (the workspace
|
|
@@ -9659,7 +9829,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9659
9829
|
const dispatchStartedMs = Date.now();
|
|
9660
9830
|
try {
|
|
9661
9831
|
const r = await planExec({ planPath: P.registerPlan, axis: a, outputPath: P.registerBand(a), ...(qids?.length ? { qids } : {}) },
|
|
9662
|
-
{ agentId: agent, sessionKey: `
|
|
9832
|
+
{ agentId: agent, sessionKey: `clearance-${run.slug}-${run.codename}-register-unit-${a}`, recordLog: runRecordLogPath(run.runDir) });
|
|
9663
9833
|
outcome = r?.ok ? "ok" : `failed: ${String(r?.cause ?? "unknown").slice(0, 140)}`;
|
|
9664
9834
|
} catch (err) { outcome = `threw: ${String(err?.message ?? err).slice(0, 140)}`; }
|
|
9665
9835
|
// — MEASURE WHETHER THE HOLE CLOSED, not whether the phone was answered. `outcome` is the
|
|
@@ -9746,7 +9916,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9746
9916
|
// production default is ON.
|
|
9747
9917
|
if (process.env.CLEAROTRON_BAND_TRUTH_GATE !== "0"
|
|
9748
9918
|
&& unitKey[a] && existsSync(P.registerBand(a)) && (ctx.registerPlan?.entries ?? []).some((e) => e?.axis === a)) {
|
|
9749
|
-
const laneKey = `
|
|
9919
|
+
const laneKey = `clearance-${run.slug}-${run.codename}-register-unit-${a}`;
|
|
9750
9920
|
const bandHasQids = (() => {
|
|
9751
9921
|
try { const b = JSON.parse(readFileSync(P.registerBand(a), "utf8")); return Array.isArray(b) && b.some((x) => x && typeof x === "object" && x.qid); }
|
|
9752
9922
|
catch { return false; /* parse defects are the stage validator's problem, not this gate's */ }
|
|
@@ -9819,7 +9989,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9819
9989
|
if (!digestLocked && repairLedger.canAttempt("taint-rerun", a, { max: taintRerunMax, epoch: repairEpoch })) {
|
|
9820
9990
|
note(`register-unit ${a}: taint-rerun — one fresh re-run (fresh session; a killed session is never warm-resumed)`);
|
|
9821
9991
|
const r = await stage("register-unit", { ...ctx, axis: a },
|
|
9822
|
-
{ force: true, trigger: "taint-rerun", sessionKey: `
|
|
9992
|
+
{ force: true, trigger: "taint-rerun", sessionKey: `clearance-${run.slug}-${run.codename}-register-unit-${a}-taint-rerun-${recoveryAttempts}` });
|
|
9823
9993
|
repairLedger.record("taint-rerun", a, r.ok ? "ok" : `failed: ${String(r.fail ?? "unknown").slice(0, 100)}`, { epoch: repairEpoch, max: taintRerunMax });
|
|
9824
9994
|
repairs.push(`taint-rerun:${r.ok ? "ok" : `failed: ${String(r.fail ?? "unknown").slice(0, 60)}`}`);
|
|
9825
9995
|
if (r.ok && r.sessionKey) unitKey[a] = r.sessionKey;
|
|
@@ -10144,7 +10314,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
10144
10314
|
runLog(run.runDir, { event: "plan-qids-missing", axis: a, qids: entries.map((e) => e.qid), action: "fresh-execute-plan" });
|
|
10145
10315
|
note(`register-unit ${a}: ${entries.length} dictated plan entr${entries.length === 1 ? "y" : "ies"} unexecuted on a resumed-past axis — one fresh execute_plan-only call`);
|
|
10146
10316
|
const freshMsg = repairFollowup("register-unit:plan-join-fresh", { axis: a, registerPlan: P.registerPlan, bandPath: P.registerBand(a) });
|
|
10147
|
-
const rf = await stage("register-unit", { ...ctx, axis: a }, { force: true, freshMessage: freshMsg, sessionKey: `
|
|
10317
|
+
const rf = await stage("register-unit", { ...ctx, axis: a }, { force: true, freshMessage: freshMsg, sessionKey: `clearance-${ctx.run.slug}-${ctx.run.codename}-register-unit-${a}-plan-join-fresh`, trigger: "plan-join-fresh" });
|
|
10148
10318
|
if (!rf.ok) note(`register-unit ${a}: fresh execute_plan call failed (${rf.fail}) — the plan-unexecuted StageFailure below holds the line`);
|
|
10149
10319
|
continue;
|
|
10150
10320
|
}
|
|
@@ -10969,7 +11139,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
10969
11139
|
note(`frame-reopen: ${a} timed out — ONE fresh SCOPED retry through the fallback chain (backup provider + retries)`);
|
|
10970
11140
|
const retryMsg = repairFollowup("register-unit:frame-reopen-retry", { paths: P, axis: a, directives: regDirectives, reopenFetchCap,
|
|
10971
11141
|
supplementalLane: !!ctx.registerPlan?.contract?.supplemental_lane });
|
|
10972
|
-
const r2 = await stage("register-unit", { ...ctx, axis: a }, { force: true, freshMessage: retryMsg, sessionKey: `
|
|
11142
|
+
const r2 = await stage("register-unit", { ...ctx, axis: a }, { force: true, freshMessage: retryMsg, sessionKey: `clearance-${ctx.run.slug}-${ctx.run.codename}-register-unit-${a}-reopen-retry`, trigger: "frame-reopen-retry" });
|
|
10973
11143
|
if (r2.ok && existsSync(P.registerUnit(a)) && readFileSync(P.registerUnit(a), "utf8") !== beforeUnit) {
|
|
10974
11144
|
changedAxes.push(a); regChanged = true; regMechFail = null;
|
|
10975
11145
|
note(`frame-reopen: ${a} fresh scoped retry closed the gap`);
|
|
@@ -11740,7 +11910,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
11740
11910
|
// violating after that one recovery pass is disclosed as unexamined + clamps the verdict (owner
|
|
11741
11911
|
// decision 2026-07-22 — the terminal branch below); the gate never silently passes a drop.
|
|
11742
11912
|
{
|
|
11743
|
-
const runPrefix = `
|
|
11913
|
+
const runPrefix = `clearance-${run.slug}-${run.codename}-`;
|
|
11744
11914
|
// Re-read the findings + recompute the fetched universe on EACH check (the recovery re-digest rewrites
|
|
11745
11915
|
// the findings AND may record_fetch the flagged URIs — both must be picked up on the post-recovery
|
|
11746
11916
|
// check). V4-1: the fetched universe is the DELIVERED ARTIFACT SET's records (run-dir _records/ ∪ this
|
|
@@ -12062,7 +12232,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12062
12232
|
regions: ctx.registerPlan?.regions ?? [], entries,
|
|
12063
12233
|
}, null, 2) + "\n");
|
|
12064
12234
|
const r = await planExec({ planPath: ccPlanPath, axis: CROWD_CONTEXT_AXIS, outputPath: ccBandPath },
|
|
12065
|
-
{ agentId: ctx.agent, sessionKey: `
|
|
12235
|
+
{ agentId: ctx.agent, sessionKey: `clearance-${run.slug}-${run.codename}-crowd-context`, recordLog: runRecordLogPath(run.runDir) });
|
|
12066
12236
|
if (!r?.ok) throw new Error(String(r?.cause ?? "crowd-context plan execution failed").slice(0, 200));
|
|
12067
12237
|
const blocks = JSON.parse(readFileSync(ccBandPath, "utf8"));
|
|
12068
12238
|
return Array.isArray(blocks) ? blocks : [];
|
|
@@ -12102,7 +12272,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12102
12272
|
// on fetched records) as a structured aim-attention artifact for synthesis. Best-effort; NEVER a
|
|
12103
12273
|
// rating rule (explicitly not the rejected escalation filter) — absence writes nothing.
|
|
12104
12274
|
try {
|
|
12105
|
-
const { records } = assembleRunRecords(run.runDir, `
|
|
12275
|
+
const { records } = assembleRunRecords(run.runDir, `clearance-${run.slug}-${run.codename}-`);
|
|
12106
12276
|
const signals = extractEnforcerSignals(records);
|
|
12107
12277
|
if (signals.length) {
|
|
12108
12278
|
writeFileSync(`${P.enforcerSignals}.tmp`, JSON.stringify({ signals }, null, 2) + "\n");
|
|
@@ -12644,7 +12814,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12644
12814
|
status: r?.screen?.status ?? r?.status, live_status: r?.screen?.live_status });
|
|
12645
12815
|
}
|
|
12646
12816
|
} catch { /* no merged band (legacy run) — fetched-record facts alone rank */ }
|
|
12647
|
-
const srPrefix = `
|
|
12817
|
+
const srPrefix = `clearance-${run.slug}-${run.codename}-`;
|
|
12648
12818
|
let srRecords = assembleRunRecords(run.runDir, srPrefix).records;
|
|
12649
12819
|
const srFetcher = opts.recordFetcher ?? defaultRecordFetcher;
|
|
12650
12820
|
const srLedger = ctx.repairLedger ?? (ctx.repairLedger = createRepairLedger(run.runDir, { log: (o) => runLog(run.runDir, o) }));
|
|
@@ -12718,7 +12888,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12718
12888
|
const carriedUris = (pf?.findings ?? []).flatMap((f) =>
|
|
12719
12889
|
Array.isArray(f?.owner?.registrations) ? f.owner.registrations : []
|
|
12720
12890
|
).map((r) => r?.uri && (normalizeRecordUri(r.uri) || String(r.uri).trim())).filter(Boolean);
|
|
12721
|
-
const fetchedUris = [...assembleRunRecords(run.runDir, `
|
|
12891
|
+
const fetchedUris = [...assembleRunRecords(run.runDir, `clearance-${run.slug}-${run.codename}-`).records.keys()];
|
|
12722
12892
|
const rfMd = existsSync(P.registerFindings) ? readFileSync(P.registerFindings, "utf8") : "";
|
|
12723
12893
|
const violations = findRecallRegressionViolations({
|
|
12724
12894
|
knownConflicts, searchedNames: namesForRecall, carriedUris, fetchedUris,
|
|
@@ -12872,7 +13042,11 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12872
13042
|
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.`,
|
|
12873
13043
|
// — the READER's sentence: what is open, in a lawyer's nouns. Counts survive;
|
|
12874
13044
|
// the token, the record ids and the engine's nouns stay in `reason` and the run record.
|
|
12875
|
-
|
|
13045
|
+
// COMPOSED BY THE AUTHORITY, NOT SPELLED HERE. The same sentence has to be reachable from a
|
|
13046
|
+
// run that stored no clause, where these two objects are long gone and only the reason's own
|
|
13047
|
+
// counts survive; `terminal-clamp.mjs` composes it from the counts either way, so the fresh
|
|
13048
|
+
// run and the republished archive cannot drift apart.
|
|
13049
|
+
clause: clauseForDefect("synthesis_unaccounted_delivered", duty.unaccounted.length, duty.totals.owed),
|
|
12876
13050
|
});
|
|
12877
13051
|
}
|
|
12878
13052
|
}
|
|
@@ -12898,7 +13072,9 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12898
13072
|
// — the READER's sentence. "Floor row" is an engine noun; what the fact IS for a
|
|
12899
13073
|
// lawyer: live registrations identical or near-identical to the mark that the report does not
|
|
12900
13074
|
// individually address. Counts survive; token, ids and engine nouns stay in `reason`.
|
|
12901
|
-
|
|
13075
|
+
// Composed by the authority in `terminal-clamp.mjs` for the reason the sibling site above
|
|
13076
|
+
// gives: a republished pre-split run has to reach the same sentence from the counts alone.
|
|
13077
|
+
clause: clauseForDefect("floor_duty_undischarged", block.undischarged, block.floors),
|
|
12902
13078
|
});
|
|
12903
13079
|
}
|
|
12904
13080
|
}
|
|
@@ -12940,7 +13116,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12940
13116
|
for (const c of fresh) clampClauses.push(conditionClauses[conditions.indexOf(c)] ?? c);
|
|
12941
13117
|
clampReasons.push(...fresh);
|
|
12942
13118
|
if (verdict === "CLEAR") {
|
|
12943
|
-
runLog(run.runDir, { event: "coverage-floor-clamp", from: "CLEAR", to: "CONDITIONAL", legalActions: conditions.length });
|
|
13119
|
+
runLog(run.runDir, { event: "coverage-floor-clamp", cause: "forward-actions", from: "CLEAR", to: "CONDITIONAL", legalActions: conditions.length });
|
|
12944
13120
|
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).`);
|
|
12945
13121
|
verdict = "CONDITIONAL";
|
|
12946
13122
|
writeRunStatus(ctx, { verdict });
|
|
@@ -12979,7 +13155,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12979
13155
|
// The CLAMP is outside that guard on purpose: a re-ask can flip a clamped verdict back to CLEAR,
|
|
12980
13156
|
// and it must meet this floor again with the reason already recorded.
|
|
12981
13157
|
if (droppedConditions.length && verdict === "CLEAR") {
|
|
12982
|
-
runLog(run.runDir, { event: "coverage-floor-clamp", from: "CLEAR", to: "CONDITIONAL", actionsDropped: droppedConditions.length });
|
|
13158
|
+
runLog(run.runDir, { event: "coverage-floor-clamp", cause: "dropped-actions", from: "CLEAR", to: "CONDITIONAL", actionsDropped: droppedConditions.length });
|
|
12983
13159
|
verdict = "CONDITIONAL";
|
|
12984
13160
|
writeRunStatus(ctx, { verdict });
|
|
12985
13161
|
}
|
|
@@ -13070,28 +13246,55 @@ async function pipelineInner(job, opts = {}) {
|
|
|
13070
13246
|
// could-not-examine record, and an unfinished register slice — CONDITIONAL carries
|
|
13071
13247
|
// lawyer-judged/disclosed residue only.
|
|
13072
13248
|
if (coverageInsufficient || frameResidual || screenGateGap || seniorGap || registerGap || deadlineGap) {
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13249
|
+
// THESE SENTENCES REACH A CLIENT AND THEY ARE NOT OURS TO WRITE (owner, 2026-09-17). One of
|
|
13250
|
+
// them — the screen-gate line, which says a mark "could not be record_fetched" — carried an
|
|
13251
|
+
// engine identifier into the list a client reads as the conditions on their result, by a route
|
|
13252
|
+
// `terminalClampDecision` guards and this one did not. A reader's sentence for it was written
|
|
13253
|
+
// here and refused: the objection was the class, not the wording. RULED 2026-09-18, design item
|
|
13254
|
+
// 25 — cut the clause: it stays in the run record and leaves the client's result entirely, with
|
|
13255
|
+
// NOTHING written in its place. That is what the `null` clause below means.
|
|
13256
|
+
const reasons = [], clauseOf = new Map(); // run-record reason → the client's clause: the same words, with any engine token taken out
|
|
13257
|
+
const machinery = (reason, clause = reason) => { reasons.push(reason); clauseOf.set(reason, clause); };
|
|
13258
|
+
if (coverageInsufficient) machinery(`the lawyer judged a material slice not fully cleared: ${coverageJudgment.reason || "register coverage gap"}`);
|
|
13259
|
+
if (frameGap) machinery("the blind frame-diff flagged a dominant-element omission the reopen pass did not close");
|
|
13260
|
+
else if (frameDeferrals.length) machinery(`follow-ups left open this run: ${frameDeferrals.map((d) => plainDirective(d.directive)).slice(0, 3).join(", ")}`);
|
|
13261
|
+
if (screenGateGap) machinery(`${sgUnresolved.length} in-scope mark(s) dropped on goods could not be record_fetched (unverified): ${sgUnresolved.map((g) => g.mark).join(", ")}`, null); // run record ONLY: no client clause (ruled 2026-09-18)
|
|
13262
|
+
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(", ")}`);
|
|
13079
13263
|
if (registerGap) {
|
|
13080
|
-
|
|
13081
|
-
|
|
13264
|
+
for (const { reason, clause } of registerGapConditions(regGap)) machinery(reason, clause);
|
|
13265
|
+
// (the deferred and cut-down register lines: registerGapConditions, at the end of this file)
|
|
13082
13266
|
// Named regressions (2026-07-22): `<MARK> (<owner> — <canonical uri>)` — a bare mark name
|
|
13083
13267
|
// shipped "ION, ION, ION" (three indistinguishable strings); the identity is front-loaded
|
|
13084
13268
|
// because the delivered statement truncates from the tail.
|
|
13085
|
-
if (regGap.recallRegressions.length)
|
|
13269
|
+
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(", ")}`);
|
|
13086
13270
|
}
|
|
13087
|
-
if (deadlineGap)
|
|
13088
|
-
|
|
13271
|
+
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(", ")}`);
|
|
13272
|
+
// ── THE THIRD CLAMP SITE NAMES ITSELF, AND NAMES WHICH OF ITS SEVEN INPUTS FIRED ────────────────
|
|
13273
|
+
//
|
|
13274
|
+
// All three clamp sites emitted this event under one name with the same from/to, distinguishable
|
|
13275
|
+
// only by which optional payload key happened to be present. Two of them fired three milliseconds
|
|
13276
|
+
// apart on a production run — the first without `frameDeferred`, the second with it — which reads
|
|
13277
|
+
// as one decision logged twice. It was two different decisions wearing one name, and the test lane
|
|
13278
|
+
// reasonably discounted one of them. `cause` makes the event self-describing.
|
|
13279
|
+
//
|
|
13280
|
+
// THE EVENT NAME IS DELIBERATELY NOT SPLIT. It is a true statement about the effect — the verdict
|
|
13281
|
+
// was clamped — and something downstream may already count clamps in aggregate. A discriminator is
|
|
13282
|
+
// additive; three names would not be.
|
|
13283
|
+
//
|
|
13284
|
+
// This site is itself seven causes under one name, so it also lists WHICH fired rather than
|
|
13285
|
+
// leaving a reader to key on field presence and guess.
|
|
13286
|
+
const clampInputs = Object.entries({
|
|
13287
|
+
coverageInsufficient, frameGap, frameDeferred: frameDeferrals.length,
|
|
13288
|
+
screenGate: screenGateGap ? sgUnresolved.length : 0, seniorRight: seniorGap,
|
|
13289
|
+
registerGap, deadlineCarry: deadlineGap ? ctx.deadlineCarryMaterial.length : 0,
|
|
13290
|
+
}).filter(([, v]) => Boolean(v)).map(([k]) => k);
|
|
13291
|
+
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 });
|
|
13089
13292
|
note(`deliver-conditional floor: ${reasons.join("; ")} — clamping CLEAR→CONDITIONAL so the delivered status carries the gap (never withheld, never halted).`);
|
|
13090
13293
|
verdict = "CONDITIONAL";
|
|
13091
13294
|
// APPEND (dedup by exact text) — the legalActions arm may already have recorded conditions,
|
|
13092
13295
|
// and this callable runs more than once (degenerate re-ask, lint repair, post-consolidation).
|
|
13093
13296
|
const freshMachinery = reasons.filter((r) => !clampReasons.includes(r));
|
|
13094
|
-
clampClauses.push(...freshMachinery); //
|
|
13297
|
+
clampClauses.push(...freshMachinery.map((r) => (clauseOf.has(r) ? clauseOf.get(r) : r))); // the clause is the reason less any engine token; null = the run record's alone
|
|
13095
13298
|
clampReasons.push(...freshMachinery);
|
|
13096
13299
|
// The reason KINDS distinguish coverage/frame/screen-gate/senior-right/register residue for the
|
|
13097
13300
|
// report bound line and the client conditions row (merged — legalActions survives).
|
|
@@ -13209,7 +13412,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
13209
13412
|
// review goes." This REVERSES T3 (H3/H5), whose flip to fail-on-BLOCKING is itself recorded
|
|
13210
13413
|
// itself an owner-approved decision. Both are his; this is the standing one, and the
|
|
13211
13414
|
// history is kept here rather than deleted because a reader who finds only one of them will conclude
|
|
13212
|
-
// the code drifted from its contract. (It did not; driver/skills/
|
|
13415
|
+
// the code drifted from its contract. (It did not; driver/skills/clearance-search/SKILL.md was the stale half, and this
|
|
13213
13416
|
// change makes it true again.)
|
|
13214
13417
|
//
|
|
13215
13418
|
// COPPER-SPIRE IS STILL ANSWERED, AND NOT BY REFUSING. That failure was a BLOCKING verdict reaching
|
|
@@ -14104,7 +14307,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
14104
14307
|
// the spec's "blocks delivery", approved 2026-06-10: delivering nothing is worse than delivering good
|
|
14105
14308
|
// work with one clearly-labelled defect — and code-produced defects would deadlock a block.)
|
|
14106
14309
|
{
|
|
14107
|
-
const lintPrefix = `
|
|
14310
|
+
const lintPrefix = `clearance-${run.slug}-${run.codename}-`;
|
|
14108
14311
|
// V4-1: the gates evaluate the artifact set assembled for THIS delivery — run-dir _records/
|
|
14109
14312
|
// (inherited or prior-session) ∪ this-session ledger rows — never the session-prefixed ledger
|
|
14110
14313
|
// alone (empty on a fork ⇒ the vacuous registry pass that shipped a false citation).
|
|
@@ -14709,7 +14912,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
14709
14912
|
Array.isArray(f?.owner?.registrations) ? f.owner.registrations : []
|
|
14710
14913
|
).map((r) => r?.uri && (normalizeRecordUri(r.uri) || String(r.uri).trim())).filter(Boolean);
|
|
14711
14914
|
const executedQids = (ctx.planExecution?.executed ?? []).map((x) => x.qid);
|
|
14712
|
-
const fetchedUrisForRecall = [...assembleRunRecords(run.runDir, `
|
|
14915
|
+
const fetchedUrisForRecall = [...assembleRunRecords(run.runDir, `clearance-${run.slug}-${run.codename}-`).records.keys()];
|
|
14713
14916
|
const regLedgerRows = loadCoverageLedger(run.runDir).rows;
|
|
14714
14917
|
// judgment-relocation (2026-06-23): the search-shape gate (findFloorShapeGaps) was DELETED here too —
|
|
14715
14918
|
// sufficiency is judgment's call (coverage_judgment), not a re-parse of the ledger at the surface. This
|
|
@@ -14998,7 +15201,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
14998
15201
|
let providerTally = null;
|
|
14999
15202
|
try {
|
|
15000
15203
|
const provider = activeProvider().id;
|
|
15001
|
-
const usage = tallyRegisterCalls(DEFAULT_LEDGER_PATH, `
|
|
15204
|
+
const usage = tallyRegisterCalls(DEFAULT_LEDGER_PATH, `clearance-${run.slug}-${run.codename}-`);
|
|
15002
15205
|
runLog(run.runDir, { event: "provider-usage", provider, ...usage });
|
|
15003
15206
|
writeRunStatus(ctx, { providerUsage: { [provider]: usage } });
|
|
15004
15207
|
providerTally = { [provider]: usage };
|
|
@@ -15637,6 +15840,46 @@ function resolveRun(job, opts) {
|
|
|
15637
15840
|
|
|
15638
15841
|
// Rebuild the ctx a single stage needs WITHOUT re-running upstream: axes from the persisted manifest, verdict
|
|
15639
15842
|
// from status.json, publishedUrl from .published. (Mirrors what pipeline() accumulates mid-run.)
|
|
15843
|
+
/**
|
|
15844
|
+
* THE JOB A RUN RAN, READ BACK OUT OF THE RUN'S OWN STATUS. PURE.
|
|
15845
|
+
*
|
|
15846
|
+
* `--resume` needs a job file, and the job file is gone by the time a resume is needed: the queue entry
|
|
15847
|
+
* that carried it is consumed when the run starts, and nothing copies it into the run directory. So the
|
|
15848
|
+
* one command that repairs a failed run names a path the box does not have, and the person who finds out
|
|
15849
|
+
* is the person trying to repair it.
|
|
15850
|
+
*
|
|
15851
|
+
* This is not a guess at the job. `seedRunStatus` writes these fields FROM the job the engine ran, so
|
|
15852
|
+
* reading them back is the engine's own record, not a reconstruction of intent. What it cannot carry is
|
|
15853
|
+
* scope, and it does not have to: `reconstructCtx` reads profile and framework from the run's frozen
|
|
15854
|
+
* sidecars with `write: false`, so the run's own configuration governs and the job supplies identity.
|
|
15855
|
+
*
|
|
15856
|
+
* THE CALLER MUST CHECK THE SLUG, and `resumeJobRefusal` below is that check. A rebuilt job that derives
|
|
15857
|
+
* the run's own slug has reproduced the run's identity — `deriveSlug` reads `ref` and `markName` and
|
|
15858
|
+
* nothing else — and one that derives a different slug is a job for a different matter.
|
|
15859
|
+
*/
|
|
15860
|
+
export function jobFromStatus(status) { // @internal
|
|
15861
|
+
const s = status ?? {};
|
|
15862
|
+
if (!s.id && !s.ref && !s.markName) return null; // not a status this can rebuild from
|
|
15863
|
+
return {
|
|
15864
|
+
id: s.id ?? null, ref: s.ref ?? null, markName: s.markName ?? null,
|
|
15865
|
+
classes: Array.isArray(s.classes) ? s.classes : null,
|
|
15866
|
+
forwarder: s.forwarder ?? null,
|
|
15867
|
+
};
|
|
15868
|
+
}
|
|
15869
|
+
|
|
15870
|
+
/** Why a rebuilt job may not stand in for the run's own, or null when it may. PURE. */
|
|
15871
|
+
export function resumeJobRefusal(job, slug) { // @internal
|
|
15872
|
+
if (!job) return "this run's status.json carries no id, reference or mark name, so the job it ran cannot be read back from it";
|
|
15873
|
+
// EVERY IDENTIFYING FIELD, OR NO REBUILD: a job missing one is a guess at that field, and a resume must not guess.
|
|
15874
|
+
const missing = [["id", job.id], ["markName", job.markName], ["forwarder", job.forwarder]].filter(([, v]) => !v).map(([k]) => k).concat(Array.isArray(job.classes) ? [] : ["classes"]);
|
|
15875
|
+
if (missing.length) return `this run's status.json carries no ${missing.join(", ")}, so the job it ran cannot be read back whole — pass --job <file.json> rather than resume on a guess`;
|
|
15876
|
+
const derived = deriveSlug(job);
|
|
15877
|
+
if (derived !== slug)
|
|
15878
|
+
return `the job rebuilt from this run's status.json derives ${derived}, and the run directory is ${slug} — `
|
|
15879
|
+
+ "they are different matters, so the rebuild is refused rather than resumed into the wrong run";
|
|
15880
|
+
return null;
|
|
15881
|
+
}
|
|
15882
|
+
|
|
15640
15883
|
export function reconstructCtx(job, opts) { // @internal
|
|
15641
15884
|
const run = resolveRun(job, opts);
|
|
15642
15885
|
const P = paths(run.runDir);
|
|
@@ -15819,7 +16062,7 @@ export async function repairStale(job, opts = {}) { // @internal
|
|
|
15819
16062
|
|
|
15820
16063
|
// --experiment <stage>: re-run ONE stage SANDBOXED — no canonical ARTIFACT is read back or overwritten. The
|
|
15821
16064
|
// stage reads COPIES of its canonical inputs from a shadow dir and writes its output there; a distinct
|
|
15822
|
-
// `
|
|
16065
|
+
// `clearance-exp-…` session key never collides with the canonical session AND sorts outside the run's
|
|
15823
16066
|
// provider-usage prefix (experiment Corsearch calls are not billed to the canonical run). Single-model by
|
|
15824
16067
|
// design (the canonical primary unless --model overrides) so the comparison is clean.
|
|
15825
16068
|
//
|
|
@@ -16016,7 +16259,7 @@ async function runExperimentInner(job, opts) {
|
|
|
16016
16259
|
// so a run with no case-law layer must arrive as null here too, never as an absent key.
|
|
16017
16260
|
for (const f of inlineDecl.fields) shadowCtx[f] = resolved[f] ?? null;
|
|
16018
16261
|
}
|
|
16019
|
-
const sessionKey = `
|
|
16262
|
+
const sessionKey = `clearance-exp-${ctx.run.slug}-${ctx.run.codename}-${name}${axis ? `-${axis}` : ""}-${ts}`;
|
|
16020
16263
|
let { text: extra, ids: extraIds } = experimentExtra(shadowCtx, name, opts);
|
|
16021
16264
|
|
|
16022
16265
|
// — THE CORRECTIVE PASS, on a COLD turn, and it says which pass it reproduced.
|
|
@@ -16278,8 +16521,13 @@ export function retiredEnvWarnings(env = process.env) {
|
|
|
16278
16521
|
// ABSOLUTE PATHS on purpose: this line is read hours later, possibly from a different directory, and a
|
|
16279
16522
|
// relative path that silently resolves somewhere else would be a worse answer than no line at all.
|
|
16280
16523
|
export function resumeCommand({ script, jobPath, codename, agent = null }) { // @internal
|
|
16281
|
-
|
|
16282
|
-
|
|
16524
|
+
// THE IDENTITY IS THE CODENAME, NOT THE JOB PATH, and requiring both is what made this print nothing
|
|
16525
|
+
// on the failures that most needed it. A resume rebuilds the job from the run's own status when no
|
|
16526
|
+
// `--job` is given, so a command without one is valid — and it is the only valid one by the time a
|
|
16527
|
+
// failure has made a resume necessary, because the queue entry carrying the job is long consumed.
|
|
16528
|
+
// Requiring a path here meant every such failure was told "nothing to resume", which was false.
|
|
16529
|
+
if (!script || !codename) return null;
|
|
16530
|
+
return `node ${script}${jobPath ? ` --job ${jobPath}` : ""}${agent ? ` --agent ${agent}` : ""} --resume ${codename}`;
|
|
16283
16531
|
}
|
|
16284
16532
|
|
|
16285
16533
|
// The stderr lines a non-clean exit owes its reader, as an array (empty = say nothing further).
|
|
@@ -16378,7 +16626,7 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16378
16626
|
let a;
|
|
16379
16627
|
try { a = parseArgv(process.argv.slice(2)); }
|
|
16380
16628
|
catch (e) { console.error(`error: ${e.message}`); console.error(USAGE); process.exit(2); }
|
|
16381
|
-
if (!a.job) { console.error(USAGE); process.exit(2); }
|
|
16629
|
+
if (!a.job && !a.codename) { console.error(USAGE); process.exit(2); } // --resume alone rebuilds the job below
|
|
16382
16630
|
// composition guards
|
|
16383
16631
|
if (a.fromStage && !a.codename) { console.error("error: --from requires --resume <codename>"); process.exit(2); }
|
|
16384
16632
|
if (a.experiment && !a.codename) { console.error("error: --experiment requires --resume <codename>"); process.exit(2); }
|
|
@@ -16400,8 +16648,47 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16400
16648
|
// mistake a first-time reader is likeliest to make. Kept guarded here through the rebase — the
|
|
16401
16649
|
// pool check above is a separate refusal and must not swallow this one.
|
|
16402
16650
|
let job;
|
|
16403
|
-
|
|
16404
|
-
|
|
16651
|
+
if (!a.job && a.codename) {
|
|
16652
|
+
// ── RESUMING A RUN WHOSE JOB FILE THE BOX NO LONGER HAS ───────────────────────────────────────
|
|
16653
|
+
//
|
|
16654
|
+
// The queue entry carrying the job is consumed when the run starts, so by the time a failure makes
|
|
16655
|
+
// a resume necessary the input needed to perform it is already gone — and the exit advice printed
|
|
16656
|
+
// on that failure composes a command naming a path that is not there. Rebuilt from the run's own
|
|
16657
|
+
// status.json, which the engine wrote FROM the job it ran, and refused unless the rebuild derives
|
|
16658
|
+
// this run's own slug.
|
|
16659
|
+
//
|
|
16660
|
+
// The scan is what makes it possible at all: finding a run by codename needs its slug, and the
|
|
16661
|
+
// slug comes from the job, which is the thing being rebuilt. So the studio root's matters are
|
|
16662
|
+
// scanned for the one holding a run dir with this codename. `findRunDirFor` decides what counts as
|
|
16663
|
+
// that leaf, imported rather than re-spelled here — a second private copy of the leaf rule is how
|
|
16664
|
+
// a reclaim and a resume come to disagree about which directory is the run. The import is lazy
|
|
16665
|
+
// because runner.mjs imports THIS module; at this point both are loaded and nothing cycles.
|
|
16666
|
+
const { findRunDirFor } = await import("./runner.mjs");
|
|
16667
|
+
const studioRoot = a.agent ? config.studioRootForAgent(a.agent) : config.studioRoot;
|
|
16668
|
+
const archiveRoot = a.agent ? config.archiveRootForAgent(a.agent) : config.archiveRoot;
|
|
16669
|
+
let found = null;
|
|
16670
|
+
try {
|
|
16671
|
+
for (const slug of readdirSync(studioRoot)) {
|
|
16672
|
+
const hit = findRunDirFor({ codename: a.codename, slug, studioRoot, archiveRoot });
|
|
16673
|
+
if (hit) { found = { ...hit, slug }; break; }
|
|
16674
|
+
}
|
|
16675
|
+
} catch (e) { console.error(`error: cannot read the studio root ${studioRoot} — ${e.message}`); process.exit(2); }
|
|
16676
|
+
if (!found) {
|
|
16677
|
+
console.error(`error: no run directory for --resume ${a.codename} under ${studioRoot}`);
|
|
16678
|
+
console.error(" pass --job <file.json> if the run lives somewhere this cannot see.");
|
|
16679
|
+
process.exit(2);
|
|
16680
|
+
}
|
|
16681
|
+
let status = null;
|
|
16682
|
+
try { status = JSON.parse(readFileSync(join(found.dir, "status.json"), "utf8")); }
|
|
16683
|
+
catch (e) { console.error(`error: cannot read ${join(found.dir, "status.json")} — ${e.message}`); process.exit(2); }
|
|
16684
|
+
job = jobFromStatus(status);
|
|
16685
|
+
const refusal = resumeJobRefusal(job, found.slug);
|
|
16686
|
+
if (refusal) { console.error(`error: ${refusal}`); process.exit(2); }
|
|
16687
|
+
console.error(`[pipeline] --job not given; rebuilt the job for ${a.codename} from ${join(found.dir, "status.json")} (slug ${found.slug} confirmed)`);
|
|
16688
|
+
} else {
|
|
16689
|
+
try { job = JSON.parse(readFileSync(a.job, "utf8")); }
|
|
16690
|
+
catch (e) { console.error(`error: cannot read job file ${a.job} — ${e.message}`); process.exit(2); }
|
|
16691
|
+
}
|
|
16405
16692
|
// Manual runs: if --agent is given, root the run-dir in that agent's workspace too (mirrors the runner).
|
|
16406
16693
|
const base = a.agent
|
|
16407
16694
|
? { agent: a.agent, studioRoot: config.studioRootForAgent(a.agent), archiveRoot: config.archiveRootForAgent(a.agent) }
|
|
@@ -16413,7 +16700,10 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16413
16700
|
? { ...base, codename: a.codename, experiment: a.experiment, model: a.model, instructions: a.instructions, axis: a.axis, label: a.label, dispatchTrigger: a.dispatchTrigger }
|
|
16414
16701
|
: { ...base, codename: a.codename, fromStage: a.fromStage };
|
|
16415
16702
|
const script = resolve(fileURLToPath(import.meta.url));
|
|
16416
|
-
|
|
16703
|
+
// `jobPath` is null on a rebuilt resume, so the advice composes a command that does not name a file
|
|
16704
|
+
// the box does not have — which is the whole defect this path exists to close, and printing it again
|
|
16705
|
+
// in the exit advice would reintroduce it one layer along.
|
|
16706
|
+
const advise = (o) => { for (const line of resumeAdvice({ script, jobPath: a.job ? resolve(a.job) : null, agent: a.agent ?? null, experiment: Boolean(a.experiment), ...o })) console.error(line); };
|
|
16417
16707
|
|
|
16418
16708
|
// ── the laptop case: a closed lid, a shutdown, a ^C ────────────────────────────────────────────────
|
|
16419
16709
|
// Until now this CLI had NO signal handler: node's default die-now killed the process mid-stage, the run
|
|
@@ -16516,3 +16806,28 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16516
16806
|
//
|
|
16517
16807
|
// Move it to the top the day those citations name symbols instead of numbers — which is what
|
|
16518
16808
|
// CONTRIBUTING.md asks for, and what makes them checkable at all.
|
|
16809
|
+
|
|
16810
|
+
/**
|
|
16811
|
+
* The verdict conditions for an unfinished register search: the run record's sentence, and the client's.
|
|
16812
|
+
*
|
|
16813
|
+
* THE CLIENT'S CLAUSE IS THE SAME SENTENCE WITH THE ENGINE TOKEN TAKEN OUT (ruled 2026-09-18). The axis
|
|
16814
|
+
* names — `primary-sweep`, `incumbent-class` — are how the engine files a register slice, and they reached
|
|
16815
|
+
* the verdict a client reads as "register coverage deferred on primary-sweep". Nothing is written in their
|
|
16816
|
+
* place: the client reads the remaining words, and the run record keeps the axes for whoever repairs the run.
|
|
16817
|
+
* PURE.
|
|
16818
|
+
*
|
|
16819
|
+
* @returns {{ reason: string, clause: string }[]}
|
|
16820
|
+
*/
|
|
16821
|
+
export function registerGapConditions(regGap) { // @internal
|
|
16822
|
+
const out = [];
|
|
16823
|
+
const deferredAxes = [...new Set((regGap?.deferred ?? []).map((g) => g?.axis).filter(Boolean))];
|
|
16824
|
+
if ((regGap?.deferred ?? []).length) out.push({
|
|
16825
|
+
reason: `register coverage deferred on ${deferredAxes.join(", ")} — the search did not finish and must be re-run before this can be relied on`,
|
|
16826
|
+
clause: "register coverage deferred — the search did not finish and must be re-run before this can be relied on",
|
|
16827
|
+
});
|
|
16828
|
+
if ((regGap?.taintAxes ?? []).length) out.push({
|
|
16829
|
+
reason: `the ${regGap.taintAxes.join(", ")} register pass was cut down at the timeout wall and its self-reported coverage is unverified`,
|
|
16830
|
+
clause: "the register pass was cut down at the timeout wall and its self-reported coverage is unverified",
|
|
16831
|
+
});
|
|
16832
|
+
return out;
|
|
16833
|
+
}
|