feat(devx): measure-self-test-floor reports each self-test's verdict handshake, derived from code (#14968) - #15799
Conversation
`classifyHandshake` reads the masked, line-anchored source and returns `sentinel` / `flag` / `helper` / `none` from the MECHANISM -- the value the self-test hands back and the comparison that consumes it -- rather than from any of the three landed names. `definitionSpan` brace-counts the body instead of taking the first column-0 `}`, which three files close early inside an inline arrow argument. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…ss check (#14968) `--json` and the human census gain a per-file `handshake` field beside `floor`, plus a per-shape summary line. One fixture per landed shape drives the recogniser in both directions, including the ACCIDENT boundary (a return value compared against a LITERAL is not a handshake), a decoy whose spellings sit in a comment and a fixture template, and a name-independence control that renames all three landed spellings out of the fixtures and requires the same verdicts. `handshakeCensusFailures` measures the census's completeness against a second opinion that shares no code with the recogniser, over the live tree rather than over fixtures -- so it lives outside `runControls()`, whose invariant is that a control failure is always the instrument and never the tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
|
ACCEPT — PR #15799 (head As ruled: one recogniser ( Measured by this seat: branch blob at #13799's split reads 9 dispatchable / 5 blocked of 14 floor-NONE files from Flip + arm when both required jobs report success on Generated by Claude Code |
|
Flipped ready + auto-merge enabled (08:52Z, Generated by Claude Code |
Fixes #14968
There was no canonical way to ask whether a self-test has a verdict handshake. The repair had
landed in three spellings,
measure-self-test-floorreported none of them, and every handshakequestion in this family was answered by a hand-written grep whose completeness nobody could check.
scripts/measure-self-test-floor.mjsnow reports a per-filehandshakebesidefloor, in--jsonand in the human census, from one recogniser —classifyHandshake— that everycaller reads. A fifth shape is a change there and in its second opinion, and nowhere else.
Derived from code, not from a name
classifyHandshakereads the masked, line-anchored source (the textselfTestDefsand theinjection anchor already read). The three landed names —
SELF_TEST_VERDICT,selfTestReachedVerdict,requireReachedVerdict— appear nowhere in it. What it reads isthe value the self-test hands back and the comparison that consumes it.
A control renames all three spellings out of the fixtures and requires the same verdicts:
classifyFloorkeyed on the NAMESELF_TEST_BATTERIESonce and called a fixture floored afterits roster had been removed.
The shapes, one real carrier each (line numbers on 76e878c)
sentinelscripts/ablation-dist-preflight.mjs:843—if (selfTest() !== SELF_TEST_VERDICT) {; the awaited form atscripts/check-aggregator-roster.mjs:718flagtrueINSIDE the self-test, read negated OUTSIDE itscripts/check-settings-bind-window.mjs— declared:739, set:1053, read:1061helperscripts/check-platform-checklist.mjs:807(the helper),:1636(a call)nonescripts/audits/14744-before-update-per-row-value-census.mjs:551—process.exit(runSelfTest() === 0 ? 0 : 1)The bare-call shape the triage found, classified precisely
Triage named a fourth spelling: the bare
selfTest();incheck-settings-bind-window.mjs. Readfrom code, it is not a fourth handshake — it is a dispatch spelling, and it is orthogonal
to the handshake. That file's dispatch is a bare call at
:1060and its handshake is the flag,read one line later at
:1061. The recogniser classifies itflag, which is what it is. Acensus that treated the dispatch spelling as the handshake shape would have invented a category.
That is why
noneis decided by what consumes the completion and never by how the call iswritten:
HOLED_GATEalso dispatches with a bareselfTest();and correctly readsnone.The ACCIDENT boundary, which is the sharpest thing here
runSelfTest() === 0is a comparison of the return value and is not a handshake: over aself-test that returned early it is
undefined === 0→ false → exit 1, having printed zero bytes.Nothing noticed anything. The compared operand must be an IDENTIFIER, never a literal — the same
boundary
PRODUCES_FAILURE_TERNARY_EXITalready draws. Controlled in both directions.Live counts on this base, and the card's
Census population (
scripts/**dispatching on--self-test, this instrument excluded): 181.sentinelflaghelpernone⭐ The card's own grep was already incomplete, and by more than the drift. Its sentinel row is
spelled
if (selfTest() !== SELF_TEST_VERDICT). Re-run literally onc99449ab5, repo-wide, thatfinds 67 files. The looser
!== SELF_TEST_VERDICTfinds 88 — the difference is 19 filesthat spell it
if ((await selfTest()) !== SELF_TEST_VERDICT)and are invisible to the spellingthe card wrote down. The artefact filed to complain about incomplete handshake greps was itself
computed with one. That is the card's thesis, measured on the card.
requireReachedVerdictalso greps 2 repo-wide today, not 1: the second is a fixture STRING insidethis instrument, landed by #15758. The census reads masked source, so it is correctly not a carrier.
Census completeness — measured, not asserted
handshakeCensusFailuresruns over the LIVE tree on every invocation and compares the files therecogniser calls non-
noneagainst a second opinion —LANDED_HANDSHAKE_NAMES, the three greps aseat actually wrote — that shares no code with it. They agree on all 181 rows, file for file.
Disagreement in either direction refuses; the census is never printed from the smaller of two
disagreeing populations.
⛔ It deliberately does not live in
runControls(), whose stated invariant is that nothingthere reads the repo so a control failure is always the instrument. This one can fail because of
what landed in the tree, which is the point.
Body extent: three live files were reading
nonedefinitionSpancounts braces over masked text. With the tree's usual "body ends at the first}at column 0" rule,
scripts/check-error-code-casing.mjscloses an inline arrow argument with});at column 0 inside
selfTest, 107 lines early — so the flag it sets as its last act fallsoutside its own body, carries nothing, and the file reads
none. Same forcheck-optional-error-sink-contract.mjsandcheck-org-identifier.mjs. All three carry perfectlyordinary handshakes.
EARLY_BRACE_GATEpins it, with a fixture-validity guard that the column-0brace really stands ahead of the assignment.
The consumer: #13799 is now machine-answerable
Yes — the per-file rule ("#13798 first, per file") is answerable from
--jsonalone, with no grep:The split this instrument gives on 76e878c is 9 dispatchable / 5 blocked, out of 14 files⚠️ That is not the card's 69/12 and is not meant to be: the card's
whose floor is
NONE.numbers were hand-computed weeks of floor repairs ago, when the
NONE-floor population was farlarger. It is now 14. The number that transfers is the method, not the split.
Blocked (floor
NONE, no handshake):scripts/audits/14744-before-update-per-row-value-census.mjs,check-step-collectors.mjs,measure-durability-swallow-family.mjs,platform-object-tenancy-census.mjs,setup-git-hooks.mjs.All seven
nonerows were hand-read: two are inline top-level blocks with no callee to leave, twohand the return value straight to
process.exit, one is the ACCIDENT ternary, andplatform-object-tenancy-census.mjsdispatches on the flag only to refuse it.Verification
Static census, before/after — base
c99449ab5in a separate worktree vs head, exit capturedbefore any pipe, both
EXIT=0:file rows + a blank). Nothing existing moved.
--json: parsed and compared row by row. Keys basedefs,file,floor→ headdefs,file,floor,handshake. Rows whose pre-existing fields changed: 0. The raw line diff(181 removed / 362 added) is entirely the trailing-comma churn from
defsno longer being last.Ablation — trap-guarded, restore proven by blob hash, both legs.⚠️ The first attempt was a
no-op:
perl -0pifailed to compile the replacement and exited without editing. The on-diskcheck caught it (
anchor after mutation (must be 0): 1), the reading was discarded as void, andthe leg was re-run with an exact editor. Reported rather than quietly retried.
Leg 1 — drop the
flagbranch of the recogniser (return flag ? 'flag' : 'none'→return 'none').Anchor 1 → 0, marker 1 on disk. CLI
EXIT=1:and the live check, reached by direct import because
main()refuses at the controls first:live completeness failures: 1—85 file(s) carry a landed handshake spelling the recogniser reads as none: ...Leg 2 — drop the flag regex from
LANDED_HANDSHAKE_NAMES. The fixture controls are untouched, sothis reaches the live check through the CLI.
EXIT=1:Restore after each leg:
git diff HEADempty, andgit hash-object== the HEAD blobb9d6d38dcfe092071bc236c66a22fecbb008ae7e.Gates.
node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack→
EXIT=0, 26 commands, all run. The first derivation warnedSTALE TREEand namedscripts/js-comment-mask.mjs— a file this one imports — soorigin/mainwas merged in(
fa8575996) and the list re-derived clean. The census counts are identical before and after thatmerge.
check-declaration-mirrors(+--self-test),check-self-test-wired(+
--self-test),check-self-test-workflow-commands(+--self-test),check-whole-set-label-write(+--self-test),check:nul-bytes,check:entry-guard,check:parse-guard,check:pnpm-filter-targets,check:cross-package-test-inputs.check-comment-mask-adoptionalso run (not derived):OK — 14 private comment-stripper(s) ... A new one reds here.node scripts/pm/check-governed-merges.mjs --test scripts/measure-self-test-floor.mjs→EXIT=0,✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.NOT MEASURED, by name — all six are dependency absence in a worktree with no
pnpm install,never a gate verdict:
packages/lint/scripts/check-reference-carrier-shape.mjs(and--self-test) — exit 3,ts-parse: PREREQUISITE NOT MET — the dependency typescript is not installedscripts/check-ci-filter-parity.mjs— exit 3,PREREQUISITE NOT MET — the dependency yaml is not installedscripts/check-closing-keyword-parity.mjs— exit 3, same reasonpnpm check:driver-memory-census— exit 3,ts-parse: PREREQUISITE NOT MET — typescriptscripts/check-comment-mask-corpus.mjs— exit 1,ERR_MODULE_NOT_FOUND '@typescript-eslint/parser';a module-resolution failure before the gate body, read as NOT MEASURED, not as red.
CI runs all six with a real install.
dispatch-gatesalso reports 16 families that apply once a changeset exists. There is none, onpurpose — see below.
Changeset
None.
skip-changesetapplies, per AGENTS.md: that label "is for a diff that publishes nothingfrom any released package." The diff is exactly one file,
scripts/measure-self-test-floor.mjs,a repo-root tooling script;
git diff --name-only origin/main...HEADoutsidescripts/is empty.Scope
⛔ No spelling was unified in the tree, no gate script was touched, and
--probewas not run.The instrument recognises the shapes; it does not legislate them — the flag form exists because
those self-tests' own exit codes are load-bearing, so the handshake cannot be the return value.
One adjacent observation, already filed: this file still carries its own
maskCommentsAndLiteralsrather than importing the one #15774 published injs-comment-mask.mjs.That is #15776 (open,
finding,pm:blocked), which names this file at:258explicitly. Nottouched here: converting it would change the masking behaviour underneath the very counts this PR
publishes, and it belongs to that card.
🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code