Uh oh!
There was an error while loading. Please reload this page.
feat(tooling): sweep the react-page useAdapter() contracts over the docs corpus too - #11584
Merged
Merged
Conversation
…ocs corpus too One wrong read -- `result.records` off an `ObjectStackAdapter.find()` result, where the normalized `QueryResult` only ever declares `data` -- was repaired three separate times: two app-showcase pages and `content/docs/ui/react-pages.mdx`. The guard left behind after the second one had the right detectors and the wrong population: it read the app-showcase page registry, so the react samples in `content/docs` -- the copy a customer starts from -- were invisible to it. `recordsOnlyReads()` and `unprefixedQueryKeys()` MOVE into `scripts/check-react-page-adapter-contract.mjs`, which sweeps both populations. They move rather than being copied: two definitions of one detector double the places a future fix has to land, which is the defect, not a fix for it. The example app's test keeps the half a text scan cannot do -- it executes the renewals-pipeline rollup against a contract-faithful adapter double. A gate rather than that test with a wider reach, decided by measurement: `content/docs/**` as a declared cross-package test input would put the example app's suite on 22 of the last 132 commits (against 3 that touch the app), which is the cost `check-cross-package-test-inputs`'s own roster refuses twice; and the per-page narrowing it prefers instead rebuilds this defect, since a list someone must remember to extend goes silently incomplete the day a react sample lands on a second page. The census control is re-pointed: an empty sweep of EITHER half fails, and each of the three files the defect was repaired in is pinned as an anchor, so a population that is non-empty but has lost coverage fails too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
os-steve
marked this pull request as ready for review
August 24, 2026 07:47
Uh oh!
There was an error while loading. Please reload this page.
os-steve pushed a commit
that referenced
this pull request
Aug 24, 2026
#11542) Eight scans in `affected-docs.mjs` ask "is a `route:` / `client:` declaration written here?". `declLead` has spelled the colon and the run after it once since #11494, but the KEY stayed each call site's own argument: `declarationsIn` anchored it with `\b` and the other seven did not. So `subroute: 'GET /x'` was a declaration to SEVEN of the eight and not to the eighth, and it minted a silent phantom ROW — silent because the partial-read verdict keys on the gap between `rows` and `routesDeclared` and both terms read the unanchored spelling. The anchor now lives in `declLead`, once, and `declarationsIn` stops restating it. Priced against the `--bridge-coverage` before/after standard: 0 divergent leads across the seven live ledgers, and the census is byte-identical row for row (269 of 269 / 222 of 222 / 45 reachable / 177 UNREACHABLE / 0 prose-quoted leads / brokenScan 0). `--self-test` moves the pin #11584 left rather than deleting it, adds one fixture per previously-unanchored scan, and pins the `$route:` residue (#11630) where the next card will find it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
This was referenced Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#10751
One wrong read —
result.recordsoff anObjectStackAdapter.find()result, where thenormalized
QueryResultonly ever declaresdata— was repaired three separate times.The guard left behind after the second repair had the right detectors and the wrong
population: it read the app-showcase page registry, so the react samples in
content/docswere invisible to it, which is how instance 3 survived the two code fixesand stayed live in the copy a customer starts from.
recordsOnlyReads()andunprefixedQueryKeys()move intoscripts/check-react-page-adapter-contract.mjs(pnpm check:react-page-adapter-contract),which sweeps both populations. They move rather than being copied: two definitions of one
detector double the places a future fix has to land, which is the defect this card is
about, not a fix for it.
examples/app-showcase/test/react-page-adapter-query-contract.test.tskeeps the half a text scan cannot do — it executes the renewals-pipeline rollup against a
contract-faithful adapter double (5 tests, still green).
Route taken, and the measurement that decided it
A gate in the docs family, not the example app's test with a wider reach.
check:cross-package-test-inputs(re-read on today'smain, post-#11486/#11554) requiresa declaration plus
turbo.jsonhashing every declared glob. Both available spellingsare wrong here:
content/docs/**maintouch it (16.7%), against 3 that touchexamples/app-showcase. 19 of those 22 carry no react sample at all.content/docs/**: docs are edited far more often than any package here."content/docs/ui/react-pages.mdx)adapter.find(occurs in exactly one docs page today.A
scripts/check-*.mjsgate has no radius to maintain — lint.yml runs it on every PR overthe whole tree, so a new docs page is in the population the moment it exists. Confirmed
empirically:
check:cross-package-test-inputsis green at9e9415ffwith no newdeclaration (
14 package(s) read outside themselves, all declared).The population, measured — what it admits and what it misses
21 app-showcase page module(s) + 1 content/docs react-page sample(s) (from 394 doc file(s), 1951 fenced block(s)).1 of 1951 fences.
.find(alone is not the marker, and that is the whole difficulty —this tree has three different
find()contracts in its docs and only one declaresdata:adapter.find/dataSource.find— objectuiObjectStackAdapter,$-prefixed options,rows under
data. Swept.engine.find/dataEngine.find— ObjectQLIDataEngine, whose options arewhere/fields/sort, unprefixed by contract.content/docs/protocol/objectql/query-syntax.mdxalone holds 11 of them; sweeping those fabricates ~30 findings.
client.data.find/useQuery—@objectstack/client, which resolves aPaginatedResultwhose rows are under
records(packages/client/src/index.ts:310).content/docs/api/client-sdk.mdx:659readsdata?.records.map(...)and is correct.Sweeping it fabricates a fourth instance of a defect that is not there.
So the marker is the identifier holding the adapter, not the method name:
useAdapter(, ora
find/findOneonadapter/dataSource. A language-tagged fence qualifies on either;an untagged fence needs the real call (which keeps
react-pages.mdx's untagged CLI-errorblock, whose hint names
useAdapter().findOnein prose, out while still catching a samplesomeone forgot to tag); any other tag (
bash,json,text) is taken at its word.Stated exclusions, rather than discovered later: a deliberate counter-example written as a
runnable fence would be flagged (this doc set writes counter-examples as prose — see the
"$ prefixes are load-bearing" Callout — and no fence in the tree is one today, so no opt-out
is invented); a sample holding the adapter under a third name with no
useAdapter(in thefence;
content/outsidedocs/, and thedocs/ADR tree.The census control, re-pointed
The old control guarded a vacuously-green empty sweep of app-showcase only. Once the
population includes
content/docs, that no longer covers the new half.censusFailures()now fails when either half comes back empty, and additionally pins each of the three
files the defect was actually repaired in as an anchor — so a population that is non-empty
but has lost coverage fails too. Non-empty and covering are different facts.
Verification
Everything below at
9e9415ff.Non-vacuity, both directions, under
trap restore EXIT INT TERM, each mutation provenon disk by
git hash-objectand by counting the injected and removed markers:$top:→top:,result?.data ?? …→result.recordsin the docs fenceb4ec8fb3→eb0bcf88content/docs/ui/react-pages.mdx$top: 500→top: 500(2 sites) inrenewals-pipeline.page.ts2814e166→8bfe5e4bb4ec8fb3→ea203fc7contributed 0 react-page sample(s)+ the missing anchorEvery leg restored byte-identical (
b4ec8fb3/2814e166recovered exactly), and thegate returned to exit 0 after each. The mutation step never trusts an editor's exit code:
each leg aborts on an anchor miss and prints injected/removed marker counts.
The existing positive control still fires — carried verbatim into the gate's
--self-test, which is a strict promotion (it now runs on every PR rather than only whenapp-showcase is affected):
Derived gate union —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-written path list; the script takes the change set from the merge base itself)
named 22 path-matched families plus the convention-triggered set for a test-file edit.
All 24 run green, exit codes captured before any pipe. Verdict lines rather than bare
$?:Package scope:
pnpm --filter '@objectstack/example-showcase^...' build(dependencyclosure first, on a fresh worktree), then the app-showcase suite —
Test Files 25 passed (25),Tests 359 passed (359)— andpnpm --filter @objectstack/example-showcase typecheckexit 0. The touched test file alone:
Test Files 1 passed (1),Tests 5 passed (5).Lint, narrowed with the narrowing proven rather than skipped. eslint over exactly the
four changed paths: 4 accepted, 0 errors, 2 warnings — both of them eslint's own
File ignored because no matching configuration was suppliedforpackage.jsonandlint.yml, so the checkable population is read from eslint's config, not guessed. Countsfrom
--format json. Invariance:eslint.config.mjsenables type-aware linting for nofile (0 matches for
projectService/parserOptions.project; the config's own headerrecords the measurement), so this diff cannot move any untouched file's verdict.
Not measured locally, declared:
check:type-check-debt --re-measure, which needs thefull workspace closure built. CI owns it. The diff removes test code and adds a
dependency-free script, and
@objectstack/example-showcase's owntypecheckis green.Changeset
None —
skip-changeset. Every touched package publishes nothing:examples/app-showcaseis
"private": true, the rootpackage.jsonis private, andscripts/and.github/workflows/are not published at all. That isscripts/pr-labels.mjs's own rule:"
skip-changesetis the exemption for a PR that publishes nothing."Premise re-check (the card is from 08-21)
Re-swept before changing anything.
.recordsoff an adapter result incontent/docstoday: one occurrence, and it is not a fourth instance —
content/docs/ui/react-pages.mdx:147reads
result?.data ?? result?.records ?? (Array.isArray(result) ? result : []), where.datais read first and always wins, so the sample renders correctly. Instance 3 is stillfixed. The other
.recordshits in the corpus are thedata.records.updatedevent-typestring and the
client-sdk.mdxPaginatedResultread, which is correct on its own contract.That surviving
??alias is a real contract-first smell, and the detector's.data-beside carve-out is what blesses it — but the same shape is what instance 1'slanded fix uses (
crm-workbench.page.ts:49), so tightening the detector would redden adifferent file and a different instance. Filed separately rather than folded in silently.
Generated by Claude Code