Skip to content

fix(cli): the #12525 read-back's anti-vacuity pins read CODE, and bind the argument rather than the argument list - #18519

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-17633-serve-port-readback-nightly
Sep 16, 2026
Merged

os-support-ai merged 1 commit into
mainfrom
claude/issue-17633-serve-port-readback-nightly

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Part of #17633

Clause-②: no

The nightly e2e tier was red on main in packages/cli/test/serve-port-readback.e2e.test.ts. Reproduced first, at the base this branch was cut from, with the tier switch the nightly uses. Two assertions failed, and neither was about the product — both source-text pins in the file's ANTI-VACUITY describe had decayed while the invariants they claim still hold.

What was red, and why

vitest run --project integration test/serve-port-readback.e2e.test.ts under OS_TEST_TIERS=nightly, at 8cf527f8e:

AssertionError: the `API:` row is no longer printed BEFORE the banner tail — keying the
read-back on the tail no longer proves the row is in the buffer: expected 47343 to be less
than 38075
AssertionError: the ready banner no longer derives its API row from `resolveAuthBaseUrl(boundPort)`
— if it now uses the REQUESTED port, the #12525 read-back is vacuous by construction:
expected '// Copyright (c) 2025 ObjectStack. Li…' to contain 'externalBaseOrigin: resolveAuthBaseUr…'
 Test Files  1 failed (1)
      Tests  2 failed | 11 passed (13)

Both pins read the raw source file, and a raw read is unsound in both directions.

1. The print-order pin fabricated. It ran indexOf over the whole of src/utils/format.ts. ed5a1e7f2 (#17892) added a docblock quoting Press Ctrl+C to stop at line 800 — 281 lines above the console.error at line 1081 that prints it. So the raw read put the tail at offset 38075 and the API: row at 47343, and the assertion reported a print order that had never changed. Read in code position the two offsets are 47343 and 53551, i.e. line 984 before line 1081, exactly as the pin claims.

2. The banner pin held a byte-exact call that a legitimate refactor moved. 89a652b83 (#17725, dev --cert/--key) made the call resolveAuthBaseUrl(boundPort, boundProtocol). The port argument is unchanged and still the bound one; only the argument list grew. That commit updated the per-PR sibling pin, packages/cli/src/commands/serve-bound-port-publication.test.ts, in the same diff — and could not update this copy, because this file runs only on the nightly main sweep and no pull request can redden it.

The fix

Both pins now read through scripts/js-comment-mask.mjs, this tree's one answer to code-versus-prose, which blanks comment spans in place so every offset and line number survives. That repairs the fabrication and closes the other direction at the same time: a comment naming a pinned spelling can no longer satisfy a pin with no code behind it, which is exactly the vacuum the describe exists to prevent. The mask carries its own control — blanked in place (code.length === raw.length) and blanked something (code !== raw).

The banner pin now binds the argument rather than the argument list: the row is derived from boundPort, whatever else the call grows. That is the division of labour the two tiers imply — the per-PR sibling keeps the byte-exact line and reddens on the PR that moves it, with that PR's author reading the failure; a nightly-only copy of the same byte-exact claim can only go red a day later on a card nobody can attribute.

That this is narrower, not looser, is proven rather than asserted — inside the test, on synthetic text, so the proof survives any future rewording of serve.ts:

expect('externalBaseOrigin: resolveAuthBaseUrl(port).baseOrigin').not.toMatch(BANNER_FROM_BOUND_PORT);
expect('externalBaseOrigin: resolveAuthBaseUrl(requestedPort, boundProtocol).baseOrigin')
  .not.toMatch(BANNER_FROM_BOUND_PORT);

…beside a standing negative asserting neither requested-port spelling is in serve.ts at all.

⛔ No test renamed, skipped, quarantined or deleted, and no assertion dropped. The describe gains four assertions and loses none.

Verification

Everything below was run in a dedicated worktree at 63d19926e, with the dependency closure built first.

Red, then green, same command, both with the tier env set. The red leg restores the base copy of the test file onto the built tree, so the only variable is this diff:

tree result
test file at 8cf527f8e (base) Tests 2 failed | 11 passed (13), the two assertions quoted above
this branch Tests 13 passed (13) — both real os serve boots included, 17.36s

Restore proven by blob equality (d00e065c02c1b76bf42b641abed34b094cddf4c1 back on disk) and an empty git diff HEAD with an empty git status --porcelain.

Ablation — two legs, each mutating product code on disk to prove the repaired pin still binds. No product code is changed by this PR; these mutations were made, measured and reverted.

leg mutation on-disk proof pin
A resolveAuthBaseUrl(boundPort, boundProtocol) becomes resolveAuthBaseUrl(port, boundProtocol) in serve.ts bound-spelling 1 to 0, requested-spelling 0 to 1; blob 803b955f to a2f0aef3 RED, naming the requested port
B the API: row print in format.ts renamed so it no longer prints API: api-row-prints 1 to 0; blob e3dda5c8 to 9ac9622f RED: printServerReady no longer prints an API: row

Both restored: blob equal to the HEAD blob, git diff HEAD empty, git status --porcelain empty.

Gate families. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack from the actual changed path, reconciled with --ran: 49 derived / 49 run / 0 NOT-MEASURED / 0 UNRUN.

Beyond the derivation (the standing blind spot — the derivation never names the lint family):

  • pnpm lint — repo-wide, eslint . --no-inline-config, exit 0 at 63d19926e.
  • pnpm --filter @objectstack/cli typecheck — exit 0, test layer included (tsconfig.test.json, ledger unchanged at 3 files / 28 errors / 6 pinned signatures).
  • pnpm --filter @objectstack/cli exec vitest run --project unitTest Files 211 passed (211), Tests 3003 passed (3003). That tier carries test/vitest-tiers-partition.test.ts, which is what would notice if the new import had moved this file between tiers. It did not.

Changeset

skip-changeset, graded from what actually moves rather than from the shape of the diff. packages/cli declares files: ["dist", "README.md", "CHANGELOG.md"]; npm pack --dry-run --json ships 533 files, 0 of them under test/ and 0 matching serve-port-readback. The one changed path is not in any published tarball, so no released package moves.

Acceptance notes

  • This PR carries no closing keyword for nightly-tiers: red on main #17633, deliberately. The nightly workflow files nothing, edits nothing and closes nothing on green, so closure is a later reading of a green nightly and not something a merge can assert. Closing on the repair rather than on the confirmation would throw the card away along with its os-nightly-tiers marker if the next sweep is red for any other reason.
  • The card's priority:p1 and everything in its comment thread were graded fresh and not inherited: the thread discusses older, already-repaired defects in other files, and the body — rewritten in place by the nightly's report job — names only this file.
  • Observation, not filed: the two source pins repaired here are near-duplicates of pins in packages/cli/src/commands/serve-bound-port-publication.test.ts, which runs per-PR. A byte-exact source pin that lives only in a nightly-only tier cannot be kept honest by the PR that moves the source — that is the mechanism behind this red, and this file is unlikely to be the only place it holds. Whether such pins belong in the nightly tier at all is a wider question than this card.

Generated by Claude Code

…d the argument rather than the argument list

`serve-port-readback.e2e.test.ts` runs only in the nightly `e2e` tier, so no
pull request can redden it. Two source-text pins in its ANTI-VACUITY describe
read the raw file, and both went red on `main` for reasons that were never
about the product:

  - the print-order pin did `indexOf` over the whole of `src/utils/format.ts`.
    #17892 added a docblock quoting `Press Ctrl+C to stop` 281 lines above the
    `console.error` that prints it, so the raw read put the tail at offset
    38075 and the `API:` row at 47343 and the assertion reported a print order
    that had never changed. In code position the two are 47343 and 53551, i.e.
    line 984 before line 1081, exactly as the pin claims.
  - the banner pin held the byte-exact `resolveAuthBaseUrl(boundPort)` call.
    #17725 added a `boundProtocol` argument. The per-PR sibling
    `src/commands/serve-bound-port-publication.test.ts` was updated in that
    same commit; this nightly-only copy could not be, and had no way to say so
    until the next sweep.

Both pins now read through `scripts/js-comment-mask.mjs`, the tree's one
answer to code-versus-prose, which also closes the other direction: a comment
naming a pinned spelling can no longer satisfy the pin with no code behind it.
The mask carries its own anti-vacuity control (blanked in place, and blanked
something).

The banner pin now binds the ARGUMENT — the row is derived from `boundPort` —
and tolerates whatever else the call grows, which is the division of labour
the two tiers imply: the per-PR sibling keeps the byte-exact line and reddens
on the PR that moves it. That this is narrower rather than looser is proven in
the test on synthetic text: the matcher rejects `resolveAuthBaseUrl(port)` and
`resolveAuthBaseUrl(requestedPort, boundProtocol)`, and a standing negative
asserts neither requested-port spelling is in `serve.ts`.

⛔ No test renamed, skipped or deleted, and no assertion dropped: the describe
gains four assertions and loses none.

Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0e51278f388214b7fbd0ff5d4bd2445cadc88843packageMentionDocs.

@github-actions github-actions Bot added the tests label Sep 16, 2026
@os-support-ai os-support-ai added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 16, 2026 — with Claude
@os-support-ai
os-support-ai marked this pull request as ready for review September 16, 2026 18:43
@os-support-ai
os-support-ai added this pull request to the merge queue Sep 16, 2026
Merged via the queue into main with commit ed7d59c Sep 16, 2026
39 of 40 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-17633-serve-port-readback-nightly branch September 16, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants