Uh oh!
There was an error while loading. Please reload this page.
Delete the tolerant ?? records aliases and narrow the guard carve-out that blessed them - #13705
Merged
os-project-manager merged 1 commit intoAug 31, 2026
Merged
Conversation
…essed them `ObjectStackAdapter.find()` resolves a normalized `QueryResult` whose sole non-empty return path (`normalizeQueryResult`) builds an object literal with exactly `data`, `total`, `page`, `pageSize`, `hasMore`. `records` is a key it READS off the transport envelope, never one it writes -- so the `?? .records` limb in both surviving repairs is unreachable by contract. Behaviour-preserving: `.data` is read first and always wins today. What goes is a spelling the producer cannot emit, sitting in the page a customer copies from. The third piece is what stops it returning: `recordsOnlyReads()` skipped any line carrying `.data`, which made `data ?? records` the one shape it could not see. That carve-out is replaced by comment/string stripping, and the detector renamed `recordsReads` since "only" no longer describes it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
This was referenced Aug 31, 2026
os-project-manager
marked this pull request as ready for review
August 31, 2026 07:45
Uh oh!
There was an error while loading. Please reload this page.
os-project-manager
deleted the
claude/issue-11585-records-tolerant-aliases
branch
August 31, 2026 08:18
os-project-manager pushed a commit
that referenced
this pull request
Aug 31, 2026
`@objectstack/docs` patch, following #13955 (the most recent `content/docs/**` diff, which named that package). `@objectstack/example-showcase` is deliberately not named: it is private and appears in 0 of the repo's changesets, and #13705 — the immediately preceding repair at two of these same three sites — carried no changeset at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
os-project-manager pushed a commit
that referenced
this pull request
Aug 31, 2026
…etector (#13970) `ObjectStackAdapter.find()` cannot resolve to an array, so an `Array.isArray(<find result>)` limb is dead code that teaches a row shape the producer cannot emit. The shape was repaired three times (#11585 -> #13705 -> #13969) and each repair left nothing pinning it, so a reintroduction at any of the three sites got a green guard. `arrayIsArrayLimbs` is a THIRD detector, not a widening of `recordsReads`. The self-test case that pins `const records = result?.data ?? (Array.isArray(result) ? result : []);` to zero `recordsReads` findings is a false-positive control on the `.records` PROPERTY matcher — the `records` there is a local — and it is still correct. It is unchanged, byte for byte: the new detector sees the limb on that same line, and both statements are true. A subject qualifies on either route: bound from an `adapter`/`dataSource` find() in the same source, or read as `<subject>.data` on the same line. The second route is load-bearing — the renewals-pipeline repair's subject was a lambda parameter bound to no find() call anywhere. Measured over the pre-#13969 tree (bd8791f): exactly the three deleted sites across the whole population, no false positives. On `origin/main` today: zero. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 1, 2026
…d()` sites (objectstack-ai#13969) * Delete the unreachable Array.isArray limb at all three adapter.find sites `ObjectStackAdapter.find()` cannot resolve to an array. Re-derived on the pinned objectui sha (9602dc82) and on objectui `origin/main`: find() has two object-literal returns (`{ data: [], total: 0 }` for a memoized 404 and for a fresh non-denial 404), two `normalizeQueryResult(...)` returns, and an inflight `return existing` that hands back a promise from that same set. Both of `normalizeQueryResult`'s branches return an object literal with exactly `data, total, page, pageSize, hasMore` -- the first one WRAPS a bare array response into it. So no `Array.isArray(<find result>)` limb can ever be taken. Behaviour-preserving, like the `?? records` deletion beside it: `.data` was already read first and always won. What goes is a shape the producer cannot emit, in the sample a customer (and a coding agent) copies from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC * Add the changeset for the docs sample change `@objectstack/docs` patch, following objectstack-ai#13955 (the most recent `content/docs/**` diff, which named that package). `@objectstack/example-showcase` is deliberately not named: it is private and appears in 0 of the repo's changesets, and objectstack-ai#13705 — the immediately preceding repair at two of these same three sites — carried no changeset at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 1, 2026
…etector (objectstack-ai#13970) (objectstack-ai#13989) `ObjectStackAdapter.find()` cannot resolve to an array, so an `Array.isArray(<find result>)` limb is dead code that teaches a row shape the producer cannot emit. The shape was repaired three times (objectstack-ai#11585 -> objectstack-ai#13705 -> objectstack-ai#13969) and each repair left nothing pinning it, so a reintroduction at any of the three sites got a green guard. `arrayIsArrayLimbs` is a THIRD detector, not a widening of `recordsReads`. The self-test case that pins `const records = result?.data ?? (Array.isArray(result) ? result : []);` to zero `recordsReads` findings is a false-positive control on the `.records` PROPERTY matcher — the `records` there is a local — and it is still correct. It is unchanged, byte for byte: the new detector sees the limb on that same line, and both statements are true. A subject qualifies on either route: bound from an `adapter`/`dataSource` find() in the same source, or read as `<subject>.data` on the same line. The second route is load-bearing — the renewals-pipeline repair's subject was a lambda parameter bound to no find() call anywhere. Measured over the pre-objectstack-ai#13969 tree (bd8791f): exactly the three deleted sites across the whole population, no false positives. On `origin/main` today: zero. Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC Co-authored-by: Claude <noreply@anthropic.com>
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#11585
ObjectStackAdapter.find()resolves a normalizedQueryResultthat declaresdataand neverrecords, so the?? .recordslimb in both surviving repairs is unreachable by contract. This is behaviour-preserving —.datais read first and always wins today, and both pages render correctly either way. What goes is a spelling the producer cannot emit, sitting in the page a customer copies from.Three things ship, because the first two alone let the pattern come back green.
1 + 2 — the two tolerant aliases
examples/app-showcase/src/ui/pages/crm-workbench.page.ts:49—(all && (all.data || all.records)) || []becomes(all && all.data) || [].content/docs/ui/react-pages.mdx:145—result?.data ?? result?.records ?? ...loses the.recordslimb. This is the one that actively TEACHES the fallback.The prose three lines above the app-showcase call said "Read .data first, with .records/array fallbacks for robustness". That sentence was the instruction the alias implemented, so it is rewritten to state the contract instead. The historical explanation of why
.recordswas wrong is kept.3 — the carve-out that blessed them
recordsOnlyReads()skipped any line carrying.data:That made
data ?? recordsthe one shape the detector could not see — which is precisely the shape both surviving repairs had landed as. The carve-out is replaced by comment and string-literal stripping (codeOnly()), so.recordsis judged as a property READ rather than as text that merely spells it. The detector is renamedrecordsReads, since "only" no longer describes it.Blanking string bodies is what lets the carve-out go safely: the webhook shape
emit({ type: 'data.records.updated' })is kept out of the sweep by the SELECTOR today, and a detector that is quiet only because of the selector is one population change away from firing.Measured, not assumed
A
.recordsPRODUCER search — none exists on this path.useAdapter()is typedObjectStackAdapter | null(objectuipackages/react/src/context/AppShellContext.tsx:10), so no otherDataSourcereaches a react page through it.ObjectStackAdapter.find()has exactly three return paths (objectuipackages/data-objectstack/src/index.ts): two{ data: [], total: 0 }literals, andnormalizeQueryResult()at line 3461, which CONSUMES the transport envelope'srecords/valueand returns an object literal with exactlydata,total,page,pageSize,hasMore.recordsis a key it reads, never one it writes. The app-showcase's own contract-faithful adapter double returns the same five keys. The other.recordsshapes in the tree are different contracts and unreachable from these two sites: the@objectstack/clientPaginatedResultthe adapter normalizes, the ObjectQL engine list shape inexamples/app-showcase/src/automation/jobs/sweep-project-health.ts:107,ApiDataSource(which also extractsrecordsINTOdata), and the seed-authoringrecords:key insrc/data/seed/index.ts. So the removal is behaviour-preserving at runtime, not just by declaration.Guard population, before and after — zero bystanders. The sweep's population is unchanged: 21 app-showcase page modules + 1
content/docsreact-page sample, from 395 doc files and 1946 fenced blocks, both before and after. Measuring the carve-out's load-bearing set over the guard's OWN population (importingcollectSources, not re-guessing it) found it suppressing exactly two lines — the two deleted here — and nothing else.Reverse-verified from the committed state: with the guard narrowed and both alias lines restored from the merge base, the gate exits 1 with exactly 2 findings, naming
crm-workbench.page.ts:49andreact-pages.mdx:145and no third file. The restore leg was proven byte-for-byte against the HEAD blobs. So the narrowing bites on the pattern and reds no unrelated existing code.The self-test grows from 30 to 37 assertions: both aliases pinned as findings, the repaired docs sample pinned silent, and the over-fire directions pinned too — a string-literal
data.records.updated, a trailing comment naming.recordsbeside a canonical read, and.recordsCount.Verification
Local gate union at
69e868cf9, the final commit. All 43 derived families green plus both convention-triggered gate-script obligations (bare-root-worklist --self-test;check:pm-dispatch-gates, 1017 cases).pnpm lint— the full-repoeslint . --no-inline-config— clean, run whole rather than narrowed.@objectstack/example-showcase: 26 files / 364 tests pass, typecheck clean, andtest/react-page-adapter-query-contract.test.tsconfirmed to have actually executed (5 tests) rather than inferred from a green suite.Two families read NOT MEASURED rather than green:
check-test-completenessandcheck:dual-build-cjs-loadsboth exit 3PREREQUISITE NOT MET(a saved turbo test log; a fullpnpm build). Neither is a finding.One red was found and fixed during this run, by the gate family rather than by review: the rewritten comment first used backtick-quoted text, and the page body lives inside a
source:template literal, so the backticks terminated it early.check:logger-receiver-detachrefused to scan the unparseable file. The comment now uses the quoting convention the rest of the file already used, and every gate was re-run afterwards on the final tree.Notes for review
skip-changesetapplied. Nothing here publishes:@objectstack/example-showcaseisprivate: true, and the other two paths are docs content and a root gate script.scripts/pr-labels.mjs:44states the criterion as "the exemption for a PR that publishes nothing". Precedent on these same files: the gate-creating PR feat(tooling): sweep the react-page useAdapter() contracts over the docs corpus too #11584 and the previous repair of this exact docs line,243218a36, both landed changeset-free. Happy to add one if this seat reads it differently.Array.isArraylimb is deliberately left in both sites. It is also unreachable against the current contract —normalizeQueryResultwraps a bare array response — and the card raises it as an open question that the triage ruling did not answer. Deciding it here would have pre-empted that, so it is reported instead. See the report comment on the issue.<list-view>) — PascalCase table reads as if it applies to both tiers #12650's html-tier / kebab-case work incontent/docs/ui/react-pages.mdxis untouched. This diff changes one line in that file.Generated by Claude Code
Generated by Claude Code