Skip to content

feat(devx): measure-self-test-floor reports each self-test's verdict handshake, derived from code (#14968) - #15799

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-14968-handshake-field
Sep 5, 2026
Merged

feat(devx): measure-self-test-floor reports each self-test's verdict handshake, derived from code (#14968)#15799
baozhoutao merged 3 commits into
mainfrom
claude/issue-14968-handshake-field

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

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-floor reported none of them, and every handshake
question in this family was answered by a hand-written grep whose completeness nobody could check.

scripts/measure-self-test-floor.mjs now reports a per-file handshake beside floor, in
--json and in the human census, from one recogniser — classifyHandshake — that every
caller reads. A fifth shape is a change there and in its second opinion, and nowhere else.

Derived from code, not from a name

classifyHandshake reads the masked, line-anchored source (the text selfTestDefs and the
injection anchor already read). The three landed names — SELF_TEST_VERDICT,
selfTestReachedVerdict, requireReachedVerdict — appear nowhere in it. What it reads is
the 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:
classifyFloor keyed on the NAME SELF_TEST_BATTERIES once and called a fixture floored after
its roster had been removed.

The shapes, one real carrier each (line numbers on 76e878c)

shape mechanism carrier
sentinel the RETURN VALUE compared against a NAMED operand scripts/ablation-dist-preflight.mjs:843if (selfTest() !== SELF_TEST_VERDICT) {; the awaited form at scripts/check-aggregator-roster.mjs:718
flag a module-level binding set true INSIDE the self-test, read negated OUTSIDE it scripts/check-settings-bind-window.mjs — declared :739, set :1053, read :1061
helper the same flag HANDED to a callee that refuses on its behalf scripts/check-platform-checklist.mjs:807 (the helper), :1636 (a call)
none nothing consumes the completion scripts/audits/14744-before-update-per-row-value-census.mjs:551process.exit(runSelfTest() === 0 ? 0 : 1)

The bare-call shape the triage found, classified precisely

Triage named a fourth spelling: the bare selfTest(); in check-settings-bind-window.mjs. Read
from 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 :1060 and its handshake is the flag,
read one line later at :1061. The recogniser classifies it flag, which is what it is. A
census that treated the dispatch spelling as the handshake shape would have invented a category.

That is why none is decided by what consumes the completion and never by how the call is
written: HOLED_GATE also dispatches with a bare selfTest(); and correctly reads none.

The ACCIDENT boundary, which is the sharpest thing here

runSelfTest() === 0 is a comparison of the return value and is not a handshake: over a
self-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_EXIT already 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.

shape this PR the card's table
sentinel 88 82
flag 85 77
helper 1 1
none 7
carrying any 174 of 181 160 union

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 on c99449ab5, repo-wide, that
finds 67 files. The looser !== SELF_TEST_VERDICT finds 88 — the difference is 19 files
that spell it if ((await selfTest()) !== SELF_TEST_VERDICT)
and are invisible to the spelling
the 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.

requireReachedVerdict also greps 2 repo-wide today, not 1: the second is a fixture STRING inside
this instrument, landed by #15758. The census reads masked source, so it is correctly not a carrier.

Census completeness — measured, not asserted

handshakeCensusFailures runs over the LIVE tree on every invocation and compares the files the
recogniser calls non-none against a second opinion — LANDED_HANDSHAKE_NAMES, the three greps a
seat 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 nothing
there 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 none

definitionSpan counts braces over masked text. With the tree's usual "body ends at the first }
at column 0" rule, scripts/check-error-code-casing.mjs closes an inline arrow argument with });
at column 0 inside selfTest, 107 lines early — so the flag it sets as its last act falls
outside its own body, carries nothing, and the file reads none. Same for
check-optional-error-sink-contract.mjs and check-org-identifier.mjs. All three carry perfectly
ordinary handshakes. EARLY_BRACE_GATE pins it, with a fixture-validity guard that the column-0
brace 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 --json alone, with no grep:

rows.filter(r => r.floor === 'NONE' && r.handshake !== 'none')   // dispatchable
rows.filter(r => r.floor === 'NONE' && r.handshake === 'none')   // blocked

The split this instrument gives on 76e878c is 9 dispatchable / 5 blocked, out of 14 files
whose floor is NONE. ⚠️ That is not the card's 69/12 and is not meant to be: the card's
numbers were hand-computed weeks of floor repairs ago, when the NONE-floor population was far
larger. 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 none rows were hand-read: two are inline top-level blocks with no callee to leave, two
hand the return value straight to process.exit, one is the ACCIDENT ternary, and
platform-object-tenancy-census.mjs dispatches on the flag only to refuse it.

Verification

Static census, before/after — base c99449ab5 in a separate worktree vs head, exit captured
before any pipe, both EXIT=0:

  • human census: 0 lines removed, 187 added — purely the new section (5 header lines + 181
    file rows + a blank). Nothing existing moved.
  • --json: parsed and compared row by row. Keys base defs,file,floor → head
    defs,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 defs no longer being last.

Ablation — trap-guarded, restore proven by blob hash, both legs. ⚠️ The first attempt was a
no-op: perl -0pi failed to compile the replacement and exited without editing. The on-disk
check caught it (anchor after mutation (must be 0): 1), the reading was discarded as void, and
the leg was re-run with an exact editor. Reported rather than quietly retried.

Leg 1 — drop the flag branch of the recogniser (return flag ? 'flag' : 'none'return 'none').
Anchor 1 → 0, marker 1 on disk. CLI EXIT=1:

measure-self-test-floor: ITS OWN CONTROLS FAILED -- no census printed.
  - HANDSHAKE CONTROL FAILED: a module-level flag set inside the self-test and read negated at the dispatch read none, not flag
  - NAME-INDEPENDENCE CONTROL FAILED: the flag fixture stopped reading flag once `selfTestReachedVerdict` was renamed; the recogniser is keyed on a NAME
  - CONTROL FIXTURE INVALID: with the template delimiters removed the flag decoy is still not recognised ...
  - BODY EXTENT CONTROL FAILED: a self-test whose body contains a column-0 `});` read none ...

and the live check, reached by direct import because main() refuses at the controls first:
live completeness failures: 185 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, so
this reaches the live check through the CLI. EXIT=1:

measure-self-test-floor: THE HANDSHAKE CENSUS IS NOT COMPLETE -- no census printed.
  - 85 file(s) carry a shape the recogniser reads but no LANDED SPELLING knows: ...
The recogniser and the landed spellings disagree about WHICH files carry a handshake.
Widen `classifyHandshake` and `LANDED_HANDSHAKE_NAMES` together, with a control on each.

Restore after each leg: git diff HEAD empty, and git hash-object == the HEAD blob
b9d6d38dcfe092071bc236c66a22fecbb008ae7e.

Gates. node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack
EXIT=0, 26 commands, all run. The first derivation warned STALE TREE and named
scripts/js-comment-mask.mjs — a file this one imports — so origin/main was merged in
(fa8575996) and the list re-derived clean. The census counts are identical before and after that
merge.

  • 20 green, including 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-adoption also 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.mjsEXIT=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 installed
  • scripts/check-ci-filter-parity.mjs — exit 3, PREREQUISITE NOT MET — the dependency yaml is not installed
  • scripts/check-closing-keyword-parity.mjs — exit 3, same reason
  • pnpm check:driver-memory-census — exit 3, ts-parse: PREREQUISITE NOT MET — typescript
  • scripts/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-gates also reports 16 families that apply once a changeset exists. There is none, on
purpose — see below.

Changeset

None. skip-changeset applies, per AGENTS.md: that label "is for a diff that publishes nothing
from 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...HEAD outside scripts/ is empty.

Scope

⛔ No spelling was unified in the tree, no gate script was touched, and --probe was 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
maskCommentsAndLiterals rather than importing the one #15774 published in js-comment-mask.mjs.
That is #15776 (open, finding, pm:blocked), which names this file at :258 explicitly. Not
touched 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

`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
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026
@github-actions github-actions Bot added the size/m label Sep 5, 2026
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #15799 (head 76e878c48, scripts/measure-self-test-floor.mjs +383/−1) reviewed against the GitHub three-dot diff, not the report.

As ruled: one recogniser (classifyHandshake over masked, line-anchored source, with definitionSpan brace-counting the body instead of the "first column-0 }" convention), a handshake key in --json beside floor, a per-file column + per-shape summary in the human census, fixtures per shape with decoys, and a LIVE completeness check (handshakeCensusFailures, EARLY_BRACE_GATE). Spellings not unified. Two findings the instrument surfaced are accepted as measured: the card's own sentinel grep was incomplete (67 literal vs 88 with the await form — the strongest evidence for the card), and triage's "fourth spelling" is a DISPATCH spelling, not a handshake (that file reads flag). Three files that were silently reading none under the old body convention now read correctly.

Measured by this seat: branch blob at scripts/, --json → EXIT=0, keys defs, file, floor, handshake, counts sentinel 88 · flag 85 · helper 1 · none 8 over 182 rows (the extra row and extra none are the probe copy itself; the dev's 88 / 85 / 1 / 7 over 181 is the tree's reading); human census → EXIT=0 with the Handshake SHAPE — … read from CODE (#14968) section; git merge-tree --write-tree origin/main <branch> → EXIT=0; governed test per the report NOT governed. NOT MEASURED here: the typescript / yaml / @typescript-eslint/parser gates by name.

#13799's split reads 9 dispatchable / 5 blocked of 14 floor-NONE files from --json — the method transfers, the card's 69/12 does not (that population has since shrunk). The maskCommentsAndLiterals copy at :258 is #15776's, deliberately untouched here so the masking under these counts does not move.

Flip + arm when both required jobs report success on 76e878c48. On landing #14968 closes and #15759 becomes dispatchable.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 5, 2026 08:52
@baozhoutao
baozhoutao enabled auto-merge September 5, 2026 08:52
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Flipped ready + auto-merge enabled (08:52Z, method: MERGE). Required jobs on 76e878c48: Lint & Repo Gates = success, TypeScript Type Check = success; git merge-tree --write-tree origin/main <branch> → EXIT=0 against the current main. Watched; on landing #14968 closes and #15759 becomes dispatchable.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants