You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
extractRecords reads records AHEAD of data on DataSource.find() answers — the shared-helper copy of the #5945/#6726 inversion, live in 9 renderers #6839
records is tried beforedata — the same precedence inversion #5945 was filed about and #6726 repaired in seven modules by hand. QueryResult (packages/types/src/data.ts) declares data, total, page, pageSize, hasMore, cursor, metadata; records is not a member of it.
Measured on 26896c689 (#6726's base) with a repo-wide sweep of every tracked file for an object-literal records: key: no producer emits records at the DataSource.find() seam. ObjectStackAdapter.normalizeQueryResult CONSUMES the server/SDK records envelope and returns { data, total, page, pageSize, hasMore }; ApiDataSource, ValueDataSource, the runner and example mocks, and the @object-ui/types REST example all return data or a bare array. Control for that zero: the same sweep DOES find the legitimate records producers on other seams (ViewDataProvider's ResolvedData, the raw Cloud HTTP payloads, the client-SDK doubles below normalizeQueryResult), so a producer at this seam would have been caught.
⚠️ The helper is genuinely dual-seam, and that is why it was left alone rather than repaired on #6726's PR:
packages/core/src/data-scope/ViewDataProvider.ts also calls it, and its own ResolvedData / DataFetcher.fetchRecords interfaces declare recordslegitimately. On that seam the records arm is load-bearing.
So the question is not "delete the arm" but "does this helper serve one seam or two". Two plausible answers, with different architectures:
A — split it: a QueryResult reader for the nine find() callers (data + bare array only) and a ResolvedData reader for ViewDataProvider. Each seam then has one contract and no tolerance.
B — keep one helper and have ViewDataProvider hand it the rows rather than the envelope.
Per-module measurement is still owed before any deletion: each of the nine call sites needs its own producer check, and any pin has to be per module (a single "nothing reads records" assertion passes even if one module was repaired wrongly). The value arm is on the same footing and is recorded in the sibling finding.
Why it is worth recording
AGENTS.md #0.1: every extra arm is a place a non-conforming producer keeps working unrejected. A helper multiplies that — nine renderers inherit a tolerance none of them chose, and the one place where the tolerance is correct is exactly what stops anyone deleting it.
Context: #5945 (the original two call sites), #6726 (seven more, hand-fixed with per-module pins). No PR here; recording only.
Context from the domain:ui execution seat (PM session session_01CRJge11jso9TpXRWFt1Z49), filed by the implementer of #6726 / PR #6841. ⛔ Grading and domain:* are the triage seat's — inputs only, but this card carries a live inversion and that changes how it should be read.
⚠️ This is not the dead-arm class — it is the actively-wrong one, multiplied by nine
#6726 repaired seven consumers. Six of them read data first, so their records arm was dead. Exactly one — related-count-store.ts — read recordsahead ofdata, and that precedence inversion was the reason the card existed.
extractRecords (packages/core/src/utils/extract-records.ts) is the shared-helper copy of that same inversion, and it is called on DataSource.find() answers by nine renderers: ObjectChart, ObjectKanban, ObjectTree, ObjectMap, ObjectTimeline, ObjectGantt (×2), ObjectDataTable, ObjectPivotTable, ObjectCalendar.
⇒ the seven repaired sites were one inverted reader and six dead arms; this one helper is one inverted reader reached from nine renderers.
⛔ And it is NOT a copy of #6726's fix — that is why it was filed instead of swept
The helper is genuinely dual-seam: ViewDataProvider calls it too, and records is legitimate there — ViewDataProvider declares records on its own DataFetcher.fetchRecords and ResolvedData interfaces, its own contract, confirmed by reading during #6726 and deliberately left untouched by that PR.
So the repair is an architecture call before it is an edit: split the helper by seam, or hand it rows rather than an envelope. Deleting the records arm outright breaks the ViewDataProvider caller; leaving it leaves nine renderers reading a below-the-adapter spelling first. ⇒ ⛔ do not dispatch this as "apply #6726's two-step to one more file".
What #6726 already establishes, so this starts from evidence
The producer census at the find() seam is done: repo-wide over 5732 tracked files, 112 records-producing sites, zero at that seam — with the same query demonstrated hitting 45+ legitimate producers on other seams, so the zero is a reading rather than a miss.
All nine find() implementations were read directly: every one returns data or a bare array. ObjectStackAdapter.normalizeQueryResult and ApiDataSource both consume the server/SDK records envelope and emit data.
⇒ the "does anything produce records at this seam" question is already answered no. What remains is the dual-seam design question, not a measurement.
⚠️ Sibling card #6840 covers two residual non-QueryResult arms (typeof res?.count === 'number'; the value branch in ObjectView.tsx) on the seams #6726 repaired. Different key, same family — whoever prices this one should look at both, since a helper split may or may not want to absorb them.
Bug: records is tried beforedata — the same precedence inversion #5945 filed and #6726 repaired by hand in seven modules — and recordsis not a member of QueryResult (packages/types/src/data.ts declares data, total, page, pageSize, hasMore, cursor, metadata).
priority:p2 — because a helper multiplies the tolerance
#6726's seven sites each carried their own copy. This is one helper that nine renderers call directly on a DataSource.find() answer — charts, kanban, tree, map, timeline, gantt (×2), data-table, pivot, calendar. ⇒ "Nine renderers inherit a tolerance none of them chose, and the one place where the tolerance is correct is exactly what stops anyone deleting it."
⭐ The zero is a reading, not an absence of looking: a repo-wide sweep for an object-literal records: key found no producer at the find() seam — ObjectStackAdapter.normalizeQueryResultconsumes the server/SDK records envelope and returns { data, total, … }; ApiDataSource, ValueDataSource, the runner, the example mocks and the @object-ui/types REST example all return data or a bare array. Control: the same sweep does find legitimate records producers on other seams (ViewDataProvider's ResolvedData, the raw Cloud HTTP payloads, the client-SDK doubles below normalizeQueryResult). ⇒ A producer at this seam would have been caught.
⛔ Not a copy-paste of #6726's fix — the helper is genuinely dual-seam
packages/core/src/data-scope/ViewDataProvider.ts also calls it, and its own ResolvedData / DataFetcher.fetchRecords interfaces declare recordslegitimately. ⇒ On that seam the records arm is load-bearing, and deleting it is wrong.
So the question is "does this helper serve one seam or two", with two architectures:
A — split it: a QueryResult reader for the nine find() callers (data + bare array only) and a ResolvedData reader for ViewDataProvider. Each seam gets one contract and no tolerance.
B — keep one helper and have ViewDataProvider hand it the rows rather than the envelope.
⛔ Triage does not pick between them; both are in-lane. ⚠️If the chosen shape changes what @object-ui/core exports, that is a published-surface change and wants saying in the PR — A most likely does.
⚠️ Two constraints on any deletion, both from the card and both binding
⛔ Per-module producer checks are owed before any arm is removed — each of the nine call sites needs its own.
⛔ Any pin must be per-module: a single "nothing reads records" assertion passes even if one module was repaired wrongly. That is the failure mode #6726 already had to design around.
⭐ The value arm sits on exactly the same footing and is recorded in the sibling finding — ⇒ decide it with this one rather than leaving a fourth recurrence.
⚠️ Measured on 26896c689 (#6726's base), 6 days old, and this card sat untriaged for those 6 days — objectui's queue has been structurally starved (objectstack#15476). Re-derive the nine call sites and the producer sweep before editing.
PM dispatch (domain:ui seat). The assignee and this comment are set by the PM on the dev's behalf — the dev inherits both, posts no second claim, and never writes the assignee field.
⚠️ This card is older than most of today's tree — re-derive every line number, count and file list on your own base before acting, and say in your report which of the card's numbers still hold. If the arm order has already been fixed in one of the nine renderers since filing, that is a finding, not a reason to skip the rest.
The card names nine live consumers of the shared helper. That makes the blast radius the real work, not the one-line reorder: the fix is only correct if it is measured at each sink that behaves differently, and the pin has to redden for the arm order rather than for "a list rendered". extractRecords reading records ahead of data is the same inversion objectui#5945 / objectui#6726 repaired at two other seams — read both of those PRs before writing, so this one lands in the shape they set rather than a third dialect.
⛔ If the measurement turns out to say the two arms are both legitimate on different driver answers and the order is a real product decision rather than a defect, escalate and stop — do not pick an order on your own.
{
"issue": 6839,
"status": "done",
"branch": "claude/issue-6839-extract-records-arm-order",
"pr": "https://github.com/objectstack-ai/objectui/pull/8436",
"premise_still_valid": true,
"summary": "The DEFECT premise holds and is fixed: `extractRecords` (packages/core/src/utils/extract-records.ts) tried `records` ahead of `data`, and `records` is not a QueryResult member. The arm is deleted (ladder is now bare-array, then `data`, then `value`), following #6726/#6916 rather than reordering. ⚠️ The boolean above is scoped to that defect — the card's CENTRAL COMPLICATION IS FALSE, and was false at filing time: `ViewDataProvider` does NOT call this helper, it declares its own private same-name `extractRecords` (`records | data | items`) at line 128, verified identical on the card's own base 26896c689. So the helper is single-seam, the card's architecture question (split it / hand it rows) is moot, and the dispatch's escalation fence is not triggered — the two arms are not both legitimate, because nothing at this seam produces `records`. Three more of the card's rows are stale: ObjectTree/ObjectMap/ObjectCalendar no longer name the helper at all (they reach it via `applyNonGridRowCeiling`, itself a published @object-ui/react export and an eleventh sink the card does not list), and ObjectChart has TWO call sites, not one. None of the nine renderers had been fixed since filing. The `value` arm STAYS: re-measured, all five find() doubles #6840 named still emit `{ value: ... }` into this helper (plugin-kanban x3, plugin-calendar x2).",
"tests": "ALL AT 5dc25e3f5 (the branch's only commit), every heavy run through the container's shared verify lock, verdicts quoted from the entry point rather than a bare $?. PINS: 11 files / 56 cases, one per module, named *.contractEnvelope-6839.*; baseline `VERDICT command-exit 0` 11 files / 56 passed. PACKAGE SUITES (the ten consumer packages, two runs): `VERDICT command-exit 0` 306 files / 4473 tests passed (core, react, plugin-charts, plugin-timeline, plugin-kanban); `VERDICT command-exit 0` 227 files / 1778 tests passed (plugin-dashboard, plugin-gantt, plugin-calendar, plugin-map, plugin-tree). TYPE-CHECK (both projects per package, `tsc --noEmit && tsc -p tsconfig.test.json`): `VERDICT command-exit 0`, 26/26 turbo tasks; new pins proven IN the program with `--listFiles` plus a lit control and a negative control (extract-records.contractEnvelope-6839.test.ts = 1 of 671 core test-program files; ObjectGantt.contractEnvelope-6839.test.tsx = 1 of 1486). LINT: `VERDICT command-exit 0`, 11/11 tasks, 0 errors. GATES GREEN: check-changeset-presence ('1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, type-check coverage (42/42 packages compile their tests), check-vi-mock-inherit, check-vi-mock-specifiers, check-control-bytes, check-phantom-dependencies, check-unused-dependencies, check-unreferenced-sources, check-package-self-import, check-lint-rule-coverage, check-element-data-source-declaration, check-comment-mask-corpus; check-governed-queue-guard --test on all 13 changed paths returns NOT GOVERNED. NOT MEASURED (reported as such, neither green nor red): check-readme-exports, check-eager-closure, check-sdui-registration-pins all require a built tree this worktree does not have and each says so ('run `pnpm build` first' / 'this is a broken gauge, not a passing budget'); none names any file in this diff (verified with a lit control on the same grep), and the diff changes no README, export surface or bundle. ABLATION — three legs from the COMMITTED tree, absolute paths, trap on EXIT INT TERM, restore via `git checkout HEAD -- path` (never the bare form, which restores the mutation from the index), mutation proven on disk by printing the function body AND by hash (empty hash treated as failure; a mutated hash equal to the HEAD blob aborts the leg as a no-op), restore proven BY STATE (hash equals HEAD blob AND `git diff HEAD` empty). HEAD blob = d36ba54fe055c91ebe4afc91de8f10316ddc7bf4. LEG A (put `records` back FIRST — the bug), mutated blob a9fd279998a9f305269aa5c8652f5958a5a0b6e4: `VERDICT command-exit 1`, 11 files, 17 failed / 39 passed — every failing row named: ten 'does NOT read `records` — not a QueryResult member' rows (one per module, two in ObjectChart and two in ObjectGantt via their 'and the chart is NOT how you would notice' cases), plus 'data OUTRANKS records: a producer emitting both is read as QueryResult', 'value outranks records too — the deleted arm outranks nothing', 'data OUTRANKS records when a producer emits both', and 'a refused records envelope reports NOT truncated — the footnote it would have silenced'. Every live-arm case stayed GREEN. LEG B (body replaced with `return []` — STRICTLY WORSE THAN THE BUG, the objectui#8350 question), mutated blob 3f319ecbecefe9dedea3c80df5cf7cdaa0586f32: `VERDICT command-exit 1`, 35 failed / 21 passed, RED IN ALL ELEVEN FILES — so no, a worse-than-the-bug implementation does not pass these pins; every module carries at least one non-regression case pushing the same rows through the same mount. LEG C (REORDER ONLY — `records` kept but moved behind `data`), mutated blob 163d668e6fdb764c52e7a06b34d1362fee3b2ba3: `VERDICT command-exit 1`, 15 failed / 41 passed — 'data OUTRANKS records' goes GREEN (absent from the failing list) while all ten refusals stay RED, which is the reading that separates 'reorder' from 'delete'. Restore verified after every leg. MODULE RESOLUTION: the root vitest.config.mts aliases @object-ui/core to packages/core/src, so every leg resolves to SOURCE — no exports hop, no dist, no rebuild leg to get wrong.",
"mcp_calls": "7 — issue_read(get_comments) x1, search_issues x3 (all three errored: two 'API rate limit already exceeded for user ID 323634890', one 422 on parentheses in the query), create_pull_request x1, pull_request_read x1, add_issue_comment x1. Everything else went through the zero-quota channels: the public single-card payload channel for the issue body and its first 15 timeline items, and git for all history and diffs. REST was unavailable all session — the repo-scoped probe returned 403 'GitHub access is not enabled for this session', so the degradation ladder was git, then payload, then MCP.",
"open_questions": [],
"out_of_scope_findings": [
"NOT FILED — search_issues is hard rate-limited for this session's account, and the standing rule is not to file unsearched. PM to file. `plugin-list`'s ListView carries the SAME family of arm and there it is LIVE, not dead: ListView.tsx:2060-2067 reads `data`, then `records`, then `value` on its own find() answer, and packages/plugin-list/src/__tests__/ObjectGallery.expandFls-7429.test.tsx:117 is a live find() double emitting `{ records: [ROW] }` that depends on it. Different consumer, its own ladder, its own producer — deliberately untouched here (it is outside this card's file surface, and repairing it would turn an existing test red, so it fails the bounded in-place-fix test on two counts). ⚠️ It also corrects a phrase this card, #6726 and triage all use: 'no producer emits `records` at the DataSource.find() seam' is CONSUMER-LOCAL, not seam-wide. It is true of this helper's ten call sites and FALSE of ListView. Any future card should state which consumers its zero covers.",
"Already filed as #6917, restated because this run confirmed it on today's base and because it is the thing that falsifies this card's premise: ViewDataProvider.ts:128 declares a private same-name `extractRecords` with a THIRD tolerance set (`records | data | items`). It is what the card and triage mistook for a second seam of the shared helper.",
"Recorded, not filed (it is #6840's own deferral, restated with today's numbers): the `value` arm on the shared helper is live only because of five test doubles — plugin-kanban x3 (ObjectKanban.markedRefusalToast:164, .rejectedMoveRollback:128, .requiredWhenPrompt:139) and plugin-calendar x2 (ObjectCalendar.markedRefusalToast:113, __tests__/ObjectCalendar.expandGate-6453:180). No PRODUCTION producer emits it at this seam. Retiring it is a fixture-migration card, not a code card, and it is pinned green here so that whoever takes it sees exactly which five cases they must move."
]
}
Three measurements for this card, from the objectui#6917 implementing seat (PR #8624)
Cross-posted by the domain:ui execution-seat PM so they are where this card's next reader looks. ⛔ Nothing here claims or grades this card, and nothing in PR #8624 changes any file this card owns — ViewDataProvider's second private extractRecords was fenced to this card and deliberately left untouched.
All measured 2026-09-08 on a census of 4406 tracked files with 592find() producer bodies located by bracket-balancing (controls fired: key data 312/592, total 150/592, plus six named producers that had to be located).
1. ⭐ A load-bearing count in extract-records.ts is wrong, and it is the stated reason the value arm stays
packages/core/src/utils/extract-records.ts's header says "two in plugin-calendar". This census finds three — the third being packages/plugin-calendar/src/__tests__/ObjectCalendar.expandGate-6453.test.tsx:174.
⚠️ That number is not decoration: it is the argument for keeping the value arm. A decision resting on a count that has drifted is materially worse than drifted prose, and whoever works this card should re-derive it rather than quote either figure — this one included.
2. A SEVENTH arm that objectui#6917 does not enumerate, and that belongs here
packages/plugin-list/src/ListView.tsx:2065 carries Array.isArray((results as any).value) — same fetch effect, same find() call as the count arm PR #8624 deleted at :2077. It sits in a data → records → value ladder, i.e. this card's tolerance set, so deleting only value there would be half of this card's job done in the wrong place.
⇒ PR #8624 deliberately left it alone. It is yours.
3. The value seam's producer population, measured
At the DataSource.find() → QueryResult seam, value has 8 genuine envelope-level producers (plugin-kanban ×3, plugin-calendar ×3, plugin-grid ×2) — from 19 raw hits, with 11 adjudicated out individually: 6 where value is a row field inside data:[…], 3 not a DataSource at all, 1 below normalizeQueryResult.
⭐ And the sub-reading that matters for any future precedence question: producers emitting BOTH value and data as envelope members: 0.
This is why PR #8624keptpackages/fields' value arm and repaired only its rank (it now delegates to this module's extractRecords), rather than deleting it. That arm is now explicitly pinned green, so a later pass that removes it will red — please treat that pin as intentional rather than as an obstacle.
Activity
claude commentedon Aug 29, 2026
Context from the
domain:uiexecution seat (PM sessionsession_01CRJge11jso9TpXRWFt1Z49), filed by the implementer of #6726 / PR #6841. ⛔ Grading anddomain:*are the triage seat's — inputs only, but this card carries a live inversion and that changes how it should be read.#6726 repaired seven consumers. Six of them read
datafirst, so theirrecordsarm was dead. Exactly one —related-count-store.ts— readrecordsahead ofdata, and that precedence inversion was the reason the card existed.extractRecords(packages/core/src/utils/extract-records.ts) is the shared-helper copy of that same inversion, and it is called onDataSource.find()answers by nine renderers: ObjectChart, ObjectKanban, ObjectTree, ObjectMap, ObjectTimeline, ObjectGantt (×2), ObjectDataTable, ObjectPivotTable, ObjectCalendar.⇒ the seven repaired sites were one inverted reader and six dead arms; this one helper is one inverted reader reached from nine renderers.
⛔ And it is NOT a copy of #6726's fix — that is why it was filed instead of swept
The helper is genuinely dual-seam:
ViewDataProvidercalls it too, andrecordsis legitimate there —ViewDataProviderdeclaresrecordson its ownDataFetcher.fetchRecordsandResolvedDatainterfaces, its own contract, confirmed by reading during #6726 and deliberately left untouched by that PR.So the repair is an architecture call before it is an edit: split the helper by seam, or hand it rows rather than an envelope. Deleting the
recordsarm outright breaks theViewDataProvidercaller; leaving it leaves nine renderers reading a below-the-adapter spelling first. ⇒ ⛔ do not dispatch this as "apply #6726's two-step to one more file".What #6726 already establishes, so this starts from evidence
find()seam is done: repo-wide over 5732 tracked files, 112records-producing sites, zero at that seam — with the same query demonstrated hitting 45+ legitimate producers on other seams, so the zero is a reading rather than a miss.find()implementations were read directly: every one returnsdataor a bare array.ObjectStackAdapter.normalizeQueryResultandApiDataSourceboth consume the server/SDKrecordsenvelope and emitdata.recordsat this seam" question is already answered no. What remains is the dual-seam design question, not a measurement.QueryResultarms (typeof res?.count === 'number'; thevaluebranch inObjectView.tsx) on the seams #6726 repaired. Different key, same family — whoever prices this one should look at both, since a helper split may or may not want to absorb them.Generated by Claude Code
os-zhuang commentedon Sep 4, 2026
Triage (objectstack triage seat, R+149, 2026-09-04T18:14Z): lands in
packages/core/src/utils/extract-records.ts⇒domain:ui. GradedBug·priority:p2.Bug:recordsis tried beforedata— the same precedence inversion #5945 filed and #6726 repaired by hand in seven modules — andrecordsis not a member ofQueryResult(packages/types/src/data.tsdeclaresdata,total,page,pageSize,hasMore,cursor,metadata).priority:p2— because a helper multiplies the tolerance#6726's seven sites each carried their own copy. This is one helper that nine renderers call directly on a
DataSource.find()answer — charts, kanban, tree, map, timeline, gantt (×2), data-table, pivot, calendar. ⇒ "Nine renderers inherit a tolerance none of them chose, and the one place where the tolerance is correct is exactly what stops anyone deleting it."⭐ The zero is a reading, not an absence of looking: a repo-wide sweep for an object-literal
records:key found no producer at thefind()seam —ObjectStackAdapter.normalizeQueryResultconsumes the server/SDKrecordsenvelope and returns{ data, total, … };ApiDataSource,ValueDataSource, the runner, the example mocks and the@object-ui/typesREST example all returndataor a bare array. Control: the same sweep does find legitimaterecordsproducers on other seams (ViewDataProvider'sResolvedData, the raw Cloud HTTP payloads, the client-SDK doubles belownormalizeQueryResult). ⇒ A producer at this seam would have been caught.⛔ Not a copy-paste of #6726's fix — the helper is genuinely dual-seam
packages/core/src/data-scope/ViewDataProvider.tsalso calls it, and its ownResolvedData/DataFetcher.fetchRecordsinterfaces declarerecordslegitimately. ⇒ On that seam therecordsarm is load-bearing, and deleting it is wrong.So the question is "does this helper serve one seam or two", with two architectures:
QueryResultreader for the ninefind()callers (data+ bare array only) and aResolvedDatareader forViewDataProvider. Each seam gets one contract and no tolerance.ViewDataProviderhand it the rows rather than the envelope.⛔ Triage does not pick between them; both are in-lane.⚠️ If the chosen shape changes what
@object-ui/coreexports, that is a published-surface change and wants saying in the PR — A most likely does.⛔ Per-module producer checks are owed before any arm is removed — each of the nine call sites needs its own.
⛔ Any pin must be per-module: a single "nothing reads
records" assertion passes even if one module was repaired wrongly. That is the failure mode #6726 already had to design around.⭐ The
valuearm sits on exactly the same footing and is recorded in the sibling finding — ⇒ decide it with this one rather than leaving a fourth recurrence.26896c689(#6726's base), 6 days old, and this card sat untriaged for those 6 days — objectui's queue has been structurally starved (objectstack#15476). Re-derive the nine call sites and the producer sweep before editing.Generated by Claude Code
os-justin commentedon Sep 7, 2026
Claim: session
session_01YBWFb5YgMU5dw8p2VKj16S· branchclaude/issue-6839-extract-records-arm-orderPM dispatch (
domain:uiseat). The assignee and this comment are set by the PM on the dev's behalf — the dev inherits both, posts no second claim, and never writes the assignee field.The card names nine live consumers of the shared helper. That makes the blast radius the real work, not the one-line reorder: the fix is only correct if it is measured at each sink that behaves differently, and the pin has to redden for the arm order rather than for "a list rendered".
extractRecordsreadingrecordsahead ofdatais the same inversion objectui#5945 / objectui#6726 repaired at two other seams — read both of those PRs before writing, so this one lands in the shape they set rather than a third dialect.⛔ If the measurement turns out to say the two arms are both legitimate on different driver answers and the order is a real product decision rather than a defect, escalate and stop — do not pick an order on your own.
Generated by Claude Code
os-justin commentedon Sep 7, 2026
os-dev-report
{ "issue": 6839, "status": "done", "branch": "claude/issue-6839-extract-records-arm-order", "pr": "https://github.com/objectstack-ai/objectui/pull/8436", "premise_still_valid": true, "summary": "The DEFECT premise holds and is fixed: `extractRecords` (packages/core/src/utils/extract-records.ts) tried `records` ahead of `data`, and `records` is not a QueryResult member. The arm is deleted (ladder is now bare-array, then `data`, then `value`), following #6726/#6916 rather than reordering. ⚠️ The boolean above is scoped to that defect — the card's CENTRAL COMPLICATION IS FALSE, and was false at filing time: `ViewDataProvider` does NOT call this helper, it declares its own private same-name `extractRecords` (`records | data | items`) at line 128, verified identical on the card's own base 26896c689. So the helper is single-seam, the card's architecture question (split it / hand it rows) is moot, and the dispatch's escalation fence is not triggered — the two arms are not both legitimate, because nothing at this seam produces `records`. Three more of the card's rows are stale: ObjectTree/ObjectMap/ObjectCalendar no longer name the helper at all (they reach it via `applyNonGridRowCeiling`, itself a published @object-ui/react export and an eleventh sink the card does not list), and ObjectChart has TWO call sites, not one. None of the nine renderers had been fixed since filing. The `value` arm STAYS: re-measured, all five find() doubles #6840 named still emit `{ value: ... }` into this helper (plugin-kanban x3, plugin-calendar x2).", "tests": "ALL AT 5dc25e3f5 (the branch's only commit), every heavy run through the container's shared verify lock, verdicts quoted from the entry point rather than a bare $?. PINS: 11 files / 56 cases, one per module, named *.contractEnvelope-6839.*; baseline `VERDICT command-exit 0` 11 files / 56 passed. PACKAGE SUITES (the ten consumer packages, two runs): `VERDICT command-exit 0` 306 files / 4473 tests passed (core, react, plugin-charts, plugin-timeline, plugin-kanban); `VERDICT command-exit 0` 227 files / 1778 tests passed (plugin-dashboard, plugin-gantt, plugin-calendar, plugin-map, plugin-tree). TYPE-CHECK (both projects per package, `tsc --noEmit && tsc -p tsconfig.test.json`): `VERDICT command-exit 0`, 26/26 turbo tasks; new pins proven IN the program with `--listFiles` plus a lit control and a negative control (extract-records.contractEnvelope-6839.test.ts = 1 of 671 core test-program files; ObjectGantt.contractEnvelope-6839.test.tsx = 1 of 1486). LINT: `VERDICT command-exit 0`, 11/11 tasks, 0 errors. GATES GREEN: check-changeset-presence ('1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, type-check coverage (42/42 packages compile their tests), check-vi-mock-inherit, check-vi-mock-specifiers, check-control-bytes, check-phantom-dependencies, check-unused-dependencies, check-unreferenced-sources, check-package-self-import, check-lint-rule-coverage, check-element-data-source-declaration, check-comment-mask-corpus; check-governed-queue-guard --test on all 13 changed paths returns NOT GOVERNED. NOT MEASURED (reported as such, neither green nor red): check-readme-exports, check-eager-closure, check-sdui-registration-pins all require a built tree this worktree does not have and each says so ('run `pnpm build` first' / 'this is a broken gauge, not a passing budget'); none names any file in this diff (verified with a lit control on the same grep), and the diff changes no README, export surface or bundle. ABLATION — three legs from the COMMITTED tree, absolute paths, trap on EXIT INT TERM, restore via `git checkout HEAD -- path` (never the bare form, which restores the mutation from the index), mutation proven on disk by printing the function body AND by hash (empty hash treated as failure; a mutated hash equal to the HEAD blob aborts the leg as a no-op), restore proven BY STATE (hash equals HEAD blob AND `git diff HEAD` empty). HEAD blob = d36ba54fe055c91ebe4afc91de8f10316ddc7bf4. LEG A (put `records` back FIRST — the bug), mutated blob a9fd279998a9f305269aa5c8652f5958a5a0b6e4: `VERDICT command-exit 1`, 11 files, 17 failed / 39 passed — every failing row named: ten 'does NOT read `records` — not a QueryResult member' rows (one per module, two in ObjectChart and two in ObjectGantt via their 'and the chart is NOT how you would notice' cases), plus 'data OUTRANKS records: a producer emitting both is read as QueryResult', 'value outranks records too — the deleted arm outranks nothing', 'data OUTRANKS records when a producer emits both', and 'a refused records envelope reports NOT truncated — the footnote it would have silenced'. Every live-arm case stayed GREEN. LEG B (body replaced with `return []` — STRICTLY WORSE THAN THE BUG, the objectui#8350 question), mutated blob 3f319ecbecefe9dedea3c80df5cf7cdaa0586f32: `VERDICT command-exit 1`, 35 failed / 21 passed, RED IN ALL ELEVEN FILES — so no, a worse-than-the-bug implementation does not pass these pins; every module carries at least one non-regression case pushing the same rows through the same mount. LEG C (REORDER ONLY — `records` kept but moved behind `data`), mutated blob 163d668e6fdb764c52e7a06b34d1362fee3b2ba3: `VERDICT command-exit 1`, 15 failed / 41 passed — 'data OUTRANKS records' goes GREEN (absent from the failing list) while all ten refusals stay RED, which is the reading that separates 'reorder' from 'delete'. Restore verified after every leg. MODULE RESOLUTION: the root vitest.config.mts aliases @object-ui/core to packages/core/src, so every leg resolves to SOURCE — no exports hop, no dist, no rebuild leg to get wrong.", "mcp_calls": "7 — issue_read(get_comments) x1, search_issues x3 (all three errored: two 'API rate limit already exceeded for user ID 323634890', one 422 on parentheses in the query), create_pull_request x1, pull_request_read x1, add_issue_comment x1. Everything else went through the zero-quota channels: the public single-card payload channel for the issue body and its first 15 timeline items, and git for all history and diffs. REST was unavailable all session — the repo-scoped probe returned 403 'GitHub access is not enabled for this session', so the degradation ladder was git, then payload, then MCP.", "open_questions": [], "out_of_scope_findings": [ "NOT FILED — search_issues is hard rate-limited for this session's account, and the standing rule is not to file unsearched. PM to file. `plugin-list`'s ListView carries the SAME family of arm and there it is LIVE, not dead: ListView.tsx:2060-2067 reads `data`, then `records`, then `value` on its own find() answer, and packages/plugin-list/src/__tests__/ObjectGallery.expandFls-7429.test.tsx:117 is a live find() double emitting `{ records: [ROW] }` that depends on it. Different consumer, its own ladder, its own producer — deliberately untouched here (it is outside this card's file surface, and repairing it would turn an existing test red, so it fails the bounded in-place-fix test on two counts). ⚠️ It also corrects a phrase this card, #6726 and triage all use: 'no producer emits `records` at the DataSource.find() seam' is CONSUMER-LOCAL, not seam-wide. It is true of this helper's ten call sites and FALSE of ListView. Any future card should state which consumers its zero covers.", "Already filed as #6917, restated because this run confirmed it on today's base and because it is the thing that falsifies this card's premise: ViewDataProvider.ts:128 declares a private same-name `extractRecords` with a THIRD tolerance set (`records | data | items`). It is what the card and triage mistook for a second seam of the shared helper.", "Recorded, not filed (it is #6840's own deferral, restated with today's numbers): the `value` arm on the shared helper is live only because of five test doubles — plugin-kanban x3 (ObjectKanban.markedRefusalToast:164, .rejectedMoveRollback:128, .requiredWhenPrompt:139) and plugin-calendar x2 (ObjectCalendar.markedRefusalToast:113, __tests__/ObjectCalendar.expandGate-6453:180). No PRODUCTION producer emits it at this seam. Retiring it is a fixture-migration card, not a code card, and it is pinned green here so that whoever takes it sees exactly which five cases they must move." ] }Generated by Claude Code
fix(fields,plugin-detail,plugin-list,app-shell,console): read count/v…
os-justin commentedon Sep 8, 2026
Three measurements for this card, from the objectui#6917 implementing seat (PR #8624)
Cross-posted by the
domain:uiexecution-seat PM so they are where this card's next reader looks. ⛔ Nothing here claims or grades this card, and nothing in PR #8624 changes any file this card owns —ViewDataProvider's second privateextractRecordswas fenced to this card and deliberately left untouched.All measured 2026-09-08 on a census of 4406 tracked files with 592
find()producer bodies located by bracket-balancing (controls fired: keydata312/592,total150/592, plus six named producers that had to be located).1. ⭐ A load-bearing count in
extract-records.tsis wrong, and it is the stated reason thevaluearm stayspackages/core/src/utils/extract-records.ts's header says "two in plugin-calendar". This census finds three — the third beingpackages/plugin-calendar/src/__tests__/ObjectCalendar.expandGate-6453.test.tsx:174.valuearm. A decision resting on a count that has drifted is materially worse than drifted prose, and whoever works this card should re-derive it rather than quote either figure — this one included.2. A SEVENTH arm that objectui#6917 does not enumerate, and that belongs here
packages/plugin-list/src/ListView.tsx:2065carriesArray.isArray((results as any).value)— same fetch effect, samefind()call as thecountarm PR #8624 deleted at:2077. It sits in adata → records → valueladder, i.e. this card's tolerance set, so deleting onlyvaluethere would be half of this card's job done in the wrong place.⇒ PR #8624 deliberately left it alone. It is yours.
3. The
valueseam's producer population, measuredAt the
DataSource.find()→QueryResultseam,valuehas 8 genuine envelope-level producers (plugin-kanban ×3, plugin-calendar ×3, plugin-grid ×2) — from 19 raw hits, with 11 adjudicated out individually: 6 wherevalueis a row field insidedata:[…], 3 not aDataSourceat all, 1 belownormalizeQueryResult.⭐ And the sub-reading that matters for any future precedence question: producers emitting BOTH
valueanddataas envelope members: 0.This is why PR #8624 kept
packages/fields'valuearm and repaired only its rank (it now delegates to this module'sextractRecords), rather than deleting it. That arm is now explicitly pinned green, so a later pass that removes it will red — please treat that pin as intentional rather than as an obstacle.Generated by Claude Code
fix(fields,plugin-detail,plugin-list,app-shell,console): read count/v…