clearotron 0.3.2-beta.8 → 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 +34 -3
- package/CONTRIBUTING.md +8 -8
- package/INSTALL.md +6 -6
- package/SECURITY.md +3 -3
- package/bin/brandowner.mjs +3 -3
- package/bin/framework-preflight.mjs +1 -1
- package/bin/start.mjs +18 -4
- 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 +6 -3
- package/docs/architecture/05-config-governance.md +6 -1
- 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 +48 -0
- package/driver/README.md +3 -3
- 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/consumption-ledger.mjs +2 -2
- package/driver/contract-arm2-baseline.json +2 -3
- package/driver/contract-dictation-registry.mjs +19 -19
- package/driver/contract-e3-backlog.mjs +19 -19
- package/driver/contract-e3-baseline.json +14 -14
- package/driver/contract-vocabulary.mjs +24 -17
- 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/doubt-ledger.mjs +2 -2
- package/driver/drainer-identity.mjs +34 -8
- package/driver/driver.config.mjs +95 -45
- 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 +5 -5
- package/driver/enqueue-schema.mjs +6 -2
- package/driver/findings-model.mjs +5 -2
- package/driver/flag-snapshot.mjs +6 -3
- package/driver/form-neighbourhood.mjs +54 -7
- package/driver/framework.mjs +4 -4
- package/driver/gateway.mjs +12 -6
- package/driver/jx-lanes.mjs +2 -2
- package/driver/jx-units.mjs +1 -1
- package/driver/jx.mjs +30 -2
- package/driver/knockout-review-record.mjs +56 -4
- package/driver/known-conflicts.mjs +1 -1
- package/driver/named-band.mjs +1 -33
- 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 +206 -68
- package/driver/placement-form.mjs +77 -1
- package/driver/placement-model.mjs +1 -1
- package/driver/portal-report.mjs +92 -5
- package/driver/portal-service.mjs +34 -8
- package/driver/portal-upstream.mjs +1 -1
- 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 +110 -5
- package/driver/publish/knockout.mjs +29 -4
- package/driver/publish/pool-admin.mjs +1 -1
- package/driver/publish/publish-inputs.mjs +18 -2
- package/driver/publish/render-knockout.mjs +115 -24
- package/driver/publish/render.mjs +153 -34
- package/driver/publish/search-depth.mjs +133 -4
- package/driver/publish/templates/report.css +60 -3
- package/driver/publish/xlsx.mjs +8 -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-count.mjs +50 -5
- package/driver/register-coverage.mjs +67 -0
- package/driver/register-grant-vocabulary.mjs +1 -1
- package/driver/register-plan.mjs +19 -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/run-requirements.mjs +3 -3
- package/driver/runner.mjs +2 -2
- 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 +53 -53
- package/driver/status-snapshot.mjs +2 -2
- package/driver/suite-census.json +218 -92
- package/driver/surface-exit-verdict.mjs +58 -0
- package/driver/systemd/README.md +2 -2
- package/driver/systemd/clearotron-worker.service +1 -1
- package/driver/terminal-clamp.mjs +2 -0
- 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 +67 -6
- package/driver/whatif-queue.mjs +1 -1
- package/driver/wordlists/en.txt +63906 -0
- package/mcp-server/CHANGELOG.md +4 -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 +3 -0
- package/package.json +12 -11
- package/portal-ui/dist/assets/{index-CVOIvdhc.css → index-CtvwLCti.css} +207 -3
- package/portal-ui/dist/assets/{index-6jzO9HiX.js → index-EVaSo5-g.js} +1459 -482
- 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/oauth-mcp-bridge/CHANGELOG.md +4 -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/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 +4 -4
- 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 +27 -0
- package/scripts/env-classify.mjs +20 -2
- package/scripts/freeze-example-run.mjs +20 -10
- 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/shared/brand.mjs +27 -0
- package/shared/connect-clients.mjs +39 -11
- 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 +3 -1
- 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
|
|
|
@@ -80,7 +80,7 @@ import { resolveSearchPolicy, gateResolvedPolicy, loadRecipes, policyFor, isRegi
|
|
|
80
80
|
import { profileOrdinals } from "./profile-selection.mjs"; // lever 3 — driver selection
|
|
81
81
|
// THE OFFERING'S own sentence about where the native-language investigation can be bought. It reaches a
|
|
82
82
|
// CLIENT REPORT through the script-scope coverage row, which is why it is imported rather than typed:
|
|
83
|
-
// 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.
|
|
84
84
|
import { NATIVE_LANGUAGE_REMEDY } from "./products.mjs";
|
|
85
85
|
import { resolveTerritories, defaultTerritoryState } from "./effective-scope.mjs"; // the ONE territory ladder (the geography stamp included) + the stored-defaults reading
|
|
86
86
|
import { acquireSlot, releaseSlot } from "./slot-lock.mjs";
|
|
@@ -170,10 +170,10 @@ import { JURISDICTION_ADAPTERS, LANGUAGE_LANES, scopeJurisdictions as jxScopeJur
|
|
|
170
170
|
// into boundDominantElementFrom, so the driver's bind and the record tool's bind are ONE body. Neither
|
|
171
171
|
// parseBlindFrameModel nor dominantElementFromManifest is called from this file any more.
|
|
172
172
|
import { boundDominantElementFrom } from "./frame-diff-record.mjs";
|
|
173
|
-
import { recordedScopeLedgerRows } from "./
|
|
173
|
+
import { recordedScopeLedgerRows } from "./clearance-variants-record.mjs";
|
|
174
174
|
import { parseFrameDiff, applyDominantBackstop, firingDirectives, reopenKey, alreadyAttemptedReopen, partitionFiring, frameResidualGaps, jurisdictionScopeFlags, deriveDirectiveRemedy, firingDirectivesLenient } from "./frame-diff-model.mjs";
|
|
175
175
|
import { verifyRegisterDirectiveClose } from "./close-verify.mjs";
|
|
176
|
-
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";
|
|
177
177
|
import { findRecallFloorViolations, findReviewFreshnessViolation, findSeedNeutralityViolations, findProbativeGradingViolations, findStatusHonestyViolation, findMatrixCeilingViolations, findDeadlineUrgencyMiss, findUnresolvedDisagreements, findOrphanVerificationFlags, findUncrossCheckedDemotions, findRecallRegressionViolations, findDeadlineCarryViolations, formatRecallRegression } from "./reasoning-tripwires.mjs";
|
|
178
178
|
import { findRuleShapeFlags } from "./rule-shape.mjs";
|
|
179
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";
|
|
@@ -939,7 +939,7 @@ function deriveGridSpec(ctx) {
|
|
|
939
939
|
// 2026-08-11 codex R2 run (no grid spec authored at all), and it is the branch that emits NO event
|
|
940
940
|
// today, which is why absence was the only signal and absence is not a record.
|
|
941
941
|
// The three inputs are recorded, not just their verdict. Working hit exactly this wall: the
|
|
942
|
-
// 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
|
|
943
943
|
// was false could not be recovered from any artifact — so the round could name the path but not the
|
|
944
944
|
// cause. A record that says "no spec" and not "no spec BECAUSE the profile carried no platforms" just
|
|
945
945
|
// moves the same unanswerable question one step along.
|
|
@@ -1720,7 +1720,7 @@ export function attachSearchPolicy(ctx, job, { write = true } = {}) { // @inte
|
|
|
1720
1720
|
// a clearance (review 2026-07-17 — the crash-reclaim/reconstruct window).
|
|
1721
1721
|
if (!noSelector)
|
|
1722
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`);
|
|
1723
|
-
ctx.searchPolicy = { schema: 1, level: "
|
|
1723
|
+
ctx.searchPolicy = { schema: 1, level: "clearance", pipeline: "clearance", stageLabel: "Depth 4", components: {}, recipe: null, origins: { level: "legacy-implicit" } };
|
|
1724
1724
|
} else {
|
|
1725
1725
|
let resolved;
|
|
1726
1726
|
// force:true — the recipe store is written by a LIVE service; a long-lived driver process must see a
|
|
@@ -1905,7 +1905,7 @@ function renderDocumentCoverageFromRecords(ctx, trigger) {
|
|
|
1905
1905
|
// The set the GATES consume, not `_records/` alone: a run whose bodies live only in the ledger would
|
|
1906
1906
|
// otherwise be described as one that fetched nothing — the defect this renders against, re-created by
|
|
1907
1907
|
// reading a narrower artifact than the one that holds the answer.
|
|
1908
|
-
const { records } = assembleRunRecords(P.runDir, `
|
|
1908
|
+
const { records } = assembleRunRecords(P.runDir, `clearance-${ctx.run.slug}-${ctx.run.codename}-`);
|
|
1909
1909
|
const summary = documentCoverage({ records, findings: safeReadJson(P.findings) });
|
|
1910
1910
|
const section = renderDocumentCoverageSection(summary);
|
|
1911
1911
|
if (!section) return;
|
|
@@ -2103,7 +2103,7 @@ function deriveCoverageLedgerJson(ctx, trigger) {
|
|
|
2103
2103
|
}
|
|
2104
2104
|
}
|
|
2105
2105
|
|
|
2106
|
-
// 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
|
|
2107
2107
|
// `### Scope ledger` table (so the JSON is authored by the driver, not the model, and matches the prose
|
|
2108
2108
|
// by construction — exactly the deriveCoverageLedgerJson pattern). NEVER-KILL: a manifest with no/unparseable
|
|
2109
2109
|
// Scope ledger (legacy artifacts, a terse run) logs a note and skips the write; the frame-diff then reads the
|
|
@@ -2132,7 +2132,7 @@ function deriveScopeLedgerJson(ctx) {
|
|
|
2132
2132
|
// are different facts about a run, and a reader of an archived ledger must be able to tell which.
|
|
2133
2133
|
runLog(P.runDir, { event: "scope-ledger-derived", source: recordedRows ? "typed-call" : "prose-parse" });
|
|
2134
2134
|
} catch (e) {
|
|
2135
|
-
note(`
|
|
2135
|
+
note(`clearance-variants: scope-ledger derivation skipped (${String(e.message).slice(0, 100)}) — frame-diff reads the manifest prose`);
|
|
2136
2136
|
runLog(P.runDir, { event: "scope-ledger-skipped", reason: `derive_failed:${String(e.message).slice(0, 80)}` });
|
|
2137
2137
|
}
|
|
2138
2138
|
}
|
|
@@ -2179,14 +2179,14 @@ function deriveFormNeighbourhood(ctx) {
|
|
|
2179
2179
|
// The MARK is the floor's fallback seed and the only input to it that is not model output —
|
|
2180
2180
|
// resolved exactly as every other job-mark consumer resolves it (stages.mjs blind-frame).
|
|
2181
2181
|
const mark = ctx.job.marks ?? ctx.job.markName ?? ctx.job.name ?? "";
|
|
2182
|
-
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
|
|
2183
2183
|
const tmp = P.formNeighbourhood + ".tmp";
|
|
2184
2184
|
writeFileSync(tmp, json);
|
|
2185
2185
|
renameSync(tmp, P.formNeighbourhood);
|
|
2186
|
-
let exact = 0, floor = 0, added = 0, seededFrom = "", seeds = [];
|
|
2186
|
+
let exact = 0, floor = 0, added = 0, seededFrom = "", seeds = [], notSearched = 0;
|
|
2187
2187
|
try {
|
|
2188
2188
|
const o = JSON.parse(json);
|
|
2189
|
-
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);
|
|
2190
2190
|
floor = o.variant_floor?.counts?.floor ?? 0;
|
|
2191
2191
|
added = o.variant_floor?.counts?.model_added ?? 0;
|
|
2192
2192
|
seededFrom = String(o.seeded_from ?? "");
|
|
@@ -2194,11 +2194,11 @@ function deriveFormNeighbourhood(ctx) {
|
|
|
2194
2194
|
} catch { /* counts are telemetry only */ }
|
|
2195
2195
|
// The SEED SET is logged by name, because it is the input that decides whether two runs of the same
|
|
2196
2196
|
// matter can produce the same floor — a floor count alone cannot tell a stable run from a re-anchored one.
|
|
2197
|
-
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 } : {}) });
|
|
2198
2198
|
// The fallback is not a quiet degrade: it means this run's variant floor rests on the mark alone
|
|
2199
2199
|
// because the stage named no usable element. Loud, so the absence is a finding and not a shrug.
|
|
2200
2200
|
if (seededFrom.startsWith("job mark"))
|
|
2201
|
-
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)`);
|
|
2202
2202
|
} catch (e) {
|
|
2203
2203
|
// The reason is carried WHOLE. It used to be truncated to 80 characters into the runLog and
|
|
2204
2204
|
// 100 into the note, which cut the part that says WHICH cause fired — and the empty floor this path
|
|
@@ -2206,7 +2206,7 @@ function deriveFormNeighbourhood(ctx) {
|
|
|
2206
2206
|
// compiles and freezes with no floor entries and no error, looking healthy. This line is the only
|
|
2207
2207
|
// record that the mechanical floor is missing from the run, so it says the whole of why.
|
|
2208
2208
|
const why = String(e.message);
|
|
2209
|
-
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`);
|
|
2210
2210
|
runLog(P.runDir, { event: "form-neighbourhood-skipped", reason: `derive_failed:${why}`, mark: markText(ctx.job.marks ?? ctx.job.markName ?? ctx.job.name ?? "") });
|
|
2211
2211
|
}
|
|
2212
2212
|
}
|
|
@@ -2297,7 +2297,7 @@ async function verifyAndRecordHouseElement(ctx, opts = {}) {
|
|
|
2297
2297
|
const { regions } = resolvePlanRegions(registerJurisdictions(ctx.job, ctx.profile), caps);
|
|
2298
2298
|
const rec = resolveRecordExecutor({
|
|
2299
2299
|
lister: opts?.recordLister ?? null, adapter: activeProvider(),
|
|
2300
|
-
agentId: ctx.agentId ?? null, sessionKey: `
|
|
2300
|
+
agentId: ctx.agentId ?? null, sessionKey: `clearance-${ctx.run.slug}-${ctx.run.codename}`,
|
|
2301
2301
|
recordLog: runRecordLogPath(P.runDir),
|
|
2302
2302
|
fixtureDir: ctx.job?.registerFixtures?.records ?? null,
|
|
2303
2303
|
});
|
|
@@ -2385,7 +2385,12 @@ function attachRegisterPlan(ctx, { frozenOnly = false } = {}) {
|
|
|
2385
2385
|
// sweep); fatal on a provider whose regions[] is mandatory, where every entry then errored on its
|
|
2386
2386
|
// count probe and the whole plan joined MISSING at fan-in (review finding 11).
|
|
2387
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) },
|
|
2388
|
-
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,
|
|
2389
2394
|
// phase 3 — the plan is compiled AGAINST THE ACTIVE PROVIDER's declared capabilities, so the
|
|
2390
2395
|
// frozen artifact is executable by construction: the OR-stack split uses that provider's width,
|
|
2391
2396
|
// jurisdictions are translated into its office vocabulary (EU→EM on Compumark), and a predicate
|
|
@@ -3833,7 +3838,7 @@ export function digestDispatchExtra(ctx, { trigger = "fresh", willRun = true, ex
|
|
|
3833
3838
|
// The old `findFloorBreaches` read ⭐-marked lines out of the manifest TEXT, tokenised them, dropped
|
|
3834
3839
|
// generic words and matched the remainder against coverage-row text. That join is why it needed free text
|
|
3835
3840
|
// at all, and it is what conversion 3 made unreachable. This one reads `search_floor` — axis names, closed
|
|
3836
|
-
// against REGISTER_AXES, designated by
|
|
3841
|
+
// against REGISTER_AXES, designated by clearance-variants in an earlier turn — and joins on `axis`, which
|
|
3837
3842
|
// both sides type. No tokens, no matching, nothing to go quietly wrong.
|
|
3838
3843
|
//
|
|
3839
3844
|
// A BREACH IS: an axis this mark's floor obliges, carrying a row labelled `coverage-limited`. That label
|
|
@@ -3859,7 +3864,7 @@ export function findFloorBreaches(ledger, floorAxes) { // @internal
|
|
|
3859
3864
|
* a pass, because the whole mechanism is opt-in — a run with no designation owes no floor, and a run whose
|
|
3860
3865
|
* manifest cannot be read has no designation to honour. The refusal for an absent or unparseable manifest
|
|
3861
3866
|
* belongs to the stage that writes it and already exists there — verify.mjs:1335 runs the same parser
|
|
3862
|
-
* through `checkSiblingJson` and fails
|
|
3867
|
+
* through `checkSiblingJson` and fails clearance-variants with `variantmodel_missing`. Checked, because
|
|
3863
3868
|
* "something else refuses it" is exactly the assumption that turns a swallowed error into a silent pass.
|
|
3864
3869
|
*/
|
|
3865
3870
|
export function readFloorAxes(paths) { // @internal
|
|
@@ -3868,7 +3873,7 @@ export function readFloorAxes(paths) { // @internal
|
|
|
3868
3873
|
}
|
|
3869
3874
|
|
|
3870
3875
|
// A never-active axis self-writes a harmless "not applicable" digest — the skill MANDATES the exact
|
|
3871
|
-
// scope wording (skills/
|
|
3876
|
+
// scope wording (skills/clearance-register/unit.md: `query:"<axis> not applicable"`,
|
|
3872
3877
|
// `reason:"not applicable — <why>"`), so the coverage-ledger row lands as `<axis> / axis not applicable`.
|
|
3873
3878
|
// It is `deferred` only because a non-search must not be dressed as a clean — it is NOT floor work left
|
|
3874
3879
|
// open. Keyed on the self-digest scope (deterministic, skill-dictated), never a genuine floor's
|
|
@@ -4059,7 +4064,7 @@ export async function runSaturationProbeCodeSide(ctx, planExec) { // @internal
|
|
|
4059
4064
|
const label = `register-unit:${a}`;
|
|
4060
4065
|
const out = P.registerUnit(a);
|
|
4061
4066
|
const bandPath = P.registerBand(a);
|
|
4062
|
-
const sessionKey = `
|
|
4067
|
+
const sessionKey = `clearance-${ctx.run.slug}-${ctx.run.codename}-register-unit-${a}`;
|
|
4063
4068
|
// crash-resume idempotency — mirrors stageOnce's skip: output present + valid ⇒ never re-execute.
|
|
4064
4069
|
const forced = forceFromActive(ctx, "register-unit");
|
|
4065
4070
|
// — hoisted above the skip return. Below it, this member's dispatch row carries boundaries and its
|
|
@@ -4179,7 +4184,7 @@ function writeSatProbeAuditNote({ ctx, out, bandPath, entries }) {
|
|
|
4179
4184
|
|
|
4180
4185
|
// ── stage contracts (2026-07-30 review round) ──────────────────────────────────────────────────────
|
|
4181
4186
|
// _driver/stage-contracts.json records, PER STAGE, the output contract the prompt THIS driver
|
|
4182
|
-
// 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
|
|
4183
4188
|
// `contract:{romanization:1}`). Written at DISPATCH time only — a skipped stage never gains one — so
|
|
4184
4189
|
// it is evidence of PROMPT VINTAGE, which is the only honest gate for a new validation rule:
|
|
4185
4190
|
// - crash-resume: the skip check re-validates the completed output BEFORE any dispatch, marker
|
|
@@ -4343,7 +4348,7 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4343
4348
|
// skips — i.e. on the babysit surface, in the middle of the shift where someone is watching it.
|
|
4344
4349
|
runLog(P.runDir, { event: "skip", stage: label, trigger: "skip", model: won?.model ?? resolveModel(model), output: outputMeta(out), ...stageWallFields(tDispatch) });
|
|
4345
4350
|
(ctx.skippedStages ??= []).push(label); // V4-1: the lint receipt states which artifacts this delivery inherited
|
|
4346
|
-
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)
|
|
4347
4352
|
// `won ? won.key : …` (NOT `won?.key ?? …`): a recovered CODE-SIDE winner carries key:null on purpose —
|
|
4348
4353
|
// falling back to opts.sessionKey there would re-mint the phantom session the null exists to suppress.
|
|
4349
4354
|
return { ok: true, skipped: true, sessionKey: won ? won.key : opts.sessionKey, model: won?.model ?? model };
|
|
@@ -4354,6 +4359,25 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4354
4359
|
// output under the rules it is being minted under. See recordStageContract's doc block.
|
|
4355
4360
|
if (def.contract) recordStageContract(P.runDir, name, def.contract);
|
|
4356
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
|
+
|
|
4357
4381
|
// — THE PLACEMENT FORM IS ON DISK BEFORE THE SEAT IS ASKED ANYTHING, and it is written at the SAME
|
|
4358
4382
|
// choke point every dispatch funnels through: the main path, the stale-repair path and the frame-reopen
|
|
4359
4383
|
// path all reach the seat through here, and three separate pre-dispatch writes would be three chances
|
|
@@ -4491,7 +4515,7 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4491
4515
|
thinking,
|
|
4492
4516
|
// opts.sessionKey lets a followup RESUME the exact key a prior run won on (winning-key hardening); else
|
|
4493
4517
|
// the canonical base key. (A followup with a stale base key would resume a failed attempt — see runStage.)
|
|
4494
|
-
sessionKey: opts.sessionKey ?? `
|
|
4518
|
+
sessionKey: opts.sessionKey ?? `clearance-${ctx.run.slug}-${ctx.run.codename}-${name}${keyAxis}`,
|
|
4495
4519
|
timeoutSec: derivedLimit.sec ?? def.timeoutSec,
|
|
4496
4520
|
derivedLimit, // recorded on every attempt row: the size it was derived from, and the number
|
|
4497
4521
|
stallSec: def.stallSec, // per-stage stall override (heavy stages); undefined → global CLEAROTRON_STALL_MS
|
|
@@ -4580,6 +4604,8 @@ async function stageOnce(name, ctx, opts = {}) {
|
|
|
4580
4604
|
});
|
|
4581
4605
|
// P2 — record WHAT this output was produced from, so a later resume can tell whether it still holds.
|
|
4582
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.
|
|
4583
4609
|
if (r.ok) recordTransition(ctx, name); // bare name → fan-out axes collapse to one display step (monotonic)
|
|
4584
4610
|
// PR-4 — document-growth tripwire, keyed on TRIGGER so it covers BOTH growth loops (the lint-repair
|
|
4585
4611
|
// "re-emit the COMPLETE updated file" cycle AND the skeptic/refutation corrective loop — the 2026-07-28 postmortem run's
|
|
@@ -4770,7 +4796,7 @@ function must(r, name) {
|
|
|
4770
4796
|
* absence reads as an empty cause — which is the whole defect, one level along. PURE.
|
|
4771
4797
|
*
|
|
4772
4798
|
* — VISIBLE IN THE STRING, not only in a sibling key. `reason` was a bare slice, so a cut sentence
|
|
4773
|
-
* 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"
|
|
4774
4800
|
* — and the reader who stat'd that directory got ENOENT and diagnosed a working mechanism as a broken
|
|
4775
4801
|
* one. Nothing had been lost: `reasonFull` carried the whole path on all three and `reasonTruncated` was
|
|
4776
4802
|
* true on all three. The field that gets read FIRST simply never said it was short. `abbrev` is this
|
|
@@ -5390,7 +5416,7 @@ export function recordConnotationAudit(run, P) { // @internal
|
|
|
5390
5416
|
// ── — DOES A CHARGED RATING CARRY ITS GROUNDS? RECORDED, NOT ENFORCED ───────────────────────
|
|
5391
5417
|
//
|
|
5392
5418
|
// Doctrine already asks for this in as many words: "rule `loaded` and use the note to state plainly
|
|
5393
|
-
// 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
|
|
5394
5420
|
// seat does not do it — e2e READ all 24 `loaded` notes in the corpus (not keyword-probed them) and
|
|
5395
5421
|
// every one describes the material instead. `grounds-grammar.mjs` was built to measure exactly that,
|
|
5396
5422
|
// proven against those real notes and against a planted violation, AND CALLED BY NOTHING. A check
|
|
@@ -6254,7 +6280,7 @@ function refuteRegistryCheckExtra(ctx) {
|
|
|
6254
6280
|
const P = ctx.paths;
|
|
6255
6281
|
const narrative = existsSync(P.narrative) ? readFileSync(P.narrative, "utf8") : "";
|
|
6256
6282
|
if (!narrative) return "";
|
|
6257
|
-
const recordsByUri = assembleRunRecords(P.runDir, `
|
|
6283
|
+
const recordsByUri = assembleRunRecords(P.runDir, `clearance-${ctx.run.slug}-${ctx.run.codename}-`).records;
|
|
6258
6284
|
const probs = [
|
|
6259
6285
|
...findRegistryArithmeticIssues(narrative).map((a) => `${a.block}: ${a.detail}`),
|
|
6260
6286
|
...findRegistryViolations(narrative, recordsByUri).filter((v) => v.kind === "mismatch")
|
|
@@ -7127,7 +7153,7 @@ function injectDeferralCoverage(P, runDir, note) {
|
|
|
7127
7153
|
// ── THE REMEDY CLAUSE NAMES A PRODUCT, AND FOR A WHILE IT NAMED A DELETED ONE ───────────────────────
|
|
7128
7154
|
//
|
|
7129
7155
|
// This row reaches a CLIENT REPORT. It read "…not searched at this level — available at Depth 5
|
|
7130
|
-
// (
|
|
7156
|
+
// (clearance-jx)": an internal product key and a rung on the depth ladder, both retired by, offered as
|
|
7131
7157
|
// the remedy for a coverage limit the same sentence had just disclosed. `injectScriptScopeCoverage`
|
|
7132
7158
|
// calls the decision on EVERY run, and it was driven — it fires for a worldwide scope, a CN-named scope
|
|
7133
7159
|
// and the ja lane — so this was delivered prose telling a reader to buy something that does not exist.
|
|
@@ -7141,7 +7167,7 @@ const WORLDWIDE_SCOPE_RE = /^(worldwide|global|all|all[- ]jurisdictions)$/i;
|
|
|
7141
7167
|
/** What a synthesis-authored Stage-1.5 coverage row looks like when it has ALREADY made this
|
|
7142
7168
|
* disclosure — see the suppression in injectScriptScopeCoverage. Both the current vocabulary and the
|
|
7143
7169
|
* retired one, because a resumed run carries whichever was current when its synthesis ran. */
|
|
7144
|
-
const SCRIPT_SCOPE_RECOMMENDATION_TOKENS = Object.freeze(["native-language investigation", "
|
|
7170
|
+
const SCRIPT_SCOPE_RECOMMENDATION_TOKENS = Object.freeze(["native-language investigation", "clearance-jx"]);
|
|
7145
7171
|
|
|
7146
7172
|
/** The disclosure vocabulary for ONE candidate lane, derived (never tabulated). Returns
|
|
7147
7173
|
* `{area, note, territories, marker}` — `marker` is the lowercased script prefix the suppression
|
|
@@ -7265,14 +7291,14 @@ export function injectScriptScopeCoverage(P, runDir, note, { searchPolicy = null
|
|
|
7265
7291
|
const rowText = (c) => `${c?.area ?? ""} ${c?.note ?? ""}`.toLowerCase();
|
|
7266
7292
|
const added = [];
|
|
7267
7293
|
for (const p of pending) {
|
|
7268
|
-
// 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
|
|
7269
7295
|
// covering the disclosure, which was harmless while zh was the only lane and is a silent row-
|
|
7270
7296
|
// eater the moment there are two: on a CN+JP scope the zh row lands first, its own note ended in
|
|
7271
|
-
// "(
|
|
7297
|
+
// "(clearance-jx)", and the ja row was then swallowed by a match on the sibling's text. So the
|
|
7272
7298
|
// deference to a synthesis-authored Stage-1.5 row requires the row to be about THIS lane.
|
|
7273
7299
|
//
|
|
7274
7300
|
// TWO VOCABULARIES ON PURPOSE. `native-language investigation` is what the offering calls it now
|
|
7275
|
-
// 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
|
|
7276
7302
|
// RESUMED from before carries a synthesis row written in it — dropping the old token would
|
|
7277
7303
|
// put a second, duplicate disclosure into a report that already makes the same one.
|
|
7278
7304
|
const already = doc.coverage.some((c) => {
|
|
@@ -7683,7 +7709,7 @@ export function buildOnlyYouSection(actions, findings, { nowMs = Date.now(), wit
|
|
|
7683
7709
|
// blocking review goes." That REVERSES T3, which retired "delivered-with-open-questions" and
|
|
7684
7710
|
// is itself recorded as an owner-approved decision — both are his, and this is the standing one.
|
|
7685
7711
|
//
|
|
7686
|
-
// 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`
|
|
7687
7713
|
// has described it all along — "delivered … as a prominent Reviewer's open questions section at the
|
|
7688
7714
|
// top of the body (the driver passes them in)" — and `:295` lists it as a required section with
|
|
7689
7715
|
// "Never omit it to look more finished." deleted the driver's half and left that text
|
|
@@ -8013,7 +8039,7 @@ export function assembleReportMd(P, findings, cardOrdinals, { grouped = [], byRi
|
|
|
8013
8039
|
} catch { /* never-kill: a malformed findings.json leaves the overview untouched (its own gates own that) */ }
|
|
8014
8040
|
// ── T3a — THE REVIEWER'S OPEN POINTS, AT THE TOP OF THE BODY ──────────────────────────────────────
|
|
8015
8041
|
//
|
|
8016
|
-
// `driver/skills/
|
|
8042
|
+
// `driver/skills/clearance-search/SKILL.md` says where: "a prominent Reviewer's open questions section at the TOP of the body
|
|
8017
8043
|
// (the driver passes them in)". That sentence has been true of the contract and false of the code
|
|
8018
8044
|
// since deleted the driver's half; this is the half coming back.
|
|
8019
8045
|
//
|
|
@@ -8989,7 +9015,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
8989
9015
|
// Phase 0 done in code (slug/codename/run-dir/customer). Phase 1+2 stages:
|
|
8990
9016
|
must(await stage("matter-frame", ctx), "matter-frame");
|
|
8991
9017
|
await deriveIntakeAsks(ctx); // A6: freeze the intake-ask register (one save-only followup if the section is missing)
|
|
8992
|
-
must(await stage("
|
|
9018
|
+
must(await stage("clearance-variants", ctx), "clearance-variants");
|
|
8993
9019
|
deriveScopeLedgerJson(ctx); // frame-omission design: code-derive scope-ledger.json from the validated prose (never-kill)
|
|
8994
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)
|
|
8995
9021
|
await verifyAndRecordHouseElement(ctx, opts); // ask the register who owns the frame's proposed house element, BEFORE the plan is compiled from it
|
|
@@ -9803,7 +9829,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9803
9829
|
const dispatchStartedMs = Date.now();
|
|
9804
9830
|
try {
|
|
9805
9831
|
const r = await planExec({ planPath: P.registerPlan, axis: a, outputPath: P.registerBand(a), ...(qids?.length ? { qids } : {}) },
|
|
9806
|
-
{ agentId: agent, sessionKey: `
|
|
9832
|
+
{ agentId: agent, sessionKey: `clearance-${run.slug}-${run.codename}-register-unit-${a}`, recordLog: runRecordLogPath(run.runDir) });
|
|
9807
9833
|
outcome = r?.ok ? "ok" : `failed: ${String(r?.cause ?? "unknown").slice(0, 140)}`;
|
|
9808
9834
|
} catch (err) { outcome = `threw: ${String(err?.message ?? err).slice(0, 140)}`; }
|
|
9809
9835
|
// — MEASURE WHETHER THE HOLE CLOSED, not whether the phone was answered. `outcome` is the
|
|
@@ -9890,7 +9916,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9890
9916
|
// production default is ON.
|
|
9891
9917
|
if (process.env.CLEAROTRON_BAND_TRUTH_GATE !== "0"
|
|
9892
9918
|
&& unitKey[a] && existsSync(P.registerBand(a)) && (ctx.registerPlan?.entries ?? []).some((e) => e?.axis === a)) {
|
|
9893
|
-
const laneKey = `
|
|
9919
|
+
const laneKey = `clearance-${run.slug}-${run.codename}-register-unit-${a}`;
|
|
9894
9920
|
const bandHasQids = (() => {
|
|
9895
9921
|
try { const b = JSON.parse(readFileSync(P.registerBand(a), "utf8")); return Array.isArray(b) && b.some((x) => x && typeof x === "object" && x.qid); }
|
|
9896
9922
|
catch { return false; /* parse defects are the stage validator's problem, not this gate's */ }
|
|
@@ -9963,7 +9989,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9963
9989
|
if (!digestLocked && repairLedger.canAttempt("taint-rerun", a, { max: taintRerunMax, epoch: repairEpoch })) {
|
|
9964
9990
|
note(`register-unit ${a}: taint-rerun — one fresh re-run (fresh session; a killed session is never warm-resumed)`);
|
|
9965
9991
|
const r = await stage("register-unit", { ...ctx, axis: a },
|
|
9966
|
-
{ force: true, trigger: "taint-rerun", sessionKey: `
|
|
9992
|
+
{ force: true, trigger: "taint-rerun", sessionKey: `clearance-${run.slug}-${run.codename}-register-unit-${a}-taint-rerun-${recoveryAttempts}` });
|
|
9967
9993
|
repairLedger.record("taint-rerun", a, r.ok ? "ok" : `failed: ${String(r.fail ?? "unknown").slice(0, 100)}`, { epoch: repairEpoch, max: taintRerunMax });
|
|
9968
9994
|
repairs.push(`taint-rerun:${r.ok ? "ok" : `failed: ${String(r.fail ?? "unknown").slice(0, 60)}`}`);
|
|
9969
9995
|
if (r.ok && r.sessionKey) unitKey[a] = r.sessionKey;
|
|
@@ -10288,7 +10314,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
10288
10314
|
runLog(run.runDir, { event: "plan-qids-missing", axis: a, qids: entries.map((e) => e.qid), action: "fresh-execute-plan" });
|
|
10289
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`);
|
|
10290
10316
|
const freshMsg = repairFollowup("register-unit:plan-join-fresh", { axis: a, registerPlan: P.registerPlan, bandPath: P.registerBand(a) });
|
|
10291
|
-
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" });
|
|
10292
10318
|
if (!rf.ok) note(`register-unit ${a}: fresh execute_plan call failed (${rf.fail}) — the plan-unexecuted StageFailure below holds the line`);
|
|
10293
10319
|
continue;
|
|
10294
10320
|
}
|
|
@@ -11113,7 +11139,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
11113
11139
|
note(`frame-reopen: ${a} timed out — ONE fresh SCOPED retry through the fallback chain (backup provider + retries)`);
|
|
11114
11140
|
const retryMsg = repairFollowup("register-unit:frame-reopen-retry", { paths: P, axis: a, directives: regDirectives, reopenFetchCap,
|
|
11115
11141
|
supplementalLane: !!ctx.registerPlan?.contract?.supplemental_lane });
|
|
11116
|
-
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" });
|
|
11117
11143
|
if (r2.ok && existsSync(P.registerUnit(a)) && readFileSync(P.registerUnit(a), "utf8") !== beforeUnit) {
|
|
11118
11144
|
changedAxes.push(a); regChanged = true; regMechFail = null;
|
|
11119
11145
|
note(`frame-reopen: ${a} fresh scoped retry closed the gap`);
|
|
@@ -11884,7 +11910,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
11884
11910
|
// violating after that one recovery pass is disclosed as unexamined + clamps the verdict (owner
|
|
11885
11911
|
// decision 2026-07-22 — the terminal branch below); the gate never silently passes a drop.
|
|
11886
11912
|
{
|
|
11887
|
-
const runPrefix = `
|
|
11913
|
+
const runPrefix = `clearance-${run.slug}-${run.codename}-`;
|
|
11888
11914
|
// Re-read the findings + recompute the fetched universe on EACH check (the recovery re-digest rewrites
|
|
11889
11915
|
// the findings AND may record_fetch the flagged URIs — both must be picked up on the post-recovery
|
|
11890
11916
|
// check). V4-1: the fetched universe is the DELIVERED ARTIFACT SET's records (run-dir _records/ ∪ this
|
|
@@ -12206,7 +12232,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12206
12232
|
regions: ctx.registerPlan?.regions ?? [], entries,
|
|
12207
12233
|
}, null, 2) + "\n");
|
|
12208
12234
|
const r = await planExec({ planPath: ccPlanPath, axis: CROWD_CONTEXT_AXIS, outputPath: ccBandPath },
|
|
12209
|
-
{ agentId: ctx.agent, sessionKey: `
|
|
12235
|
+
{ agentId: ctx.agent, sessionKey: `clearance-${run.slug}-${run.codename}-crowd-context`, recordLog: runRecordLogPath(run.runDir) });
|
|
12210
12236
|
if (!r?.ok) throw new Error(String(r?.cause ?? "crowd-context plan execution failed").slice(0, 200));
|
|
12211
12237
|
const blocks = JSON.parse(readFileSync(ccBandPath, "utf8"));
|
|
12212
12238
|
return Array.isArray(blocks) ? blocks : [];
|
|
@@ -12246,7 +12272,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12246
12272
|
// on fetched records) as a structured aim-attention artifact for synthesis. Best-effort; NEVER a
|
|
12247
12273
|
// rating rule (explicitly not the rejected escalation filter) — absence writes nothing.
|
|
12248
12274
|
try {
|
|
12249
|
-
const { records } = assembleRunRecords(run.runDir, `
|
|
12275
|
+
const { records } = assembleRunRecords(run.runDir, `clearance-${run.slug}-${run.codename}-`);
|
|
12250
12276
|
const signals = extractEnforcerSignals(records);
|
|
12251
12277
|
if (signals.length) {
|
|
12252
12278
|
writeFileSync(`${P.enforcerSignals}.tmp`, JSON.stringify({ signals }, null, 2) + "\n");
|
|
@@ -12788,7 +12814,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12788
12814
|
status: r?.screen?.status ?? r?.status, live_status: r?.screen?.live_status });
|
|
12789
12815
|
}
|
|
12790
12816
|
} catch { /* no merged band (legacy run) — fetched-record facts alone rank */ }
|
|
12791
|
-
const srPrefix = `
|
|
12817
|
+
const srPrefix = `clearance-${run.slug}-${run.codename}-`;
|
|
12792
12818
|
let srRecords = assembleRunRecords(run.runDir, srPrefix).records;
|
|
12793
12819
|
const srFetcher = opts.recordFetcher ?? defaultRecordFetcher;
|
|
12794
12820
|
const srLedger = ctx.repairLedger ?? (ctx.repairLedger = createRepairLedger(run.runDir, { log: (o) => runLog(run.runDir, o) }));
|
|
@@ -12862,7 +12888,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
12862
12888
|
const carriedUris = (pf?.findings ?? []).flatMap((f) =>
|
|
12863
12889
|
Array.isArray(f?.owner?.registrations) ? f.owner.registrations : []
|
|
12864
12890
|
).map((r) => r?.uri && (normalizeRecordUri(r.uri) || String(r.uri).trim())).filter(Boolean);
|
|
12865
|
-
const fetchedUris = [...assembleRunRecords(run.runDir, `
|
|
12891
|
+
const fetchedUris = [...assembleRunRecords(run.runDir, `clearance-${run.slug}-${run.codename}-`).records.keys()];
|
|
12866
12892
|
const rfMd = existsSync(P.registerFindings) ? readFileSync(P.registerFindings, "utf8") : "";
|
|
12867
12893
|
const violations = findRecallRegressionViolations({
|
|
12868
12894
|
knownConflicts, searchedNames: namesForRecall, carriedUris, fetchedUris,
|
|
@@ -13221,22 +13247,22 @@ async function pipelineInner(job, opts = {}) {
|
|
|
13221
13247
|
// lawyer-judged/disclosed residue only.
|
|
13222
13248
|
if (coverageInsufficient || frameResidual || screenGateGap || seniorGap || registerGap || deadlineGap) {
|
|
13223
13249
|
// THESE SENTENCES REACH A CLIENT AND THEY ARE NOT OURS TO WRITE (owner, 2026-09-17). One of
|
|
13224
|
-
// them — the screen-gate line, which says a mark "could not be record_fetched" —
|
|
13250
|
+
// them — the screen-gate line, which says a mark "could not be record_fetched" — carried an
|
|
13225
13251
|
// engine identifier into the list a client reads as the conditions on their result, by a route
|
|
13226
|
-
// `terminalClampDecision` guards and this one
|
|
13227
|
-
// here and refused: the objection was the class, not the wording.
|
|
13228
|
-
//
|
|
13229
|
-
//
|
|
13230
|
-
const reasons = [];
|
|
13231
|
-
const machinery = (reason) => reasons.push(reason);
|
|
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); };
|
|
13232
13258
|
if (coverageInsufficient) machinery(`the lawyer judged a material slice not fully cleared: ${coverageJudgment.reason || "register coverage gap"}`);
|
|
13233
13259
|
if (frameGap) machinery("the blind frame-diff flagged a dominant-element omission the reopen pass did not close");
|
|
13234
13260
|
else if (frameDeferrals.length) machinery(`follow-ups left open this run: ${frameDeferrals.map((d) => plainDirective(d.directive)).slice(0, 3).join(", ")}`);
|
|
13235
|
-
if (screenGateGap) machinery(`${sgUnresolved.length} in-scope mark(s) dropped on goods could not be record_fetched (unverified): ${sgUnresolved.map((g) => g.mark).join(", ")}
|
|
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)
|
|
13236
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(", ")}`);
|
|
13237
13263
|
if (registerGap) {
|
|
13238
|
-
|
|
13239
|
-
|
|
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)
|
|
13240
13266
|
// Named regressions (2026-07-22): `<MARK> (<owner> — <canonical uri>)` — a bare mark name
|
|
13241
13267
|
// shipped "ION, ION, ION" (three indistinguishable strings); the identity is front-loaded
|
|
13242
13268
|
// because the delivered statement truncates from the tail.
|
|
@@ -13268,7 +13294,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
13268
13294
|
// APPEND (dedup by exact text) — the legalActions arm may already have recorded conditions,
|
|
13269
13295
|
// and this callable runs more than once (degenerate re-ask, lint repair, post-consolidation).
|
|
13270
13296
|
const freshMachinery = reasons.filter((r) => !clampReasons.includes(r));
|
|
13271
|
-
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
|
|
13272
13298
|
clampReasons.push(...freshMachinery);
|
|
13273
13299
|
// The reason KINDS distinguish coverage/frame/screen-gate/senior-right/register residue for the
|
|
13274
13300
|
// report bound line and the client conditions row (merged — legalActions survives).
|
|
@@ -13386,7 +13412,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
13386
13412
|
// review goes." This REVERSES T3 (H3/H5), whose flip to fail-on-BLOCKING is itself recorded
|
|
13387
13413
|
// itself an owner-approved decision. Both are his; this is the standing one, and the
|
|
13388
13414
|
// history is kept here rather than deleted because a reader who finds only one of them will conclude
|
|
13389
|
-
// 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
|
|
13390
13416
|
// change makes it true again.)
|
|
13391
13417
|
//
|
|
13392
13418
|
// COPPER-SPIRE IS STILL ANSWERED, AND NOT BY REFUSING. That failure was a BLOCKING verdict reaching
|
|
@@ -14281,7 +14307,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
14281
14307
|
// the spec's "blocks delivery", approved 2026-06-10: delivering nothing is worse than delivering good
|
|
14282
14308
|
// work with one clearly-labelled defect — and code-produced defects would deadlock a block.)
|
|
14283
14309
|
{
|
|
14284
|
-
const lintPrefix = `
|
|
14310
|
+
const lintPrefix = `clearance-${run.slug}-${run.codename}-`;
|
|
14285
14311
|
// V4-1: the gates evaluate the artifact set assembled for THIS delivery — run-dir _records/
|
|
14286
14312
|
// (inherited or prior-session) ∪ this-session ledger rows — never the session-prefixed ledger
|
|
14287
14313
|
// alone (empty on a fork ⇒ the vacuous registry pass that shipped a false citation).
|
|
@@ -14886,7 +14912,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
14886
14912
|
Array.isArray(f?.owner?.registrations) ? f.owner.registrations : []
|
|
14887
14913
|
).map((r) => r?.uri && (normalizeRecordUri(r.uri) || String(r.uri).trim())).filter(Boolean);
|
|
14888
14914
|
const executedQids = (ctx.planExecution?.executed ?? []).map((x) => x.qid);
|
|
14889
|
-
const fetchedUrisForRecall = [...assembleRunRecords(run.runDir, `
|
|
14915
|
+
const fetchedUrisForRecall = [...assembleRunRecords(run.runDir, `clearance-${run.slug}-${run.codename}-`).records.keys()];
|
|
14890
14916
|
const regLedgerRows = loadCoverageLedger(run.runDir).rows;
|
|
14891
14917
|
// judgment-relocation (2026-06-23): the search-shape gate (findFloorShapeGaps) was DELETED here too —
|
|
14892
14918
|
// sufficiency is judgment's call (coverage_judgment), not a re-parse of the ledger at the surface. This
|
|
@@ -15175,7 +15201,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
15175
15201
|
let providerTally = null;
|
|
15176
15202
|
try {
|
|
15177
15203
|
const provider = activeProvider().id;
|
|
15178
|
-
const usage = tallyRegisterCalls(DEFAULT_LEDGER_PATH, `
|
|
15204
|
+
const usage = tallyRegisterCalls(DEFAULT_LEDGER_PATH, `clearance-${run.slug}-${run.codename}-`);
|
|
15179
15205
|
runLog(run.runDir, { event: "provider-usage", provider, ...usage });
|
|
15180
15206
|
writeRunStatus(ctx, { providerUsage: { [provider]: usage } });
|
|
15181
15207
|
providerTally = { [provider]: usage };
|
|
@@ -15814,6 +15840,46 @@ function resolveRun(job, opts) {
|
|
|
15814
15840
|
|
|
15815
15841
|
// Rebuild the ctx a single stage needs WITHOUT re-running upstream: axes from the persisted manifest, verdict
|
|
15816
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
|
+
|
|
15817
15883
|
export function reconstructCtx(job, opts) { // @internal
|
|
15818
15884
|
const run = resolveRun(job, opts);
|
|
15819
15885
|
const P = paths(run.runDir);
|
|
@@ -15996,7 +16062,7 @@ export async function repairStale(job, opts = {}) { // @internal
|
|
|
15996
16062
|
|
|
15997
16063
|
// --experiment <stage>: re-run ONE stage SANDBOXED — no canonical ARTIFACT is read back or overwritten. The
|
|
15998
16064
|
// stage reads COPIES of its canonical inputs from a shadow dir and writes its output there; a distinct
|
|
15999
|
-
// `
|
|
16065
|
+
// `clearance-exp-…` session key never collides with the canonical session AND sorts outside the run's
|
|
16000
16066
|
// provider-usage prefix (experiment Corsearch calls are not billed to the canonical run). Single-model by
|
|
16001
16067
|
// design (the canonical primary unless --model overrides) so the comparison is clean.
|
|
16002
16068
|
//
|
|
@@ -16193,7 +16259,7 @@ async function runExperimentInner(job, opts) {
|
|
|
16193
16259
|
// so a run with no case-law layer must arrive as null here too, never as an absent key.
|
|
16194
16260
|
for (const f of inlineDecl.fields) shadowCtx[f] = resolved[f] ?? null;
|
|
16195
16261
|
}
|
|
16196
|
-
const sessionKey = `
|
|
16262
|
+
const sessionKey = `clearance-exp-${ctx.run.slug}-${ctx.run.codename}-${name}${axis ? `-${axis}` : ""}-${ts}`;
|
|
16197
16263
|
let { text: extra, ids: extraIds } = experimentExtra(shadowCtx, name, opts);
|
|
16198
16264
|
|
|
16199
16265
|
// — THE CORRECTIVE PASS, on a COLD turn, and it says which pass it reproduced.
|
|
@@ -16455,8 +16521,13 @@ export function retiredEnvWarnings(env = process.env) {
|
|
|
16455
16521
|
// ABSOLUTE PATHS on purpose: this line is read hours later, possibly from a different directory, and a
|
|
16456
16522
|
// relative path that silently resolves somewhere else would be a worse answer than no line at all.
|
|
16457
16523
|
export function resumeCommand({ script, jobPath, codename, agent = null }) { // @internal
|
|
16458
|
-
|
|
16459
|
-
|
|
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}`;
|
|
16460
16531
|
}
|
|
16461
16532
|
|
|
16462
16533
|
// The stderr lines a non-clean exit owes its reader, as an array (empty = say nothing further).
|
|
@@ -16555,7 +16626,7 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16555
16626
|
let a;
|
|
16556
16627
|
try { a = parseArgv(process.argv.slice(2)); }
|
|
16557
16628
|
catch (e) { console.error(`error: ${e.message}`); console.error(USAGE); process.exit(2); }
|
|
16558
|
-
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
|
|
16559
16630
|
// composition guards
|
|
16560
16631
|
if (a.fromStage && !a.codename) { console.error("error: --from requires --resume <codename>"); process.exit(2); }
|
|
16561
16632
|
if (a.experiment && !a.codename) { console.error("error: --experiment requires --resume <codename>"); process.exit(2); }
|
|
@@ -16577,8 +16648,47 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16577
16648
|
// mistake a first-time reader is likeliest to make. Kept guarded here through the rebase — the
|
|
16578
16649
|
// pool check above is a separate refusal and must not swallow this one.
|
|
16579
16650
|
let job;
|
|
16580
|
-
|
|
16581
|
-
|
|
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
|
+
}
|
|
16582
16692
|
// Manual runs: if --agent is given, root the run-dir in that agent's workspace too (mirrors the runner).
|
|
16583
16693
|
const base = a.agent
|
|
16584
16694
|
? { agent: a.agent, studioRoot: config.studioRootForAgent(a.agent), archiveRoot: config.archiveRootForAgent(a.agent) }
|
|
@@ -16590,7 +16700,10 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16590
16700
|
? { ...base, codename: a.codename, experiment: a.experiment, model: a.model, instructions: a.instructions, axis: a.axis, label: a.label, dispatchTrigger: a.dispatchTrigger }
|
|
16591
16701
|
: { ...base, codename: a.codename, fromStage: a.fromStage };
|
|
16592
16702
|
const script = resolve(fileURLToPath(import.meta.url));
|
|
16593
|
-
|
|
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); };
|
|
16594
16707
|
|
|
16595
16708
|
// ── the laptop case: a closed lid, a shutdown, a ^C ────────────────────────────────────────────────
|
|
16596
16709
|
// Until now this CLI had NO signal handler: node's default die-now killed the process mid-stage, the run
|
|
@@ -16693,3 +16806,28 @@ if (isEntrypoint(import.meta.url)) void (async () => {
|
|
|
16693
16806
|
//
|
|
16694
16807
|
// Move it to the top the day those citations name symbols instead of numbers — which is what
|
|
16695
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
|
+
}
|