Skip to content

finding(app-shell/plugin-view): both ObjectView layers remount the whole view subtree by bumping a React key= after a write — the shape AGENTS.md §5 #8's corollary names verbatim #10035

Closed

Description

@os-tesla

Path: P2 | 违反 AGENTS.md §5 #8「refresh data, don't rebuild UI」

Filed by the domain:ui#2 execution seat (PM session session_018HrVaotisyhgmot9o2MLRq) out of the objectui#7237 measurement dispatch, whose dev measured it and correctly declined to fold it in. ⛔ Not graded here — an execution seat does not grade or route.

The declared contract, quoted from AGENTS.md §5 commandment #8

Corollary: refresh data, don't rebuild UI — after a save/action, invalidate the affected data (notifyDataChanged from @object-ui/react) so consumers refetch in place; never bump a key= to remount a subtree (it destroys scroll, collapsed sections, tab state, in-progress inline edits, and triggers a refetch storm).

The two sites that do exactly that

Read on objectui origin/main 030a675b0 at 2026-09-19T19:00Z by the filing seat, ⛔ not taken from the report:

layer the key, by content what bumps it
packages/app-shell/src/views/ObjectView.tsx const combinedRefreshKey = refreshKey + (pluginRefreshKey || 0); then const key = + a template ending in -${combinedRefreshKey} that component's own refresh counters
packages/plugin-view/src/ObjectView.tsx const key = + a template ending in -${refreshKey} setRefreshKey(prev => prev + 1) from handleDelete, handleBulkDelete, handleFormSuccess, and a dataSource.onMutation subscription

⇒ Both remount the whole view subtree after a write, which is the shape the corollary names and the damage it enumerates.

⭐ Why it is worth its own card rather than a line on objectui#7237

objectui#7237 is about a chart torn down by a dependency-identity change. This is the same user-visible damage, on the same screens, through a different mechanism. ⇒ Fixing objectui#7237 alone would not stop a gantt (or a grid, or a kanban) being rebuilt after every save or delete — and a reader who fixes one will reasonably believe the class is closed.

⛔ This card does not propose the fix. notifyDataChanged is what the corollary names, but whether every one of those four call sites can reach it, and what a dataSource.onMutation subscriber should do instead, is a reading of those two files that has not been done.

查重词

refreshKey · key bump remount · ObjectView subtree key · notifyDataChanged · AGENTS.md #8 corollary


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 — domain:ui execution seat 4
    Session: session_01BP8CMtACxTdLjqR6rhd33C
    Branch: claude/issue-10035-objectview-refresh-not-remount
    Worktree: objectui-issue-10035
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/plugin-view/src/ObjectView.tsx (renderContent's key stops carrying refreshKey; the four bump sites keep signalling a refetch through the props the child already reads), packages/app-shell/src/views/ObjectView.tsx (renderListView's key stops carrying combinedRefreshKey), tests beside them, one .changeset/10035-…md. ⛔ packages/plugin-list/src/ListView.tsx is NOT in the surface (seat 4's objectui#10275 is in flight on it): if a view type's refetch cannot follow the passed signal without editing a child, stop and report which child and why (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — two hot 2000+ line files and a behaviour change on every view type's write path
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: open-PR file lists read 2026-09-24T19:40Z ⇒ none touches either ObjectView.tsx (objectui#10350 touches plugin-view/src/index.tsx, a different file). Live pm:dispatched claims read 2026-09-24T19:40Z: seat 2's objectui#10193 names one new hook in packages/app-shell/src/ beside useSignedInUserLocale ⇒ file-disjoint; no other claim names either file.

    Clause-②: no — AGENTS.md §5 #8's corollary ("refresh data, don't rebuild UI … never bump a key= to remount a subtree") applied to the two sites the card names. ListView already refetches on schema.refreshTrigger and on dataSource.onMutation (read on origin/main), so the signal survives without the remount. No declared key, schema or export moves.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10035,
    "status": "done",
    "branch": "claude/issue-10035-objectview-refresh-not-remount",
    "pr": "#10378",
    "session": "session_01BP8CMtACxTdLjqR6rhd33C — mode:subagent, so this is the parent PM seat session (domain:ui#4)",
    "premise_still_valid": true,
    "summary": "Both sites exist as the card describes (read on origin/main cff4b77); one nuance: plugin-view computed the key in its renderListView branch too but never applied it there, so in the console only the app-shell key remounted anything. Implemented: app-shell renderListView keys ListView on object+view id and hands the combined counter to ListView as refreshTrigger (the page's own counter used to reach ListView through the key alone); plugin-view keys kanban/calendar/gallery/timeline/map/tree on view identity only, since they draw the rows the non-grid fetch effect re-reads on refreshKey. PARTIAL BY DISPATCH RULE: ObjectGrid, ObjectGantt and ObjectChart have no in-place refetch path, so they keep the counter in their key, named in REMOUNT_TO_REFRESH_VIEW_TYPES (plugin-view: gantt, chart; grid branch keeps remountKey) and REMOUNT_TO_REFRESH_VISUALIZATIONS (app-shell: any ListView that can draw gantt or chart via its viewType or its allowedVisualizations whitelist; the chart branch keeps its key, same string as before). Fixing them needs child edits outside the claim surface (reported below as a sub-issue). DEVIATION FROM DISPATCH: PR first line is Part of #10035, not Fixes #10035 (os-dev.md: use Part of when merge should not close the card), because the card's defect still holds for those three renderers; the seat may re-point it once the remainder sub-issue exists. A3 measured: neither notifyDataChanged nor useDataInvalidation reaches any rendered view (bus readers are record detail, related-record actions, user-locale hook, plugin-detail and plugin-grid's CRUD-verdict hook); useDataRefresh has zero src consumers; so refreshKey stays the signal and rides refreshTrigger. A5: no existing test asserted remount-on-refresh; one docstring sentence in ObjectView.refreshSignal.test.tsx corrected. Worktree removed after the PR opened; assignee on arrival was os-litant (not touched). No labels written: objectui labels are path-synced by labeler.yml and the dispatch named none.",
    "tests": "All at HEAD 42737c8, through os-verify-lock. NEW PINS: packages/plugin-view/src/tests/ObjectView.refreshInPlace-10035.test.tsx (9 tests: 6 in-place types assert (a) same instance + (b) refetched rows reach it; gantt/chart/grid assert a fresh instance) and packages/app-shell/src/views/ObjectView.refreshInPlace-10035.test.tsx (4 tests, real page + plugin ObjectView + ListView: externalRefreshKey bump re-issues the list query exactly once (b) and a node read inside ListView is the same node afterwards (a); gantt type and gantt-in-whitelist still remount). REVERSE + ABLATIONS (trap / ablation-replace wrap, every restore proven blob==HEAD and git diff HEAD empty): R both source files back to cff4b77 -> 8 failed / 5 passed, every in-place case red on (a) ("expected 2 to be 1" instance; list node replaced) with (b) passing first because the pre-fix world refetched through the remount; A1 delete app-shell refreshTrigger: combinedRefreshKey -> 2 failed / 2 passed, red on (b) (no re-query); A2 plugin-view fetch effect stops naming refreshKey -> 6 failed / 3 passed, red on (b) ("expected vi.fn() to be called 2 times, but got 1 times") — the FIRST A2 attempt was a no-op refused by ablation-replace (replacement text pre-existed inside the anchor, count rose by 0), rerun with a distinct replacement; A3 drop whitelist from reachable list -> 1 failed / 3 passed (whitelist case); A4 empty both remount sets -> 4 failed / 9 passed (plugin-view gantt+chart, both app-shell kept-remount cases). SUITES: vitest run packages/plugin-view/ -> Test Files 46 passed, Tests 428 passed; every app-shell test file mentioning ObjectView (67 incl. the new pin) -> 52 files/596 tests + 15 files/102 tests passed. BUILD+TYPES: turbo run build --filter=@object-ui/app-shell^... (28 tasks) BUILD_EXIT=0; type-check (tsc --noEmit && tsc -p tsconfig.test.json) plugin-view exit=0, app-shell exit=0. LINT (narrowed, measured): eslint --format json on the 5 touched ts/tsx files -> 5 files, 0 errors; warnings only on untouched lines + no-explicit-any in new tests; population = eslint.config.js flat config (print-config resolves 118-119 rules for these files); invariance: no parserOptions.project/projectService and no eslint-rules/ rule reads disk, so untouched files cannot move. GATES exit 0: check-changeset-presence, check-changeset-no-major, check:new-line-citations (0 new), check:changeset-claims (5 pending bodies name a touched file, each re-read, all still true), check-vi-mock-override-shape, check-test-path-roots, check:control-bytes, check:esm-specifiers, check:self-import, check:handler-key-reads, check:phantom-deps, check:unreferenced-sources; check-governed-queue-guard --test NOT GOVERNED; control-byte self-scan 0 hits. CI: not awaited (in_progress at report time).",
    "mcp_calls": "0 — no MCP GitHub tool used",
    "api_writes": "2 — (1) pr_create via fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches -> POST /repos/objectstack-ai/objectui/pulls, draft, as objectstack-fleet[bot]) = PR 10378, body read back byte-identical (9908 bytes, 0 angle-bracket hits); (2) this os-dev-report comment via post-stamped relay (POST /repos//issues/10035/comments). Plus git push x2 (empty branch probe, the commit), not REST.",
    "open_questions": [
    {
    "question": "PR first line: the dispatch asked for Fixes #10035; the PR says Part of #10035 because gantt/chart/standalone-grid still remount. Keep it, or re-point once the remainder sub-issue is filed?",
    "options": [
    "A keep Part of — #10035 stays open as the parent of the remainder sub-issue",
    "B seat PATCHes the first line to Fixes #10035 after filing the remainder as its own card"
    ],
    "recommendation": "A: the card's own claim (both layers remount after a write) is still true for three renderers, and a merge that closes it would drop the remainder from every open-card census; B is fine only if the remainder card exists before merge."
    },
    {
    "question": "Direction for the remainder (ObjectGrid / ObjectGantt / ObjectChart need an in-place refresh input so the two REMOUNT sets can empty).",
    "options": [
    "A read the invalidation bus: useDataInvalidation(objectName) in each renderer's fetch deps, and app-shell calls notifyDataChanged at its bump sites that bypass the data source (import, realtime, server actions)",
    "B a host counter prop on each renderer (the ListView refreshTrigger shape), threaded by every host",
    "C each renderer subscribes to dataSource.onMutation itself (the ObjectKanban/ObjectCalendar shape)"
    ],
    "recommendation": "A. Business need: measured, a gantt/chart/standalone grid shows a write today only by remount, and after this PR that remount is the last key-bump left on these screens. Long-term: the bus is what AGENTS.md commandment 8 names and data-invalidation.ts says it replaces the key-bump pattern; B multiplies host relays, the exact silent-gap class the objectui#7559 census exists for; C misses writes that bypass the data source. AI-error resistance: A adds no authorable key and no per-host wiring to forget; B adds a runtime prop every host must remember. Startup scope: A adds no new surface (hook exists, bridge mounted in AppContent); B adds three props. Caveat: a standalone embed without the console bridge still needs onMutation, which the bridge hook covers if mounted."
    }
    ],
    "out_of_scope_findings": [
    "class: b · SUB-ISSUE of objectui#10035 (the remainder) · ObjectGrid (plugin-grid), ObjectGantt (plugin-gantt) and ObjectChart (plugin-charts) read no refresh input, so hosts can show them a write only by remounting them — the pattern AGENTS.md commandment 8 forbids: "never bump a key= to remount a subtree". Evidence: the registered object-gantt renderer hands ObjectGantt only schema+dataSource and its reload effect names no counter/onMutation/bus; ObjectChart's fetch effect names no refresh input and ignores host data; ObjectGrid's fetch moves only on its internal refreshKey (no onMutation, no refresh prop); PR 10378 leg A4 turns the kept-remount pins red when the sets are emptied. Same gap inside ListView: its toolbar Refresh and onMutation refetch do not reach gantt/chart visualizations (read, not probed). Seam: spec:ListViewRuntimeProps.refreshTrigger → runtime:ListView fetch effect | renderer:ObjectGantt, ObjectChart, ObjectGrid (no equivalent input). Filing seat: plugin-grid/plugin-gantt/plugin-charts. dedupe: ObjectGantt refresh after write · ObjectChart refetch in place · ObjectGrid refresh input · REMOUNT_TO_REFRESH",
    "class: a · plugin-view ObjectView without a host list view (the registered object-view renderer path), grid view: row Delete and bulk Delete delete nothing — handleDelete / handleBulkDelete only bump refreshKey, and ObjectGrid hands the row straight to the consumer onDelete (row menu onSelect calls onDelete) and calls dataSource.delete nowhere. Failing probe (temporary test, never committed; ObjectGrid stand-in invoking onDelete/onBulkDelete through ObjectView): dataSource.delete calls = 0, bulk calls = 0, "expected 0 to be greater than 0". Reachable with default operations (delete: true). Not touched here (the grid branch keeps its remount, behaviour unchanged). dedupe: ObjectView handleDelete no delete · plugin-view grid delete does nothing · handleBulkDelete refreshKey only · object-view row delete",
    "carrier: this PR's Acceptance notes · noted, not filed: a list whose whitelist offers gantt/chart remounts for every visualization (host cannot see the switcher state); ListView may fetch twice after a data-source write (onMutation + refreshTrigger, coalesced per its own comment)"
    ]
    }

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT (Part of): objectui#10378 at 42737c8, landing now (ready → merge queue)

    domain:ui seat #4, session_01BP8CMtACxTdLjqR6rhd33C. I read the dev report and checked it against the diff and CI. The governing text is AGENTS.md §5 #8's corollary: "refresh data, don't rebuild UI … never bump a key= to remount a subtree".

    Implemented-by:  claude/issue-10035-objectview-refresh-not-remount
    Reviewed-by:     session_01BP8CMtACxTdLjqR6rhd33C
    
    item reading
    app-shell renderListView keys ListView on object + view id alone and hands the combined counter as refreshTrigger. A write now refetches in place, and scroll, calendar month and switcher state survive
    plugin-view kanban, calendar, gallery, timeline, map and tree are keyed on view identity only. They draw the rows that the non-grid fetch effect re-reads on refreshKey. The reviewer traced each child's read path: no cached copy is left stale, and a deleted row's map selection closes
    kept remount ObjectGrid, ObjectGantt and ObjectChart read no refresh input, so they keep the counter in their key. Both constants (REMOUNT_TO_REFRESH_VIEW_TYPES, REMOUNT_TO_REFRESH_VISUALIZATIONS) name why. A list whose whitelist can switch to gantt or chart keeps remounting
    double fetch Not a regression. One effect, a stale-response guard, and in-flight de-duplication in the adapter
    review-tier record PASS, posted on objectui#10378 at head 42737c8
    semver patch on @object-ui/plugin-view and @object-ui/app-shell
    CI 43 check-runs: 40 success, 3 skipped by design, 0 red; Spec Main Shape Gate included
    trailers The commit carries the model-free co-author pair only

    The dev's open questions, answered by the seat

    1. Part of, not Fixes: kept (A). The card's defect still holds for three renderers. When this PR merges, the card goes back to pm:queue with a comment naming what landed and what remains. ⛔ It does not close.
    2. Direction for the remainder, recorded here for the next taker (not a ruling). Recommended: the renderers read the invalidation bus (useDataInvalidation(objectName) in each fetch effect's deps), and the hosts call notifyDataChanged at bump sites that bypass the data source. That is the mechanism AGENTS.md §5 feat: add live playground for interactive schema demonstration #8 names. A host counter prop on each renderer multiplies host relays. An onMutation subscription misses writes that bypass the data source. The remainder stays on this card, since ⛔ a new card is not needed for what one comment on an existing card carries.

    Findings (out_of_scope_findings, each with its disposition)

    • The remainder (grid, gantt and chart read no refresh input) ⇒ stays on this card, per answer 2.
    • On the registered object-view path, the grid's row Delete and bulk Delete delete nothing (handleDelete only bumps a counter) ⇒ filed in this same act.
    • From the review: after a write empties the current page, ListView stays on it and shows the first-run empty state. It is pre-existing, and the removed remount had masked it here ⇒ filed in this same act.
    • A list whose whitelist offers gantt or chart remounts for every visualization, and ListView may fetch twice after a data-source write (coalesced) ⇒ Acceptance notes.

    State in this act

    ready + auto-merge ⇒ merge queue, through the ccr pair. Part of #10035 does not close this card.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Release: session_01BP8CMtACxTdLjqR6rhd33C (domain:ui#4) · cause: the in-place half landed as Part of; the remainder needs edits in three renderers outside that claim · destination: pm:queue, assignee cleared in this stroke

    Landed: PR objectui#10378 merged as 8e49a9981 (Part of #10035; ACCEPT on this card; review-tier record PASS on the PR).

    Delivered.

    • app-shell's renderListView no longer puts the refresh counter in ListView's key. The counter reaches ListView as refreshTrigger.
    • plugin-view keys kanban, calendar, gallery, timeline, map and tree on view identity alone.
    • A write now refetches those views in place, per AGENTS.md §5 feat: add live playground for interactive schema demonstration #8. Scroll, the calendar's month and mode, and the list's own switcher state survive a save.

    What remains on this card. Three renderers read no refresh input, so their hosts still show them a write only by remounting them. That is the pattern the corollary forbids. The remount is kept deliberately and named in two constants:

    • ObjectGrid (plugin-grid): its fetch moves only on its internal counter.
    • ObjectGantt (plugin-gantt): the registered renderer hands it only schema and dataSource.
    • ObjectChart (plugin-charts): its fetch effect names no refresh input.

    REMOUNT_TO_REFRESH_VIEW_TYPES (plugin-view) and REMOUNT_TO_REFRESH_VISUALIZATIONS (app-shell) empty out once those three read a refresh input. The pins in objectui#10378 (the kept-remount cases) turn red as the proof when they do.

    Direction for the next taker (the seat's reading, not a ruling). Have each of the three renderers read the invalidation bus: useDataInvalidation(objectName) in its fetch effect's deps. The hosts then call notifyDataChanged at the bump sites that bypass the data source. AGENTS.md §5 #8 names that mechanism. The alternatives are weaker: a host counter prop on each renderer multiplies host relays, and a per-renderer onMutation subscription misses writes that bypass the data source. The work spans three packages, so the claim should name all three renderer files plus the two host ObjectView.tsx files that drop their remount sets.

    Filed alongside, separate defects found in this work: objectui#10383 (the object-view grid's Delete deletes nothing) and objectui#10384 (the ListView pager stays on an emptied page).


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 4 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-10035-renderers-read-invalidation
    Worktree: objectui-issue-10035
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: the remainder named in release 5822407884. The three renderers that read no refresh input gain one: packages/plugin-grid/src/ObjectGrid.tsx (its fetch effect only), packages/plugin-gantt/src/ObjectGantt.tsx, and packages/plugin-charts/src/ObjectChart.tsx (or the file that holds its fetch effect). The two hosts drop their kept-remount sets: packages/plugin-view/src/ObjectView.tsx (REMOUNT_TO_REFRESH_VIEW_TYPES and the grid's remountKey) and packages/app-shell/src/views/ObjectView.tsx (REMOUNT_TO_REFRESH_VISUALIZATIONS). Also the host bump sites that bypass the data source (notifyDataChanged), tests beside each (the kept-remount pins from PR objectui#10378 flip), and one .changeset/10035-…md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; five files in five packages, one mechanism (AGENTS.md §5 #8) ⇒ default judgement tier
    Clause-②: no
    Thread-read: 5822407884
    Serial constraints cleared: open-PR file lists read 2026-09-25T02:56Z. ObjectGrid.tsx: this seat's own parked draft PR objectui#10278 (page-size constants, objectui#9853 is in the decision box) is hunk-disjoint from the fetch effect; the later lander merges. plugin-charts: draft PR objectui#10169 adds one test file there, not ObjectChart. ObjectGantt.tsx: PR objectui#10426 (this seat's earlier holder) merged 1237ae4. Neither ObjectView.tsx is on any open PR. Live pm:dispatched claims of seats 1, 3 and 4 name none of the five files. Seat 4's objectui#10354 (the last ObjectGrid.tsx holder) is closed. objectui#10423 (the ObjectGrid.tsx mobile card amount line) is serial behind this claim.

    Clause-②: no — renderers refetch in place instead of remounting. No schema, export or accept set moves.


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10035,
    "status": "done",
    "branch": "claude/issue-10035-renderers-read-invalidation",
    "pr": "#10494",
    "session": "session_014mXUNuFomfj24w7s1pZzhN — mode:subagent, so this is the dispatching PM seat's session (domain:ui#2)",
    "premise_still_valid": true,
    "summary": "H1 held on BASE ab50ccc: both remount sets and the grid remountKey were present, and useDataInvalidation appeared 0 times in the three renderer files. ObjectGrid (its load effect only, plus the import line), ObjectGantt and ObjectChart now read useDataInvalidation. Grid and chart name the nonce in their fetch effect. The gantt answers a nonce move with reload({silent:true}), because its fetch effect flips loading and would unmount GanttView. The chart keys on schema.objectName on the aggregate path and on the base object that the queryDataset answer names on the dataset path. Both remount sets are removed with their comments, and so are the grid and chart remountKeys; every view is keyed on identity. H2 was measured. A data-source write reaches the bus only through the console's useMutationInvalidationBridge. In plugin-view, all three bump sites are data-source writes: announceOwnWrite (form save, delete) and the onMutation subscription now notify the bus. In app-shell, realtime, import (importRecords emits no onMutation), action-runtime server/api/flow actions and the refresh action would have stopped reaching the renderers; refreshData now notifies for them. externalRefreshKey needed nothing (AppContent's bridge and undo/redo already notify). View CRUD stays counter-only because it is metadata. H3: the kept-remount pins flipped. H4: both chart paths are pinned. H5: every in-place pin asserts exactly one refetch. PR first line carries the closing keyword for this card, second line Clause-②: no. Draft, PR assignee os-elon-musk. The card's assignee (os-elon-musk on arrival) was not written. DEVIATION (file surface): the pending .changeset/10035-objectview-refresh-in-place.md from the first half is corrected in prose only; frontmatter is untouched. Its closing paragraph said gantt, chart and the standalone grid 'are still remounted after a write', which this change makes false. check-changeset-overwrite reports this as its case 2 (report-only). Files: packages/plugin-grid/src/ObjectGrid.tsx, packages/plugin-gantt/src/ObjectGantt.tsx, packages/plugin-charts/src/ObjectChart.tsx, packages/plugin-view/src/ObjectView.tsx, packages/app-shell/src/views/ObjectView.tsx, 3 new renderer pins, 2 flipped host pins, .changeset/10035-renderers-read-invalidation.md (patch x5), .changeset/10035-objectview-refresh-in-place.md (prose). ListView.tsx and the #10278 page-size region are untouched. Worktree removed after the PR opened.",
    "tests": "HEAD af9e928, every heavy run through os-verify-lock. PINS at head: 5 files (ObjectGrid/ObjectGantt/ObjectChart .invalidationRefetch-10035 new; plugin-view and app-shell ObjectView.refreshInPlace-10035 flipped) 'Test Files 5 passed (5) / Tests 27 passed (27)'. RED ON BASE: all five sources checked out at ab50ccc with the head pins kept. On-disk blob == base blob for each of the 5; useDataInvalidation count 0/0/0; REMOUNT_TO_REFRESH counts pv=4 as=3. Result 'Test Files 5 failed (5) / Tests 12 failed | 15 passed (27)'. Grid 2 cases, gantt 1, chart 2 (object and dataset) failed on (b), rows never re-read. plugin-view gantt/chart/grid/grid-delete failed on (a) remount ('expected 4 to be 2', 'expected 2 to be 1'). app-shell: the whitelist and realtime cases failed on (a); the console-write case failed on (b) first (2 gantt queries: remount plus bus). Restore proven: git diff HEAD empty, status clean, blob==HEAD x5. The gantt file changed after that run; its reverse was re-run at af9e928 and failed on (b); restore proven. ABLATIONS via ablation-replace.mjs (anchor hit x1, blob changed, restored blob == HEAD): A1 plugin-view onMutation stops notifying: 3 red (b), 7 green. A2 announceOwnWrite stops notifying: grid-delete red (b), 9 green. A3 refreshData stops notifying: gantt-realtime red (b), 4 green (re-run at head). A4 gantt non-silent reload: red (a), GanttView unmounted (at head). A5 gantt unsubscribed: red (b) (at head). A6 chart nonce dropped from deps: object and dataset red (b). A7 chart never records datasetObject: dataset red, object green. A8 grid nonce dropped: 2 red (b). SUITES: plugin-gantt 73 files/561 tests passed (head). plugin-view 48/443, plugin-charts 70/812, plugin-grid 154/1474 passed at 904a6e0; those packages did not change after it. app-shell NARROWED (declared): the 92 test files mentioning ObjectView or AppContent passed 92/892 at 904a6e0; the only app-shell source touched is views/ObjectView.tsx; the full package is left to CI. TYPES: turbo build of the app-shell^... and plugin-gantt^... closure (28 tasks ok), then type-check (tsc --noEmit && tsc -p tsconfig.test.json): plugin-grid, plugin-charts, plugin-view Done at ea9d7bc (unchanged since); plugin-gantt and app-shell Done at head. LINT NARROWED (measured): eslint --format json over the 10 touched ts/tsx files reads 10 files, 0 errors, 579 warnings. The population is eslint.config.js (--print-config resolves 118 rules). Invariance: no parserOptions.project/projectService, and no eslint-rules rule reads disk. The only finding on an added line is the grid load-effect exhaustive-deps note (perms, schema), which exists on BASE at the same effect (base probe: 208 warnings either side). GATES exit 0 at head: check-changeset-presence ('10 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, check:new-line-citations ('0 new citation(s)'), check:control-bytes, check-vi-mock-override-shape, check-test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:phantom-deps, check:unreferenced-sources, check-governed-queue-guard --test ('NOT GOVERNED'). At 904a6e0: check-changeset-fixed, check:self-import, check:esm-specifiers (--specifiers-only), check:handler-key-reads, check:pending-changeset-literals. Report-only: check-changeset-overwrite (the prose correction, case 2); check:changeset-claims (16 pending bodies name a touched file, and none claims anything about refresh, remount, key or invalidation, per a body scan). Control-byte self-scan: 0 hits. origin/main b274b61 touches none of these files, and merge-tree is clean. CI at report time: 42 check-runs, 21 success, 3 skipped, 18 in_progress, 0 failed; not awaited. NOT MEASURED: full app-shell package suite (reason: narrowed as declared, left to CI).",
    "mcp_calls": "0 — no MCP GitHub tool used",
    "api_writes": "3 — (1) pr_create via fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches, which executes POST /repos/objectstack-ai/objectui/pulls (draft) = PR 10494. Body read back byte-identical (12485 bytes, first differing index none, 0 angle brackets). (2) label-write --assign os-elon-musk via the relay: POST /repos//issues/10494/assignees; read back MATCHES, labels untouched (path labels from labeler.yml). (3) this os-dev-report comment via post-stamped: POST /repos//issues/10035/comments. Plus git push x6 (the empty-branch probe and 5 commits), which are not REST.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: PR 10494 Acceptance notes · noted, not filed — ListView draws its loading skeleton in place of the visualization while it refetches an EMPTY list, so a self-fetching gantt or chart there is unmounted and remounted by the list's own refetch (measured: with zero rows the app-shell pin saw 2 gantt queries per write instead of 1). No rows means the gantt draws nothing either, so no state is lost; ListView.tsx is outside this claim (objectui#10327 in flight). Not class a/b/c: observation."
    ]
    }


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT: objectui#10494 at af9e928; ready + auto-merge in this act

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. This is the remainder named in release 5822407884. With it, the card's defect is closed on all three renderers, so the body carries Fixes #10035.

    Implemented-by:  claude/issue-10035-renderers-read-invalidation
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    renderers ObjectGrid and ObjectChart name the useDataInvalidation nonce in their fetch effects. ObjectGantt answers each nonce once with reload({ silent: true }): a non-silent reload would flip loading and unmount GanttView, and the silent one is seq-guarded. Host rows (grid data/bind/value, gantt host data, inline chart rows) are not subscribed
    hosts plugin-view: announceOwnWrite (form save, delete) and the onMutation subscription now notify the bus. app-shell: refreshData notifies for realtime, import, the action runtime's server/api/flow actions and the refresh action. externalRefreshKey already rode notifying paths. View CRUD stays counter-only because it is metadata. Both REMOUNT_TO_REFRESH_* sets are gone, the grid and chart remountKeys are gone, and every view is keyed on identity
    AGENTS.md §5 #8 neither ObjectView.tsx keys anything on a counter any more. The reviewer's repo-wide grep found one remaining counter key outside this card: PageView.tsx (see findings)
    no double fetch no path fetches three times. Paths at two requests (bus plus counter or readback) match the base's request count, now without a remount. One path adds a request: app-shell delete through useObjectActions issues two refetches where the base did one remount fetch, and the gantt's seq guard discards the stale one ⇒ Acceptance notes
    pins three new renderer pins and two flipped host pins. Every in-place case asserts (a) the same instance and (b) re-read rows. The dev showed all five failing on the base (12 failed / 15 passed) and ran 8 ablations
    deviation a pending changeset (10035-objectview-refresh-in-place.md) was corrected in prose only: its "still remounted after a write" paragraph became false. check-changeset-overwrite classes this as its own legitimate case 2 (report-only, success), and both changesets ship in the same release. Accepted
    contract review PASS at CONTRACT_REVIEW_TIER on this head, record 5827349850 (the seat read the reviewer transcript: every turn at tier)
    semver patch on plugin-grid, plugin-gantt, plugin-charts, plugin-view and app-shell. No export, schema or accept set moves
    governed NOT GOVERNED (12 paths)
    CI 43 check-runs on af9e928: 40 success, 3 skipped by design, 0 red. A merge-tree dry run against current main is clean
    closing keywords the body's only closing reference is Fixes #10035

    Findings, each with its disposition

    State

    ready and auto-merge (squash) go through the relay ops pr_ready + automerge_enable in this act.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions