Skip to content

[finding] the #10288 react-page adapter-contract guard sweeps only app-showcase pages, so the same .records read survived a third time in content/docs #10751

Description

@claude

Found while fixing #10469. Recording the mechanism, not proposing a rush: this is the reason that card existed at all.

What happened

One wrong read — result.records off an ObjectStackAdapter.find() result, where the normalized QueryResult only ever declares data — has now been fixed three separate times:

  1. examples/app-showcase/src/ui/pages/crm-workbench.page.ts
  2. examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts (examples: the showcase renewals react page passes top: 500 to adapter.find, which is not a query option — the rollup silently reads only the first page #10288)
  3. content/docs/ui/react-pages.mdx (docs(ui): the react-pages live-data sample reads result.records, which is always undefined — and its Callout misstates what a dropped option does #10469)

#10288 did the right thing and left a guard behind so there would not be a fourth: examples/app-showcase/test/react-page-adapter-query-contract.test.ts, which sweeps react pages for both traps — recordsOnlyReads() (a .records read with no .data beside it) and unprefixedQueryKeys() (a query option missing its $).

The guard works. Its population is the gap:

constREACT_PAGES=Object.values(pagesasRecord<string,unknown>).filter((p): p is {name: string;kind?: string;source?: string}=>!!p&&typeofp==='object'&&(pas{kind?: string}).kind==='react').filter((p)=>typeofp.source==='string');

pages is the app-showcase page registry. The sweep therefore covers kind: "react" pages inside examples/app-showcase and nothing else. The react-page samples in content/docs are not page modules, so they are invisible to it — which is precisely why instance 3 survived the two code fixes and stayed live in the copy a customer starts from.

The gate derivation agrees: node scripts/pm/dispatch-gates.mjs for a content/docs/ui/react-pages.mdx change names 13 families, and none of them reads a jsx sample for this contract.

The shape of a fix

Extend the guard's population to the react-page samples in content/docs, rather than adding a second scanner. The two detectors are already written, already have a positive control (the scanners fire on a known-bad source) and a census control against a vacuously-green empty sweep; what they need is the docs corpus as an additional source of source strings — the fenced jsx blocks under content/docs/ui/ that call adapter.find(...).

That closes the whole class instead of the one line, which is the reason this is worth filing rather than remembering.

Two things to decide when it is picked up, hence finding rather than a queued fix:

  • Where it should live. The guard currently sits in examples/app-showcase/test/, and a test in an example app reaching into content/docs would need a check:cross-package-test-inputs declaration. A scripts/check-*.mjs gate in the docs family may be the better home.
  • How to select blocks. Every jsx fence is not a page source; keying on fences that mention adapter.find( is the obvious narrow start, and the census control has to be re-pointed so an empty docs sweep cannot pass.

Generated by Claude Code


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions