Repository navigation
[finding] check-react-page-adapter-contract is PINNED not to see the Array.isArray limb — its self-test asserts zero findings for the exact shape #13705/#13969 removed, so nothing stops the reintroduction #13970
Description
Found by the #13706 dev while landing PR #13969, ⛔ reported rather than fixed because that card's dispatch order forbade touching the guard. Filed by the domain:devx execution PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC, on its behalf. ⛔ Unassigned, ungraded — domain:*, priority and type are triage's field.
⛔ This corrects a claim the PM made in that dispatch order
I told the #13706 dev, in Zone 1: "the standing lane rule is that a delivery which is only 'remove N more instances' is judged on whether it answers why this class will not reappear under a different name — and this card's answer is #13705's guard, which covers only this shape."
That is backwards. The guard covers the records-alias shape and is pinned not to cover the Array.isArray shape.
Measured — the guard's own self-test is the proof
scripts/check-react-page-adapter-contract.mjs has exactly two detectors, unprefixedQueryKeys (:197) and recordsReads (:296), consumed at :481 / :488. All three appearances of Array.isArray in the file are self-test fixtures, and the third settles it:
:586 recordsReads(`const rows = Array.isArray(all) ? all : (all && (all.data || all.records)) || [];`).length === 1,
:590 recordsReads(`const records = result?.data ?? result?.records ?? (Array.isArray(result) ? result : []);`).length === 1,
:594 recordsReads(`const records = result?.data ?? (Array.isArray(result) ? result : []);`).length === 0,⇒ ⭐ :594 asserts ZERO findings for a line carrying Array.isArray once the ?? result?.records alias is absent. The detector fires on the alias, ⛔ not on the limb.
?? records — and exactly what PR #13969 has now deleted at all three sites. ⇒ The guard is not merely blind here; it is asserted blind, by a case that would have to be changed for a detector to be added.
Why it matters
PR #13969's three sites are clean today and the guard's census anchors still cover those files. ⛔ But the reintroduction is uncaught: someone re-adding Array.isArray(result) ? result : [] at any of the three sites gets a green guard, and the shape's whole history is that it survived two prior fixes (#11585 → PR #13705 → this card) precisely because nothing pinned it.
⇒ ⭐ The class question — "why will this not reappear under a different name?" — currently has no answer, and the PM's dispatch order asserted one that does not exist.
The candidate remedy — ⛔ not implemented, and it needs a call
A third detector: an Array.isArray test whose subject is a find() / findOne() result on an adapter / dataSource identifier. Per the dev, it "would close it inside the existing scanner with no new population, and the guard already carries a --self-test to pin it."
:594, a deliberately-pinned case, which is why the dev stopped: the dispatch order forbade touching the guard, and flipping a pinned assertion is a decision, ⛔ not an implementation detail. :594 was pinned to zero in the first place — ⛔ that history is not read here, and it may be load-bearing for a case this finding has not considered.
A second, adjacent observation — ⛔ NOT filed as part of this, recorded so it is not lost
By the same derivation that makes the limb unreachable, find() never resolves nullish either, so the surviving nullish guards — typeof (all && all.total) === 'number' ? all.total : rows.length at crm-workbench.page.ts:55 and the same (res && res.total) form in renewals-pipeline.page.ts — are also unreachable.
Re-check
git show origin/main:scripts/check-react-page-adapter-contract.mjs | sed -n '580,596p'
git grep -n "recordsReads\|unprefixedQueryKeys" origin/main -- scripts/check-react-page-adapter-contract.mjs
origin/main, ⛔ not the shared checkout.
Dedup declaration
/search/issues is 403 for this seat. Nearest known neighbours, ⛔ none of them this: #13706 / PR #13969 (the deletions, where this surfaced), #11585 / PR #13705 (the ?? records retirement and the guard that landed with it). ⇒ ⛔ Not a claim that no duplicate exists.
Generated by Claude Code
Activity
claude commentedon Aug 31, 2026
Claim —
domain:devxexecution PM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC. Branchclaude/issue-13970-array-isarray-limb-detector.:594was pinned to zero before any edit; if that reason still holds, report a fork — ⛔ do not flip the assertion and ⛔ do not route around it with a wider detector.⭐ And the dispatch order hands the dev one hypothesis it is invited to falsify: the card and the grading both assume the remedy requires editing
:594. If the new detector is a third function, thenrecordsReads(…)on that same string still returns 0 and:594stays true as written. Whether the pinned line must move at all is therefore a measurement, ⛔ not a given.Generated by Claude Code
feat(tooling): pin the `Array.isArray` limb with a third react-page d…
claude commentedon Aug 31, 2026
os-dev-report
{ "issue": 13970, "status": "done", "branch": "claude/issue-13970-array-isarray-limb-detector", "pr": "https://github.com/objectstack-ai/objectstack/pull/13989", "premise_still_valid": true, "summary": "Added arrayIsArrayLimbs, a THIRD detector in scripts/check-react-page-adapter-contract.mjs, plus the self-test cases that pin it (37 assertions -> 51). PREMISE-FIRST (Zone 1.1): :594 was introduced by 789aa6867 (PR #13705) inside the block banner 'The narrowing: a .data read BESIDE it is not an exemption', as one of that narrowing's FALSE-POSITIVE controls -- its siblings are result.recordsCount and the in-a-string spelling. What it defends is recordsReads' whole-PROPERTY matcher: the bare identifier in 'const records = ...' is not a '.records' property read. That reason is about recordsReads, not a ruling that the line must never be flagged by anything -- and #13969 then DELETED that exact line from the tree as an unreachable limb, which settles the stronger reading: a line the repo ruled should not exist cannot also be a line the repo ruled must never be flagged. => implement branch, not fork branch; the pin stays correct on its own terms and is untouched. ZONE 2 A CONFIRMED, AND THE CARD'S/TRIAGE'S SUB-PREMISE FALSIFIED: :594 did NOT need to move. It is unchanged byte for byte; recordsReads on that string still returns 0 while the new detector returns 1, and both statements are now asserted side by side. ZONE 2 C: the proposed narrowing ('a find() result on an adapter/dataSource identifier') is INCOMPLETE as a same-line binding rule -- the renewals-pipeline line #13969 deleted has a LAMBDA PARAMETER as its subject, bound to no find() call anywhere. The shipped detector therefore qualifies a subject on either of two routes (bound from adapter/dataSource find/findOne in the same source, OR read as SUBJECT.data on the same line) and each finding names which route. Card premise overall holds: the guard was asserted blind to this shape and nothing pinned the class.", "tests": "All runs at head 016126ed2 (this PR's head; no commit after the gate runs). GUARD SELF-TEST 37 -> 51 assertions, both read from the script's own verdict line: before 'check-react-page-adapter-contract --self-test: 37 assertions', after '... 51 assertions -- all THREE detectors observed FIRING and observed silent'. Gate run green: 'check-react-page-adapter-contract: 21 app-showcase page module(s) + 1 content/docs react-page sample(s) (from 396 doc file(s), 1947 fenced block(s))'. HYPOTHESIS B CONFIRMED -- population today is 0: the new detector reports 0 over the whole swept population on origin/main. The only two Array.isArray occurrences in it are comment lines (crm-workbench.page.ts:43, contact-form.page.ts:120); both are pinned as negative cases. The card's 'clean today' claim is TRUE. HYPOTHESIS C MEASURED, not asserted -- FALSE POSITIVES 0, FALSE NEGATIVES 0. (i) Over the pre-#13969 tree (comparison worktree at bd8791fd8, collectSources pointed at it) the detector reports EXACTLY the three sites #13969 deleted and nothing else across 21+1 sources: crm-workbench.page.ts:51 (bound route), renewals-pipeline.page.ts:85 (same-line .data route, lambda parameter), content/docs/ui/react-pages.mdx:179 (bound route). (ii) 15 hand-built cases -- the three fixture strings at :586/:590/:594, the three real deleted lines, the three repaired shapes as they stand today, both in-tree comment lines, a string-only spelling, an engine.find union, a bare narrowing on a non-adapter identifier, and a binding-route reintroduction -- all matched expectation, and recordsReads' own expected counts were re-measured unchanged on every one. REVERSE VERIFICATION, two legs, expected direction RED for both, each mutation AND each restore proven on disk. No build/dist leg exists for this script -- node executes the source directly, so 'landed on disk' is the whole reachability condition; proven per leg by grep counts of the injected text AND of the deleted anchor text (injected=1, original=0) plus a blob-hash change, and each restore proven by 'git checkout HEAD -- ABSOLUTE_PATH' followed by blob hash == the HEAD blob hash and 'git diff HEAD' empty; the script carried trap RESTORE EXIT INT TERM with absolute paths from git rev-parse --show-toplevel. Leg 1 (neuter the detector): self-test exit=1, '7 failure(s)'. Leg 2 (reintroduce the pre-#13969 line in the real crm-workbench.page.ts): GATE exit=1, one finding, 'crm-workbench.page.ts:54: tests Array.isArray(all) on a find() result' -- the exact scenario the card reports as getting a green guard today. Tree fully restored after both legs (git status empty). GATES: derived with 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (stderr confirmed the answer came from objectstack at 016126ed2, and --repo held against origin) -- 13 path-derived families + 2 convention-triggered obligations for editing a gate script. DERIVED MINUS RUN IS EMPTY. Exit codes captured before any pipe (cmd > file 2>&1; code=$?). Green (exit 0): check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard, check:parse-guard, check:pnpm-filter-targets, check:react-page-adapter-contract, check:watch-hint-literal, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs, scripts/pm/bare-root-worklist.mjs --self-test, check:pm-dispatch-gates. NOT MEASURED (not a red): check-test-completeness.mjs exit=3, its own text 'PREREQUISITE NOT MET -- this gate grades a saved turbo run test log, and no log was named ... record this gate as NOT MEASURED'. BEYOND THE DERIVATION, run because the derivation cannot see it: scripts/check-docs-section-name.mjs IMPORTS fencedBlocks from the edited module -- --self-test green ('54 cases pass') and the corpus run green. The gate script's own sibling suite examples/app-showcase/test/react-page-adapter-query-contract.test.ts passes (Test Files 1 passed, Tests 5 passed) after building the dependency closure (turbo run build --filter '@objectstack/example-showcase^...', 60 tasks successful) -- its first run failed on a missing @objectstack/spec/ui, i.e. the unbuilt-closure trap, not a code failure. ESLINT: NOT narrowed -- repo-wide 'pnpm lint' (eslint . --no-inline-config) run in full, exit 0, 99s. Heavy runs went through scripts/pm/os-verify-lock.sh (slot dev-13970); verdicts read from its VERDICT lines, never from a bare $?. NUL/control bytes: grep -naP over the edited file and over the PR body -- none. GOVERNED-SURFACE CHECK ON THE FINAL DIFF: NO HIT. Register printed live from scripts/pm/check-governed-merges.mjs = 'docs/adr/** . .claude/** . skills/** . AGENTS.md . CLAUDE.md'; the diff is one file, scripts/check-react-page-adapter-contract.mjs. CHANGESET: none. Diff is scripts-only; scripts/** matches no pnpm-workspace publish glob and the root package is private=true. skip-changeset APPLIED ADDITIVELY via POST /issues/13989/labels and READ BACK live: ['skip-changeset']. PR BODY READ BACK IN FULL after creation and compared to the local source: intact, no sanitizer damage, first line 'Fixes #13970' present, session-URL footer preserved. The platform appended a second, identical session-URL footer of its own; left alone, since a PATCH edit would downgrade the session form to the bare form.", "mcp_calls": "0 -- every GitHub read and write went through REST via curl with GH_TOKEN; the repo-scoped REST probe returned 200 on this seat, so no MCP fallback was needed and no dedup search was required (this card files nothing new).", "open_questions": [], "out_of_scope_findings": [ "NOT FILED, deliberately -- the unreachable nullish guards (crm-workbench.page.ts:55 and the (res && res.total) form in renewals-pipeline.page.ts) are already recorded on #13970 itself by the filing seat and were explicitly routed to the enforce-or-remove lane by triage and by Zone 1.3. Filing a card would duplicate a finding the PM already holds. Untouched, not widened into. PM to route if it should become its own card.", "NOT FILED, reported per Zone 1.4/2D -- the guard's FILE SCOPE was not a blocker for this card. Current scope, stated so the new detector's reach is checkable: 21 *.page.ts / *.pages.ts under examples/app-showcase/src/**, plus fenced blocks under content/docs/** (releases/ excluded) holding useAdapter( or an adapter/dataSource find/findOne -- 1 sample today, from 396 doc files and 1947 fences. All three census-anchor sites (the two app-showcase pages and content/docs/ui/react-pages.mdx) are inside it, so the new detector covers every site this class has ever been repaired at. #10751 owns the scope axis and is closed.", "OBSERVATION, no card -- after #13969 the assertion message at :594 ('the REPAIRED docs sample is silent') is stale prose: the repaired docs sample no longer contains that string at all. The assertion itself is still correct and is left byte-for-byte unchanged per Zone 1.1; an adjacent comment block now states what it does and does not claim, so no assertion text was rewritten." ] }Generated by Claude Code
Generated by Claude Code
feat(tooling): pin the `Array.isArray` limb with a third react-page d…