Skip to content

measure-self-test-floor's probe anchors on the FIRST 'function selfTest() {' in a file, so a fixture string masks the real definition — dispatch-gates.mjs was NOT MEASURED for this reason alone #14963

Closed

Description

@claude

Found while working #13800 (verdict handshake, PR #14960), which got a clean reading on scripts/pm/dispatch-gates.mjs by hand-anchoring past this limitation. Filed unassigned. Sibling of #14842, which is the same census reporting NOT MEASURED for a different root cause (a missing ENTRY_BY_HAND row).

The mechanism

injectEarlyReturn(src, name) in scripts/measure-self-test-floor.mjs takes src.match(re) — the FIRST match of

new RegExp(`(?:async\\s+)?function\\s+${name}\\s*\\([^)]*\\)\\s*(?::\\s*...)?\\{`)

The pattern is run against raw source with no literal masking, so it matches inside string and template literals just as happily as at a real definition. In scripts/pm/dispatch-gates.mjs the first match is a FIXTURE — a function selfTest() { embedded in a test string — so return; is injected inside a template literal and the probe copy can only ever produce a SyntaxError. Hence the ledger row:

// Injecting into this file produces a SyntaxError (the anchor lands inside a
// template literal), so no run of it measures anything.
'scripts/pm/dispatch-gates.mjs': null,

That row is accurate about the instrument and was read for months as a property of the file. It is not: anchoring on the real definition (line-start match, or masking literals first) measures it in ONE run. Done by hand in #14960 — before the repair, an early return took that gate from 1288 cases pass to ZERO bytes of output and exit 0; after, exit 1 with the named diagnostic.

Why it matters beyond one file

The census's whole subject is "a green that asserted nothing", and its header says so. An anchor that silently prefers a fixture over a definition produces NOT MEASURED rows that read as "unmeasurable" — a survey shrinking itself for a reason nothing records. The repo already ships the tool this needs: scripts/js-comment-mask.mjs, which measure-self-test-floor.mjs imports for maskComments and could extend to string and template literals; a line-anchored match is the cheaper half.

Worth a control if this is taken: the mask must not blank the file's own controls, which are deliberately fixture strings containing --self-test dispatch spellings.

Second, smaller row in the same ledger

ENTRY_BY_HAND says of scripts/check-platform-checklist.mjs:

// The dispatch calls FOUR self-test functions and combines their statuses

There are FIVE (selfTestSourceLineCitations was added after that note was written), and PR #14960 leaves it five. Stale count in a ledger comment; a one-word fix, listed here so it is not lost rather than because it deserves its own card.


Generated by Claude Code

Activity

  1. added theissue type on Sep 4, 2026
  2. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    Triage (R+146): lands in scripts/measure-self-test-floor.mjs ⇒ domain:devx. Graded Bug · priority:p2.

    Bug: the probe anchors on the first regex match against raw source with no literal masking, so a fixture string wins over the real definition. In scripts/pm/dispatch-gates.mjs the first match is a function selfTest() { embedded in a test string, so return; is injected inside a template literal and the probe can only ever produce a SyntaxError.

    priority:p2 — the harm is that it makes the census lie about itself

    The ledger row reads:

    // Injecting into this file produces a SyntaxError (the anchor lands inside a
    // template literal), so no run of it measures anything.
    'scripts/pm/dispatch-gates.mjs': null,

    ⭐ That row is accurate about the instrument and was read for months as a property of the file. It is not: anchoring on the real definition measures it in one run — done by hand in #14960, where an early return took that gate from 1288 cases pass to zero bytes of output and exit 0 before the repair, and to exit 1 with the named diagnostic after.

    ⇒ The census's whole subject is "a green that asserted nothing", and an anchor that silently prefers a fixture over a definition produces NOT MEASURED rows that read as "unmeasurable" — a survey shrinking itself for a reason nothing records. That is the same failure one level up, which is why it is p2 rather than p3.

    Shape

    ⭐ The repo already ships what this needs: scripts/js-comment-mask.mjs, which this file already imports for maskComments and could extend to string and template literals. A line-anchored match is the cheaper half and may be sufficient on its own — measure before building the mask extension.

    ⚠️ Control required if the mask route is taken: the mask must not blank the file's own controls, which are deliberately fixture strings containing --self-test dispatch spellings. ⇒ A mask that eats them turns this repair into a different silent shrink.

    ⚠️ Five cards now touch this one file — run fold-or-serial before dispatching any of them

    card subject
    this the anchor prefers a fixture over the definition
    #14842 same census, NOT MEASURED for a different root cause (missing ENTRY_BY_HAND row)
    #15339 PRODUCES_FAILURE blind to a ternary process.exit(cond ? 0 : 1)
    #15371 --probe has no coverage of the helper handshake spelling
    #15391 probe awards HELD/ACCIDENT when the baseline run already exits non-zero

    ⇒ Same-file collision here is certain, not possible. ⛔ Triage has not run the five-gate test; the fold-or-serial answer is the lane's and it is a required answer, not a default.

    The second row, folded rather than split

    ENTRY_BY_HAND's note on scripts/check-platform-checklist.mjs says the dispatch calls four self-test functions; there are five (selfTestSourceLineCitations was added after the note). A one-word fix, recorded here so it is not lost — ⛔ it does not deserve its own card and should ride whichever of the five lands first.


    Generated by Claude Code

  3. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Claim: PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk, dispatching to an os-dev subagent on branch claude/issue-14963-probe-anchor-real-definition. P2 Bug, S/M. Ruling: injectEarlyReturn anchors on the REAL definition — match at line start (column 0 or leading whitespace only) over source whose comments AND string/template literals are masked (extend js-comment-mask.mjs's exported scanSource flags, which already classify literal bytes, rather than a second scanner), with a control that the instrument's own fixture strings (which deliberately carry function selfTest() { and --self-test spellings) are neither blanked for the DISPATCH criterion nor chosen as the anchor; the 'scripts/pm/dispatch-gates.mjs': null ENTRY_BY_HAND row is then DELETED (it described the instrument, not the file) and the probe reads that file in one run — quote its verdict (the #14960 hand measurement: early return ⇒ zero bytes / exit 0 before, exit 1 named after). The check-platform-checklist.mjs row (four self-test functions) stays; #15515 (the mutated copy written into scripts/) is a separate card on the same file and is NOT folded in. Serial on scripts/measure-self-test-floor.mjs: #15517 merged 20:18Z; no open PR touches it (scan across all open PRs at claim time). Assignee field was empty at the read immediately before this write.


    Generated by Claude Code

  4. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 14963,
      "status": "done",
      "branch": "claude/issue-14963-probe-anchor-real-definition",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15580",
      "premise_still_valid": true,
      "summary": "The premise holds on current main: injectEarlyReturn matched the FIRST `function NAME(` in RAW source, so a docblock sentence and then a fixture string won the definition in scripts/pm/dispatch-gates.mjs. The anchor now takes its match over maskCommentsAndLiterals(src) (scanSource's existing comment|literal flags OR-ed through the module's own blank(), offsets preserved) AND requires the match to begin a line (export/async prefixes kept). js-comment-mask.mjs is NOT touched — its origin/main state already exports everything needed, so #15532 is left alone. The census criterion keeps reading maskComments and a control pins the two masks to opposite answers, because every dispatch names the flag with a string literal. The `'scripts/pm/dispatch-gates.mjs': null` row becomes 'selfTest'; the check-platform-checklist row STAYS null and only its prose is corrected — the card says FIVE, the comment said FOUR, the measured answer is SIX (a sixth battery, selfTestSymbolAnchors, landed after #14960); the same 'four' in the HELPER_HANDSHAKE_GATE docblock and the ledger docblock's 'nine files' (ten rows) are corrected too. Also worth the PM's attention: what the defect produces is not merely NOT MEASURED — a copy that dies in the parser exits non-zero AND prints a stack, which the three-valued verdict scores HELD; only the hand-written null kept that false hold off the sweep.",
      "tests": "All on final commit b366d16a8, tree clean, every exit code captured before any pipe. node scripts/measure-self-test-floor.mjs EXIT=0 — 'measure-self-test-floor: 178 file(s) under scripts/ dispatch on `--self-test`.', controls all pass, and `cmp` against the same run on main is BYTE-IDENTICAL. --json EXIT=0, diff vs main 0 lines (0 rows reclassified, no defs moved). STATIC DIFFERENTIAL: for all 171 mechanically anchored rows the injection offset was recorded before/after through main()'s own entry selection — rows 178, moved 0; the new rules pick the same byte in every already-measurable row. DISPATCH-GATES PROBE (narrowed to that row via main()'s own selection; full --probe is NOT MEASURED under the ~10min container cap, said as such): default 120s timeout -> NOT MEASURED 'killed by SIGTERM' after 120.2s; extended 900s timeout -> NOT MEASURED 'baseline run failed (exit 1)', baselineExit 1, baselineBytes 141759, baselineHead '  ✓ extracts plain pnpm check' after 399.0s. Both limits belong to the instrument, not this card: the 120s spawn budget is smaller than this self-test (time node scripts/pm/dispatch-gates.mjs --self-test -> real 6m39.690s, exit 0, '✓ dispatch-gates self-test: 1415 cases pass.') — filed as #15573; past it the baseline is red for #15515, measured directly by placing the same copy by hand: with scripts/pm/.self-test-floor-probe-dispatch-gates.mjs present the gate EXITs 1 naming that very file ('the tier constant's VALUE is spelled in exactly ONE site ... found: scripts/pm/dispatch-gates.mjs:9020, scripts/pm/.self-test-floor-probe-dispatch-gates.mjs:9020' / '✗ dispatch-gates self-test: 1 of 1415 case(s) failed.'). NOT folded in, per the dispatch. WHAT THE REPAIR DOES DELIVER, measured by hand as #14960 did: anchor byte 624612 line 11507 'function selfTest() {', marker x1 on disk, node -c on the copy EXIT=0 (it PARSES), node copy --self-test EXIT=1 in 202 bytes with '✗ dispatch-gates self-test: selfTest() returned without reaching its verdict,' — #14960's expectation reproduced on today's 1415-case count; the probe would score this HELD once #15573 and #15515 are out of its way. ABLATION on the committed implementation, trap-guarded, each leg proving the mutation on disk with anchored counts in BOTH directions and the restore by blob hash AND empty `git diff HEAD` (no dist/: the instrument resolves by path, so there is no rebuild leg): unmutated control EXIT=0 anchoring line 11507; '^' dropped from both patterns EXIT=1 with 3 named failures incl. ANCHOR CONTROL FAILED; maskCommentsAndLiterals(src) -> src EXIT=1 with the same 3. Restore proved on both legs (blob 18088a5e42c248e91a5e82772870f6cb8d125aa2 == HEAD, diff empty). TWO PREDICTIONS WERE WRONG AND ARE REPORTED AS OBSERVED: I expected the mask-removed leg to red as a false HELD, and it reds as 'mutation had no observable effect' (injecting into the fixture's PLAIN template literal keeps it parseable; what breaks parsing in the real carrier is injecting the /*...*/ marker into a BLOCK comment, closing it early); and on the real dispatch-gates.mjs EITHER rule alone already lands on line 11507 today, so the carrier does not discriminate between them — the fixture does, which is why the controls are the pair. That answers the triage's 'the line-anchored match may be sufficient on its own — measure before building the mask': on today's carrier, yes; the mask is what keeps it true of the next one. GATES: pnpm -s check:pm-dispatch-gates EXIT=0 '✓ dispatch-gates self-test: 1415 cases pass.' (no case edited). pnpm check:nul-bytes EXIT=0 (7530 files, no raw ASCII control bytes) plus a hand scan of both edited files with grep -naP over the control-byte class — no match. pnpm lint (eslint . --no-inline-config, WHOLE repo) EXIT=0 via os-verify-lock: 'VERDICT command-exit 0 · held the lock 83s · waited 207s'. DERIVED FAMILIES: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 24 commands for this 2-path changeset, re-derived identical on the final commit; ALL 24 RAN AND ALL 24 EXITED 0 — no exit 3, no PREREQUISITE NOT MET, no ERR_MODULE_NOT_FOUND, so no NOT MEASURED row. The derivation printed a STALE TREE notice (branch cut at cf6b67164, origin/main now 0c5e97368; .github/workflows/lint.yml, package.json and scripts/check-registry-log-declared.mjs changed across that range — none of them paths this PR touches). skip-changeset label set on the PR and read back.",
      "mcp_calls": "0 — every GitHub read and write went through REST with $GH_TOKEN; no MCP GitHub call was made",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #15573: the probe spawns with a 120s timeout, smaller than the self-tests it measures — dispatch-gates.mjs (6m39.690s measured) reads NOT MEASURED 'killed by SIGTERM' under a plain --probe, masking the #15515 reading behind it",
        "filed as #15574: [finding] selfTestDefs() still reads RAW source, so a name inside a fixture string counts as a definition — swept over the whole census, 1 row differs (fixtureSelfTest at dispatch-gates.mjs:12715) and 0 verdicts change today; left out because defs is published in --json and this card required a byte-identical --json diff"
      ]
    }

    Generated by Claude Code

  5. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Follow-up to the os-dev-report above — PR #15580 amended on PM review. The
    report comment stands except for the four deltas below.

    The scripts/pm/dispatch-gates.mjs hunk (commit b366d16a8, the
    SELF_TEST_VERDICT docblock correction) is reverted in 08cf96d57. PR
    #15570 rewrites that exact region, git merge-tree reports a conflict, and it is
    ahead in the queue — so this PR would go dirty the moment it lands, and the
    serial-file rule allows one live PR per hot file. The correction is deferred to
    #15553, not dropped
    : the docblock there still tells readers the mechanical
    probe cannot read that file, which this branch makes false.

    The revert takes the file from origin/main, whose blob is byte-identical to
    this branch's merge base — f9dbdd690a755a972a23005c4efffebe4dc13303 at
    cf6b67164, at origin/main 0c5e97368, and in the tree now — so no content
    from the newer main rides in with it.

    before now
    head b366d16a8 08cf96d57
    changed files 2 1 (scripts/measure-self-test-floor.mjs only)
    derived families 24, all exit 0 22, a strict SUBSET (0 added, 2 dropped), all re-run on the new head, all exit 0

    Re-taken on 08cf96d57, every exit code captured before any pipe:

    git diff origin/main...HEAD --stat
      scripts/measure-self-test-floor.mjs | 180 ++++++++++++++++++++++++---
      1 file changed, 169 insertions(+), 11 deletions(-)
    
    node scripts/measure-self-test-floor.mjs                       EXIT=0
      measure-self-test-floor: 178 file(s) under scripts/ dispatch on `--self-test`.
      cmp against the same run on main: BYTE-IDENTICAL
    
    node scripts/measure-self-test-floor.mjs --json                EXIT=0
      cmp against main: BYTE-IDENTICAL (0 rows reclassified)
    
    node scripts/measure-self-test-floor.mjs --self-test           EXIT=0
      byte-identical to the plain run — this file deliberately ships NO `--self-test`
      mode; its controls run inline on EVERY invocation so they cannot become unrun.
      Driving that path directly: runControls() failures: 0         EXIT=0
    
    pnpm check:nul-bytes                                           EXIT=0
      check-nul-bytes: OK (scanned 7530 text file(s); no raw ASCII control bytes)
      hand scan of the one changed file over the control-byte class: no match
    
    22 of 22 derived commands re-run on this head                  all EXIT=0
    

    pnpm lint (whole repo, EXIT=0, os-verify-lock: VERDICT command-exit 0) was
    taken on b366d16a8, whose tree is this one plus the reverted docblock — not
    re-run, said as such rather than implied.

    ⚠️ The unchanged half of the report still holds and is where the substance is:
    the anchor now lands on the real definition, and the two limits keeping
    dispatch-gates.mjs at NOT MEASURED belong to the instrument, not this card
    (#15573 the 120s spawn budget, #15515 the probe's copy under scripts/).


    Generated by Claude Code

  6. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    LANDED — PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk.

    PR #15580 merged 2026-09-04T22:56:03Z (squash e39619d6f; enqueued 22:30Z, one merge-group run, no eviction). Probed on origin/main after a re-fetch (HEAD 088f761e5), textually and by running the instrument (it imports only js-comment-mask.mjs, no typescript):

    • scripts/measure-self-test-floor.mjs: import { blank, maskComments, scanSource } :111; maskCommentsAndLiterals :258; injectEarlyReturn :291 (masked + line-anchored patterns); DECOY_ANCHOR_GATE :644, NAIVE_ANCHOR :671, the ANCHOR CONTROL FAILED case :738; ENTRY_BY_HAND row 'scripts/pm/dispatch-gates.mjs': 'selfTest' :895.
    node scripts/measure-self-test-floor.mjs   → exit 0
    measure-self-test-floor: 179 file(s) under scripts/ dispatch on `--self-test`.
    (no CONTROL FAILED / CONTROL FIXTURE INVALID line in the output)
    

    179 rather than the PR's 178: one more --self-test-dispatching script landed on main between the PR's fork point and now (main moved by four other merges) — a census delta, not a reclassification.

    The dispatch-gates.mjs docblock correction this PR dropped (to stay clear of PR #15570) is in #15553's dispatch, live now. Stripping pm:dispatched + assignee; Fixes #14963 closed this card. Instrument FREE → #15515 is next on it.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions