Skip to content

Commit 9a6f790

Browse files
feat(scripts): dispatch-gates names check:durability-log-level for a file holding a catch and a declared durability-critical callee (#23612)
Fixes #23584 Clause-②: no ## What this does `dispatch-gates --commands` now names `check:durability-log-level` for a diff that touches a non-test TypeScript file holding a catch clause and one of the callee names the guard declares in `DURABILITY_CRITICAL_CALLEES`. PR #23567 ran every derived family (64 of 64, 0 unrun) and still went red on this guard, because the derivation never named it for `packages/runtime/src/domains/automation.ts`. ## Why no path could name it (measured at `origin/main` 16abea8) - The guard runs two rules. Its read-seam rule walks three package roots it spells as literals, so cards touching those already reach it by path. Its log-level rule walks `join(ROOT, 'packages')` and admits every non-test `.ts` under it: 3206 tracked files, 34.8% of that root. Four sibling gates walk that root the same way, and `bare-root-worklist.mjs` records each of them REFUSE-WIDE in `CENSUS_REFUSE_WIDE`. - The wide-population ruling (decision batch #43, 「同意」) keeps such a root out of the matched column and out of `--commands`. So this PR does not declare the bare root, or any spelling of it, as the guard's path population. - Correction to the card's mechanism note: the guard was `silent` for `automation.ts` (it names the three read-seam roots, none of which cover the file), not `undetermined`. The effect was the same: it was in no block that reaches `--commands`. ## The freeze `instrument-discipline.md:10`: 「工具位只有一个,先花在删除上;`dispatch-gates.mjs` 冻结,只在它喂的 workflow 坏了时碰。」 The triage grade (6183320441) found the exception met: the derived list is the workflow it feeds, and it was wrong. This PR does edit `dispatch-gates.mjs`, because no data-only route exists: - A `CHANGE_KIND_ROWS` row can only name a predicate the engine binds. - The seven bound predicates are test-file, i18n-bundle-package, metadata-form-module, gate-script, root-ts-program, error-code-literal and http-status-emit. None of them reads a catch clause or the guard's vocabulary. So the frozen file gains one predicate, its vocabulary reader and one binding line. This meets #23426's `Restart-when`, and #23426's docblock paragraph rides this PR as a declared rider (see below). ## The change | file | what | |---|---| | `scripts/pm/dispatch-gates.data.mjs` | One `CHANGE_KIND_ROWS` row (`durability-seam` → `check:durability-log-level`), in the two content precedents' voice: what the gate sweeps, why no path names it, the PR #23567 round, no build, the repair direction and the WIDE trade. Also its deletion criterion in the table docblock. | | `scripts/pm/dispatch-gates.mjs` | `guardsADurabilityCriticalCall`, the predicate. `declaredMapKeys` reads the keys of a `const NAME = new Map([...])` table out of source: bracket depth is counted on the comment-and-literal mask, and a non-identifier key THROWS. `durabilityCriticalCallees` reads the guard's table through the guard's discovered family, once per process, and throws loudly when the gate or the table is gone. Plus the `'durability-seam'` binding and the `maskCommentsAndLiterals` import. | | `scripts/pm/dispatch-gates.self-test.mjs` | 43 cases: 7 for the reader, 2 for the vocabulary refusals, 16 for the predicate limbs through an injected vocabulary, 5 on the live tree (specimen, control, discrimination held at a tenth), and 13 for the rendered section, table, runnable union and STALE. | | `scripts/check-durability-degradation-log-level.mjs` | Comment only: a docblock section naming the second reader of `DURABILITY_CRITICAL_CALLEES`. ⛔ Detection logic untouched. The guard is not narrowed or weakened. | **What the predicate matches.** A file matches when it is non-test TypeScript (the precedents' extension set, without `.d.ts`), holds a catch clause (a promise `.catch(` is not one), and spells one of the guard's declared callee names as a whole word. All three are read from the comment-masked source. The names are read from the guard's own source and are never copied, so a callee added there lands in this net on the next run. ## Measured: why this net and not the PM's catch-only guess (16abea8, 3441 non-test TS files) | predicate | files | covers the 28 files holding the guard's 46 log-level seams | precision | fires on the last 300 first-parent commits on main | |---|---|---|---|---| | catch clause alone | 705 (20.5%) | 28 of 28 | 4.0% | 169 (56.3%) | | catch clause + declared callee (this PR) | 43 (1.2%) | 28 of 28 | 65.1% | 72 (24.0%) | The commit column judges today's content of the paths each commit touched. The catch-only net fires for 83% of the commits that touch package TypeScript at all. That is the REFUSE-WIDE trade under another name, so this PR declines it. The guard takes about 5.7 s of wall time on this tree (two runs: 5.82 s and 5.69 s) and needs no build. That is the price of a wasted run, against a CI round trip for a miss. What it cannot see, and says so in its docblock: - A file with no content on disk answers false, as with both precedents. - A diff that deletes the last declared call from a function named in `FAILURE_PROPAGATION_SITES` strands that entry. One such entry sits outside the read-seam roots today. ## Before / after `--commands` (`--repo objectstack-ai/objectstack`) - `packages/runtime/src/domains/automation.ts`: **47 at 16abea8 → 48 at 7195bc6 and at 67a8400**. The one added line is `pnpm check:durability-log-level`; the control `route-envelope` stays at 1. - Control `packages/services/service-storage/src/storage-routes.ts` holds catch clauses and no declared callee: **51 → 51**, byte-identical, `durability` 0. ## Reverse verification (one ablation, at head 7195bc6) `node scripts/ablation-replace.mjs` swapped the binding `'durability-seam': guardsADurabilityCriticalCall` for `'durability-seam': () => false`. The anchor went 1 → 0 and the replacement 0 → 1. Under that mutation: - `--commands` on `automation.ts` printed 47 commands, with `durability` 0. - `dispatch-gates --self-test --fast` exited 1, with **10 of 1884 cases failed**. Among them: "through the whole table, the specimen derives the durability guard" and "the runnable union carries the durability guard for the specimen". The restore was proven: blob after restore `c4656786dbae` equals the HEAD blob, and `git diff HEAD` was empty. Unmutated, the same battery reads 1884 of 1884. ## Population declaration (mechanism note A5): not added The acceptance does not need it, and it would not hold: - A `wide-population` marker moves a family into the declared column, which is outside `--commands`. - `widePopulationRefusal` refuses a marker on a gate that names paths its reason does not account for, and this guard names its three read-seam roots. - `bare-root-worklist.mjs` cannot discover the `join(ROOT, 'packages')` call spelling, so its self-test owes no row. It stayed green: 82 live rows, 47 recorded verdicts, none stale or missing. ## Rider: #23426 (prose only; ⛔ the value line does not move) The first paragraph of the `CONTRACT_REVIEW_TIER` docblock, **before**: > The single source of truth for the model tier the PM lane's governance reads — clause ②'s CONTRACT-REVIEW tier: the tier the clause-② REVIEW runs at, both halves of it — the spec and skills lanes' review of every round they deliver (a card that WIDENS a published accept set or the public surface is spec-lane work, whichever seat found it; a card that only NARROWS one is not, and owes one contract-review-tier review before the queue while staying in its lane) and the `Served-tier:` line every `## Contract review` record carries. The BUILD of such a card is at the default judgment tier, so this constant is a review tier and never a dispatch mandate. **After**: > The single source of truth for the model tier the PM lane's governance reads — clause ②'s CONTRACT-REVIEW tier: the tier a contract review runs at when the face that made it owed is a published schema or governed rules text, and the `Served-tier:` line that review's `## Contract review` record carries. The review is owed by SURFACE, never by lane, and served at the tier of the face that made it owed (ruling record 6159870311, letter B′ with C): `reviewFace` in `record-recognisers.mjs` holds the mapping, and a review owed ONLY by a `Clause-②: yes` declaration is served at `TIER_DEFAULT` by the lane seat, as its own review of record. No lane names this tier. As the `--tier` note spells it, a card that WIDENS a published accept set or the public surface is spec-lane work, whichever seat found it; a card that only NARROWS one is `Clause-②: no`, stays in its lane, and owes one contract-review-tier review before the queue. The BUILD of such a card is at the default judgment tier, so this constant is a review tier and never a dispatch mandate. The rest of the docblock is unmoved: the retirement rule and the quoted rulings. Rider acceptance, at head 7195bc6 (the fast tier and `check:pm-dispatch-gates` re-read at 67a8400): - `grep -c "spec and skills lanes" scripts/pm/dispatch-gates.mjs` prints 0. - `node scripts/pm/record-recognisers.mjs --self-test` exits 0 (165 cases). - The `dispatch-gates` self-test, fast tier, exits 0 (1884 cases). - `pnpm check:pm-dispatch-gates` exits 0. - Both tiers exit 0 at 7195bc6 (2144 cases), as required when the data file is touched. #23426 remains open; no keyword in this body acts on it. The seat decides whether this landing discharges it. ## Gates (local, final head 67a8400: 7195bc6 merged with `origin/main` 8d05456, whose 4 incoming commits touch none of these files) Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the merge base: 32 commands, the same list at both heads. All 32 were run at 67a8400, each exit code captured before any pipe. `--ran` reconciles: 32 derived, 32 run, 0 NOT-MEASURED. - **31 exited 0:** `check:` `agent-test-spelling`, `bash32-floor`, `cli-command-ids`, `cross-package-test-inputs`, `declared-population-live`, `driver-memory-census`, `durability-log-level`, `entry-guard`, `gitlink-declared`, `nul-bytes`, `parse-guard`, `pnpm-filter-targets`, `ratchet-remedy-authority`, `refd-timer-probe`, `swallow-census-controls` and `watch-hint-literal`, plus `bare-root-worklist.mjs --self-test`, `check-ci-filter-parity`, `check-comment-mask-corpus`, and the run and `--self-test` modes of `check-closing-keyword-parity`, `check-declaration-mirrors`, `check-scripts-symbol-anchors`, `check-self-test-wired`, `check-self-test-workflow-commands` and `check-whole-set-label-write`. - **`pnpm check:pm-dispatch-gates` exited 0** on its fast tier, the tier CI runs on a PR: 1884 cases, 305 s at 67a8400. Both tiers (`--slow`) also exited 0 at 7195bc6: 2144 cases, 1196 s. They were not re-run after the merge, which moved none of these files. `pnpm exec eslint --no-inline-config` on the four changed files exits 0. The control-byte self-scan is empty. Every family CI runs is read from this PR's check-runs. ## Acceptance notes - **Out of scope, reported to the seat.** A diff to the guard's own log-level baseline, `scripts/durability-degradation.baseline.json`, still derives no `check:durability-log-level`; `--commands` on that path prints 0. The guard spells that path as `join(ROOT, 'scripts', 'durability-degradation.baseline.json')`, which builds no hint, while its sibling read-invention baseline has a whole-string constant and does reach it. - **Not fixed here.** The fix is a one-line whole-string constant in the guard, which is outside this claim's surface (the guard is comment-only here). --- _Generated by [Claude Code](https://claude.ai/code/session_01ESFcvW3staenyBCGq7kcDH)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent b1e9280 commit 9a6f790

4 files changed

Lines changed: 322 additions & 8 deletions

File tree

‎scripts/check-durability-degradation-log-level.mjs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,14 @@ const READ_INVENTION_BASELINE_PATH = join(
318318
* choice is between paying for those 36 and giving this map a per-site scope
319319
* the way `FAILURE_PROPAGATION_SITES` already scopes the other vocabulary. That
320320
* is a design call, and it is deliberately not taken here.
321+
*
322+
* ## A second reader: the dispatch derivation (#23584)
323+
*
324+
* `scripts/pm/dispatch-gates.mjs` reads this table's KEYS, by its name, to tell
325+
* a card whose file holds a catch and one of these callees to run this gate
326+
* (`guardsADurabilityCriticalCall`). A key added here reaches that lead with
327+
* nothing else to edit. Renaming the table, or spelling a key as anything but
328+
* a quoted word, makes that tool refuse loudly until its reader moves too.
321329
*/
322330
const DURABILITY_CRITICAL_CALLEES = new Map([
323331
[

‎scripts/pm/dispatch-gates.data.mjs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,15 @@ export const ESCAPABLE_LITERAL_LEDGER_ROWS = Object.freeze([
866866
* row moving off REFUSE-WIDE, which takes a ruling. ⛔ Growing the gate's
867867
* derivation rules does NOT qualify, and ⛔ neither does this predicate
868868
* going quiet on a card, for the reason given above.
869+
* - durability-seam entry: when the durability guard's own source declares
870+
* the population its log-level rule walks in a form this derivation can
871+
* read, the ordinary path match names it and this entry is redundant. Today
872+
* that population is the bare package root, so it would take a ruling that
873+
* moves that root off the wide-population channel. ⛔ Growing the guard's
874+
* DURABILITY_CRITICAL_CALLEES does NOT qualify: the predicate already reads
875+
* that table, so a new callee widens this entry's net and does nothing to
876+
* let a path name the gate. ⛔ Neither does this predicate going quiet on a
877+
* card, for the reason given under the error-code entry.
869878
* - root-program entry: when the gate's own source names its root population
870879
* in a form this derivation can read — a positive literal, or a generated
871880
* manifest of the resolved program — the ordinary path match names it and
@@ -990,6 +999,16 @@ export const CHANGE_KIND_ROWS = [
990999
},
9911000
],
9921001
},
1002+
{
1003+
kind: 'adds or edits a file holding a catch clause and a durability-critical callee the durability guard declares (judged from CONTENT — no path derivation can name this gate)',
1004+
matches: 'durability-seam',
1005+
gates: [
1006+
{
1007+
name: 'check:durability-log-level',
1008+
why: 'it walks every non-test TypeScript source under the package root for a catch whose try calls an operation it declares durability-critical (a write, a DDL step, a store initialisation: the names in its DURABILITY_CRITICAL_CALLEES table, which this lead reads rather than copies), and fails when that catch logs below error without rethrowing or handing the failure to the caller. A counter-guarded summary that reaches error only through an optional call fails it too, and so does a declared propagation site that no longer excuses anything. This is a gate no path derivation can name for most of that population: its log-level walk is the bare package root, which the wide-population ruling keeps out of every matched column, and the only roots it spells are the three its read-seam rule walks, so it scored silent for every other card and PR #23567 paid a CI round trip for that silence (#23584). It needs NO build, being one AST pass over the whole tree in about six seconds, and it names the file, the catch line and the guarded callee. Repair by logging at error with the CONSEQUENCE and the FIX in the first line, by rethrowing, or by handing the failure to the caller through a callee its propagation vocabulary declares, which the checker then proves every path out of the catch reaches. ⛔ Never by `logger.error?.(…)`, which prints nothing against a sink with no error, and ⛔ never by baselining correct code: an entry in its shrink-only baseline means a real unfixed degradation. ⚠ This lead fires on any file holding both a catch clause and a declared callee name, not only one whose seam changed, for the trade the vocabulary entry above states: the wasted run is one cheap gate and the miss is a CI round trip',
1009+
},
1010+
],
1011+
},
9931012
];
9941013

9951014
/**

‎scripts/pm/dispatch-gates.mjs‎

Lines changed: 180 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ import {
446446
// this constant, so a repo that ever renumbered the class would move this tool
447447
// with it instead of leaving a stale 3 behind in a second copy (#17204).
448448
import { EXIT_PREREQUISITE_NOT_MET } from '../import-prerequisite.mjs';
449-
import { blank, maskComments, scanSource } from '../js-comment-mask.mjs';
449+
import { blank, maskComments, maskCommentsAndLiterals, scanSource } from '../js-comment-mask.mjs';
450450
import { invokedAs, isEntrypoint } from '../invoked-as.mjs';
451451
// The human-merge line threshold is declared ONCE, in the landing gate; this
452452
// tool prints the same reading at dispatch time and never carries a second copy.
@@ -10571,6 +10571,172 @@ export function emitsAnHttpStatus(path, readSource = readTrackedSource) {
1057110571
return STATUS_STAMP_POSITION.test(masked) || STATUS_DOOR_CALL.test(masked) || STATUS_CONSTANT_BINDING.test(masked);
1057210572
}
1057310573

10574+
/**
10575+
* Does this file hold a `try`/`catch` AND spell a durability-critical callee
10576+
* the durability guard declares? This is the content trigger for
10577+
* `check:durability-log-level` (#23584), and the third CONTENT predicate,
10578+
* beside the code and status predicates above.
10579+
*
10580+
* ## Why no path derivation names this gate
10581+
*
10582+
* The guard runs two rules. Its read-seam rule walks three package source
10583+
* roots that it spells as literals, so a card touching those already reaches
10584+
* the matched column by path. Its log-level rule walks the bare package root
10585+
* and admits every non-test `.ts` source under it: 3206 tracked files at
10586+
* 16abea884, which is 34.8% of the files under that root. Four sibling gates
10587+
* walk that root the same way, and the bare-root ledger records each of them
10588+
* REFUSE-WIDE in `CENSUS_REFUSE_WIDE`. The wide-population ruling (decision
10589+
* batch #43) keeps such a root out of the matched column and out of
10590+
* `--commands`. So,
10591+
* for a file outside the three read-seam roots, the guard scored `silent` on
10592+
* every card. PR #23567 paid a CI round trip for that silence, on the
10593+
* `saveMetaItem` seam in the runtime's automation domain.
10594+
*
10595+
* ## What it matches, and why the vocabulary is READ rather than copied
10596+
*
10597+
* A log-level finding needs two things in ONE file. The first is a catch
10598+
* clause. The second is a call, in that clause's `try`, that resolves to a name
10599+
* in the guard's `DURABILITY_CRITICAL_CALLEES`. The guard resolves that call
10600+
* through the call's own spelling, a `const` alias or a `.call`/`.apply`/`.bind`
10601+
* adapter, and every one of those spells the name somewhere in the same file. So
10602+
* the two limbs read those two facts, and neither one grades the catch:
10603+
*
10604+
* - CATCH CLAUSE: the keyword followed by its binding or its block. A promise
10605+
* `.catch(` is not a clause and the guard never judges one.
10606+
* - VOCABULARY: any declared callee name, as a whole word, anywhere in the
10607+
* comment-masked source. An import specifier and a `const` alias count.
10608+
*
10609+
* The names come from the guard's own source, from the file its discovered
10610+
* family resolves to. They are never mirrored here. A callee added to that
10611+
* table is in this net on the next run, and nothing here has to move. The read
10612+
* keys on the table's NAME. A guard that renames the table, or stops spelling
10613+
* its keys as quoted words, makes this THROW, naming both. That is the loud
10614+
* direction, the same contract `rootTsconfigExcludeEntries` states. The read
10615+
* runs only for a non-test TypeScript path that already holds a catch clause.
10616+
* A card touching no such file never pays for it and never meets the throw.
10617+
*
10618+
* ## Measured, so a widening or narrowing has a number to beat
10619+
*
10620+
* At 16abea884, over 3441 non-test TypeScript files:
10621+
*
10622+
* - THIS predicate matches 43 (1.2%). Every one is inside the guard's own
10623+
* population. The guard's `--list` reports 46 log-level seams in 28 files,
10624+
* and all 28 match, so recall over what this rule can fail on is complete.
10625+
* 28 of the 43 hold a seam (65%).
10626+
* - A catch clause ALONE matches 705 (20.5%), at 4% precision. Over the last
10627+
* 300 first-parent commits on main, judged against today's content of the
10628+
* paths each touched, it fires for 169 (56.3%) against 72 (24.0%) for this
10629+
* predicate. That is the REFUSE-WIDE trade under another name, so this
10630+
* entry declines it.
10631+
*
10632+
* The false-positive trade is the code predicate's. One wasted run costs about
10633+
* six seconds of a gate that needs no build. A miss costs a CI round trip.
10634+
*
10635+
* ## What it cannot see, said out loud
10636+
*
10637+
* A file with nothing on disk answers false, as in both predicates above. ⛔
10638+
* Do not close that by falling back to a path. One more shape can red the guard
10639+
* with no vocabulary name left in the file: a diff that deletes the last
10640+
* declared call from a function named in `FAILURE_PROPAGATION_SITES`, which
10641+
* leaves that entry stale. One such entry sits outside the read-seam roots
10642+
* today. The edit that strands it removes the very token this predicate reads,
10643+
* and the guard's own failure text names the entry to delete.
10644+
*/
10645+
export const DURABILITY_SEAM_GATE = 'check:durability-log-level';
10646+
const DURABILITY_CALLEE_TABLE = 'DURABILITY_CRITICAL_CALLEES';
10647+
const DURABILITY_CATCH_CLAUSE = /(?<!\.\s*)\bcatch\s*[({]/;
10648+
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
10649+
10650+
/**
10651+
* The KEYS of `const NAME = new Map([...])` in a module's source, in
10652+
* declaration order. Null when the module declares no such table.
10653+
*
10654+
* Bracket depth is counted on the comment-and-literal mask, so a bracket
10655+
* inside a reason string or a comment opens nothing. Each tuple's key is then
10656+
* read out of the ORIGINAL source between its quote delimiters, which that mask
10657+
* keeps. A tuple whose key is not a quoted identifier THROWS: the table's shape
10658+
* has moved, and returning fewer names would narrow the predicate silently.
10659+
*
10660+
* @param {string} source
10661+
* @param {string} name
10662+
* @returns {string[] | null}
10663+
*/
10664+
export function declaredMapKeys(source, name) {
10665+
const code = maskCommentsAndLiterals(source);
10666+
const decl = new RegExp(`\\b(?:const|let|var)\\s+${name}\\s*=\\s*new\\s+Map\\s*\\(\\s*\\[`).exec(code);
10667+
if (!decl) return null;
10668+
const keys = [];
10669+
let depth = 1;
10670+
for (let i = decl.index + decl[0].length; i < code.length && depth > 0; i++) {
10671+
const c = code[i];
10672+
if (c === ']' || c === ')' || c === '}') {
10673+
depth -= 1;
10674+
continue;
10675+
}
10676+
if (c !== '[' && c !== '(' && c !== '{') continue;
10677+
depth += 1;
10678+
if (c !== '[' || depth !== 2) continue;
10679+
const lead = /^\s*(['"`])/.exec(code.slice(i + 1, i + 200));
10680+
const close = lead ? code.indexOf(lead[1], i + 1 + lead[0].length) : -1;
10681+
const key = close > 0 ? source.slice(i + 1 + lead[0].length, close) : null;
10682+
if (key === null || !IDENTIFIER.test(key)) {
10683+
throw new Error(
10684+
`dispatch-gates: an entry of ${name} has no quoted identifier as its key ` +
10685+
`(${JSON.stringify(source.slice(i, i + 60))}), so its callee names cannot be read`,
10686+
);
10687+
}
10688+
keys.push(key);
10689+
}
10690+
return keys;
10691+
}
10692+
10693+
/**
10694+
* The guard's declared durability-critical callee names, read once per process
10695+
* from the file `DURABILITY_SEAM_GATE`'s discovered family resolves to.
10696+
*/
10697+
let durabilityCallees = null;
10698+
export function durabilityCriticalCallees(families = null) {
10699+
if (!families && durabilityCallees) return durabilityCallees;
10700+
const entry = (families ?? discoverFamilies()).byCheck.get(DURABILITY_SEAM_GATE);
10701+
if (!entry) {
10702+
throw new Error(
10703+
`dispatch-gates: no workflow runs ${DURABILITY_SEAM_GATE} any more, so the durability-seam change kind cannot read ` +
10704+
`${DURABILITY_CALLEE_TABLE}. Re-point DURABILITY_SEAM_GATE at the gate's new name, or delete the kind with the gate`,
10705+
);
10706+
}
10707+
const names = [];
10708+
for (const file of entry.files ?? []) {
10709+
const source = readTrackedSource(file);
10710+
const keys = source === null ? null : declaredMapKeys(source, DURABILITY_CALLEE_TABLE);
10711+
if (keys) names.push(...keys);
10712+
}
10713+
if (names.length === 0) {
10714+
throw new Error(
10715+
`dispatch-gates: ${DURABILITY_SEAM_GATE} resolves to ${(entry.files ?? []).join(', ') || 'no file'}, and none of them ` +
10716+
`declares a non-empty ${DURABILITY_CALLEE_TABLE} map, so the durability-seam change kind has no vocabulary to read. ` +
10717+
'The table was renamed or reshaped: move this reader with it',
10718+
);
10719+
}
10720+
if (!families) durabilityCallees = names;
10721+
return names;
10722+
}
10723+
10724+
export function guardsADurabilityCriticalCall(path, readSource = readTrackedSource, callees = durabilityCriticalCallees) {
10725+
if (!/\.[cm]?tsx?$/.test(path) || /\.d\.[cm]?ts$/.test(path)) return false;
10726+
if (isTestFilePath(path)) return false;
10727+
const source = readSource(path);
10728+
if (source === null || source === undefined) return false;
10729+
// Masked for the code predicate's reason: a catch or a callee DISCUSSED in
10730+
// prose is neither a clause nor a call.
10731+
const masked = maskedComments(source);
10732+
if (!DURABILITY_CATCH_CLAUSE.test(masked)) return false;
10733+
const names = callees();
10734+
// An empty alternation matches the empty string at every word boundary, so
10735+
// an empty vocabulary must answer false here rather than reach every file.
10736+
if (names.length === 0) return false;
10737+
return new RegExp(`\\b(?:${names.map((n) => n.replace(/\$/g, '\\$')).join('|')})\\b`).test(masked);
10738+
}
10739+
1057410740
/**
1057510741
* Is this path inside the ROOT package's tsc program — the population behind
1057610742
* the `@objectstack/spec-monorepo` entry of `check:type-check-debt`?
@@ -10846,6 +11012,7 @@ const CHANGE_KIND_PREDICATES = Object.freeze({
1084611012
'root-ts-program': (path) => isInRootTsProgram(path, rootTsProgramExcludedDirs()),
1084711013
'error-code-literal': stampsAnErrorCodeLiteral,
1084811014
'http-status-emit': emitsAnHttpStatus,
11015+
'durability-seam': guardsADurabilityCriticalCall,
1084911016
});
1085011017

1085111018
export function bindChangeKindRows(rows, predicates = CHANGE_KIND_PREDICATES) {
@@ -11619,13 +11786,18 @@ export function residueLines(
1161911786

1162011787
/**
1162111788
* The single source of truth for the model tier the PM lane's governance
11622-
* reads — clause ②'s CONTRACT-REVIEW tier: the tier the clause-② REVIEW runs
11623-
* at, both halves of it — the spec and skills lanes' review of every round
11624-
* they deliver (a card that WIDENS a published accept set or the public
11625-
* surface is spec-lane work, whichever seat found it; a card that only
11626-
* NARROWS one is not, and owes one contract-review-tier review before the
11627-
* queue while staying in its lane) and the `Served-tier:` line every
11628-
* `## Contract review` record carries. The
11789+
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a contract review runs at
11790+
* when the face that made it owed is a published schema or governed rules
11791+
* text, and the `Served-tier:` line that review's `## Contract review` record
11792+
* carries. The review is owed by SURFACE, never by lane, and served at the
11793+
* tier of the face that made it owed (ruling record 6159870311, letter B′
11794+
* with C): `reviewFace` in `record-recognisers.mjs` holds the mapping, and a
11795+
* review owed ONLY by a `Clause-②: yes` declaration is served at
11796+
* `TIER_DEFAULT` by the lane seat, as its own review of record. No lane names
11797+
* this tier. As the `--tier` note spells it, a card that WIDENS a published
11798+
* accept set or the public surface is spec-lane work, whichever seat found
11799+
* it; a card that only NARROWS one is `Clause-②: no`, stays in its lane, and
11800+
* owes one contract-review-tier review before the queue. The
1162911801
* BUILD of such a card is at the default judgment tier, so
1163011802
* this constant is a review tier and never a dispatch mandate. Declared HERE
1163111803
* and only here, as a constant, so a model upgrade is a one-line change in one

0 commit comments

Comments
 (0)