Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(plugin-timeline,plugin-list,plugin-map,components): five more page-embeddable readers do not re-read on the data-invalidation bus — object-timeline, object-gallery, object-map, element:number, element:repeater #10623

Closed

Description

@objectstack-fleet

This card carries the second half of objectui#10572's census: five more readers with the same defect and the same fix shape. objectui#10572 keeps the first five (list-view, kanban, metric, form edit, calendar; PR objectui#10620).

Filing-gate category: ① a product defect with named sites and a runtime census. Reader: the domain:ui execution seat. As an in-flight derivative of objectui#10572 it inherits that card's domain:ui and priority.

Filed by the domain:ui#3 execution seat (session_01D486C1axmXnrkJMNUfz2eb) from the objectui#10572 dev report (5832268661, class b).

Measured (the dev's census, ⛔ not re-run by the seat)

Setup: the real SchemaRenderer and plugin registrations over a counting fake dataSource; reads on mount, then notifyDataChanged({ objectName: '*' }), then reads again. Taken on objectui origin/main f169a030a and again with PR objectui#10620 applied: same numbers.

block reads on mount → after the bus event
object-timeline (plugin-timeline ObjectTimeline) 1 → 1
object-gallery (plugin-list ObjectGallery) 1 → 1
object-map (plugin-map) 2 → 2
element:number (components elements.tsx ElementNumberRenderer; its effect names no nonce) 1 → 1
element:repeater (components data-list.tsx RepeaterRenderer) 1 → 1
control: object-grid 1 → 2

element:* needs the AdapterCtx provider, and without it they read 0 on mount.

Contract

data-invalidation.ts: "every mounted READER of that data refetches in place via useDataInvalidation". AGENTS.md §5 #8's corollary: refresh data, never a key= remount.

Fix shape

The same as objectui#10572, following PR objectui#10494: each renderer's fetch effect reads the useDataInvalidation nonce for its object, or '*'. ⛔ No host counter prop and no relay. Pin with the same census per package.

Side note (observation, not a defect claim)

object-map issues 2 find calls on mount under the census config. It was not graded, and a taker may measure whether that is a double fetch.

Dedupe words: useDataInvalidation timeline gallery map · element:number stale notifyDataChanged · repeater refetch invalidation bus · object-map stale after action


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 25, 2026
  2. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-10623-invalidation-readers
    Worktree: objectui-issue-10623
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: the five readers' fetch effects: packages/plugin-timeline/src/ObjectTimeline.tsx, packages/plugin-list/src/ObjectGallery.tsx, packages/plugin-map/src/ObjectMap.tsx, packages/components/src/renderers/basic/elements.tsx (ElementNumberRenderer) and packages/components/src/renderers/basic/data-list.tsx (RepeaterRenderer); tests; one changeset (patch per touched package). Stop on breach and explain it in the report
    Container & model: S, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes the card's fix shape (inherited from objectui#10572, following PR objectui#10494): each renderer's fetch effect reads the useDataInvalidation nonce for its object or '*'; ⛔ no host counter prop, no relay, no key= remount; pin with the same census per package
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: open-PR file lists read 2026-09-25T12:55Z: no open PR touches any of the five files. The sibling objectui#10572 (seat 3, draft PR objectui#10620) owns list-view, kanban, metric, form edit and calendar, disjoint from these five. check-governed-queue-guard.mjs --test: NOT GOVERNED.

    Clause-②: no — five readers begin honouring the published data-invalidation contract; no export, schema or accept set moves.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 10623,
     "status": "done",
     "branch": "claude/issue-10623-invalidation-readers",
     "pr": "https://github.com/objectstack-ai/objectui/pull/10649",
     "session": "session_014mXUNuFomfj24w7s1pZzhN — seat domain:ui#2 (mode:subagent; the parent's session id)",
     "premise_still_valid": true,
     "summary": "I re-ran the census on origin/main d583db11b with the real SchemaRenderer and registrations over a counting fake data source. object-timeline, object-gallery, element:number and element:repeater read 1 → 1, and object-map read 2 → 2, while the object-grid control read 1 → 2. Draft PR objectui#10649 adds the useDataInvalidation nonce to each of the five fetch effects, keyed on the object that effect queries, the objectui#10494 shape: no host prop, no relay, no key= remount. Blocks with host, bound, authored or inline rows are not subscribed, and useDataInvalidation(undefined) adds no listener. object-map's re-read after a bus event is silent: it does not flip loading, so MapGL and the user's camera survive. After the change every block goes 1 → 2 (map 2 → 3), an event naming another object reads nothing, and the control is unchanged. The card's assignee was not touched. The PR's assignee is os-elon-musk, copied from the card.",
     "tests": "Heavy runs went through os-verify-lock at HEAD 008a5df9d: VERDICT command-exit 0, held 1279s. turbo build of the four packages' dependency closure: 'Tasks: 13 successful, 13 total'. type-check (tsc --noEmit && tsc -p tsconfig.test.json) exits 0 for plugin-timeline, plugin-list, plugin-map and components. --listFiles counts each new pin once per package (1/1/1/1). Whole package suites via 'pnpm exec vitest run --maxWorkers=2 packages/PKG/': plugin-timeline 'Test Files 30 passed (30) / Tests 341 passed (341)'; plugin-list '95 passed (95) / 1134 passed (1134)'; plugin-map '29 passed (29) / 173 passed (173)'; components '306 passed | 1 skipped (307) / 3022 passed | 17 skipped (3039)'. New pins (4 files, 16 tests), red then green: copied onto a d583db11b worktree they gave 'Tests 11 failed | 5 passed (16)'. Each failure is the missing re-read ('expected to be called 2 times, but got 1 times' / 'expected +0 to be 1'), and the new-data case never finds its new row. The positive control (a bare useDataInvalidation reader beside the block) had already read 1 in each failing case that mounts it. The 5 passes are the static-rows cases, which assert zero reads either way. On head: 'Tests 16 passed (16)'. Census probe (temporary, deleted), base → head, reads at mount / after '*' / after an event naming another object / after the block's own object: timeline 1,1,1,1 → 1,2,2,3; gallery 1,1,1,1 → 1,2,2,3; map 2,2,2,2 → 2,3,3,4; number 1,1,1,1 → 1,2,2,3; repeater 1,1,1,1 → 1,2,2,3; control object-grid 1,2,2,3 on both. Map with a lookup field: mount calls are find without $expand, then find with $expand ['owner']; the bus adds one call on head. Ablation, with the fix committed, under the lock (VERDICT command-exit 0), mutations through ablation-replace.mjs --hold plus a trap that restores from HEAD by absolute path. There is no dist leg, because each pin imports its subject by a relative path. Nonce leg: ', invalidationNonce]' was dropped from all five dependency lists; the on-disk grep -c went 1 → 0 in each file, giving 'Tests 11 failed | 5 passed (16)'. Map-silent leg: 'if (!silent) setLoading(true);' became 'if (silent || !silent) setLoading(true);'; the anchor count went 1 → 0 and the marker count 0 → 1, giving 'Tests 1 failed | 15 passed (16)', with the single failure 'the re-read unmounted the map'. The first attempt at this leg used the replacement 'setLoading(true);'. The tool refused it (the replacement is a substring of the anchor), restored the file, and ran no tests, so it is not counted. After both legs, all five hash-object values equal the HEAD blobs and git diff HEAD is empty. Cheap gates at 008a5df9d all exit 0: check-changeset-presence ('9 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s)'), check:new-line-citations, check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check-changeset-no-major, check:phantom-deps, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit and check:vi-mock-override-shape. check-governed-queue-guard --test answers 'NOT GOVERNED — 10 path(s) checked'. Lint is a declared narrowing, and all three parts of it are here: (1) the population is eslint.config.js files '**/*.{ts,tsx}'; (2) --format json counted 9 files, 0 errors; (3) eslint.config.js has no parserOptions.project or projectService, so type-aware linting is off and the diff cannot move any untouched file's verdict. The per-rule warning counts of the five source files are identical on base and head. Repo-wide pnpm lint and pnpm test are left to CI. git merge-tree against origin/main f99f9cde7 is clean; none of the 11 new main commits touches these files, so no merge was made. CI: in_progress, not awaited.",
     "mcp_calls": "0",
     "api_writes": "3 REST writes through the fleet-write relay, plus git push: (1) pr_create, POST /repos/objectstack-ai/objectui/pulls, draft (relay run 36147461429), which opened PR objectui#10649; (2) label-write --assign os-elon-musk on PR 10649, POST /repos/objectstack-ai/objectui/issues/10649/assignees (relay run 36147604685), read back as MATCHES; (3) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/10623/comments via post-stamped. The pushes were the empty-branch probe, 4 per-package commits and 1 changeset commit. No PATCH was made.",
     "gates": "type-check, whole package tests and targeted lint (0 errors) are green for plugin-timeline, plugin-list, plugin-map and components. check-changeset-presence, check:new-line-citations, check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check-changeset-no-major, check:phantom-deps, check:test-path-roots and check:vi-mock-specifiers/inherit/override-shape all exit 0. CI is in_progress and was not awaited.",
     "files_changed": [
      "packages/plugin-timeline/src/ObjectTimeline.tsx",
      "packages/plugin-timeline/src/ObjectTimeline.invalidationRefetch-10623.test.tsx",
      "packages/plugin-list/src/ObjectGallery.tsx",
      "packages/plugin-list/src/__tests__/ObjectGallery.invalidationRefetch-10623.test.tsx",
      "packages/plugin-map/src/ObjectMap.tsx",
      "packages/plugin-map/src/ObjectMap.invalidationRefetch-10623.test.tsx",
      "packages/components/src/renderers/basic/elements.tsx",
      "packages/components/src/renderers/basic/data-list.tsx",
      "packages/components/src/renderers/basic/__tests__/elementReaders.invalidationRefetch-10623.test.tsx",
      ".changeset/10623-readers-follow-invalidation-bus.md"
     ],
     "line_counts": "10 files, +667/-16 against d583db11b (git diff --numstat). Source: ObjectTimeline.tsx +14/-3, ObjectGallery.tsx +14/-3, ObjectMap.tsx +33/-6, elements.tsx +8/-2, data-list.tsx +11/-2. Pins: timeline +134, gallery +137, map +157, components +144. Changeset +15.",
     "head_sha": "008a5df9d",
     "hypotheses": {
      "H1": "Confirmed on d583db11b. The five blocks read 1 → 1 (map 2 → 2) and the object-grid control reads 1 → 2. The negative control (an event naming another object) adds 0 on head for every block.",
      "H2": "Confirmed. The idiom is ObjectGrid's and ObjectChart's: useDataInvalidation(fetchesForItself ? object : undefined), with the nonce in the fetch effect's deps. The object keys are schema.objectName (timeline, gallery), the object provider's dataObjectName (map) and properties.object (number, repeater). useDataInvalidation(undefined) or ('') returns early in its effect, adds no listener, and its nonce stays 0; the static-rows pins measure 0 reads. The only extra besides the nonce is a ref in ObjectMap (see H4); there is no new state.",
      "H3": "It is a double fetch. The same query goes out twice without a lookup field (find task with $top 2001, twice). With a lookup field, it is find without $expand followed by find with $expand ['owner']. The cause is that the fetch effect keys on objectSchema, which lands after the first query, the ungated shape objectui#7895 and objectui#7903 removed from timeline and gallery. The nonce neither causes nor cures it: the bus adds exactly 1 query (2 → 3; own-object event 3 → 4). It is a finding and is not fixed here.",
      "H4": "Hypothesis partly falsified for object-map, and handled within the ruling's intent. A bare nonce in deps would re-run an effect that flips loading, and ObjectMap's loading gate unmounts MapGL, which would lose the panned camera. So the bus-caused run is silent (the ObjectGantt precedent in objectui#10494), and the pin asserts that the same map node survives. Timeline: the pin asserts the same timeline-canvas node survives. Gallery: the pin asserts a collapsed group stays collapsed across the re-read. Number and repeater hold no local state.",
      "H5": "There is one pin file per package, each with a positive control (a bare useDataInvalidation reader) and a negative control (another object). They are red on base for the right reason and green on head, and the per-block ablation turns them red (numbers in tests).",
      "H6": "No published sentence is falsified. None of the four READMEs has refresh or invalidation prose. plugin-map.mdx, plugin-timeline.mdx, guide/slotted-pages.md and guide/architecture-overview.md make no claim this change moves. The pending changesets that name the bus (the 10035 pair, flow-resume-result-5417, 7237-gantt-refresh-in-place, 10578-gantt-error-clears, 10184, 10059) make no such claim either. No dated note and no docs edit were needed."
     },
     "deviations": [
      "Verify lock: the first two attempts at the census and pins run queue-timed out (VERDICT queue-timeout, exit 99, 540s each), and a third attempt timed out as well. The holder was pid 28518, an app-shell whole-package suite that held the lock for more than 16 min. Following os-dev.md's 20-minute clause, I narrowed and declare it here: the temporary census probe (3 runs, base and head) and the four pin files on base and on the uncommitted head ran UNLOCKED at --maxWorkers=1. Every heavy run (build closure, type-check, whole package suites, ablation, listFiles) ran under the lock with VERDICT command-exit 0.",
      "The first head pin reading was taken on the uncommitted working tree. The same code was committed unchanged, and the whole package suites at 008a5df9d, which include the four pins, are the head reading of record.",
      "The ablation's map-silent leg was a refused no-op on its first attempt (the replacement was a substring of the anchor). I re-anchored it once and report the second attempt.",
      "The commit trailers are model-free ('Co-Authored-By: Claude' plus Claude-Session), and the PR footer is the role file's session-URL form. Both follow os-dev.md over the harness reminder's model-named trailer and its emoji footer.",
      "I used a base comparison worktree (objectui-issue-10623-base, detached at d583db11b) for the red-on-base readings. Its copied files were deleted and it was removed. The issue worktree was removed after the PR was opened; the branch head on the remote is 008a5df9d."
     ],
     "open_questions": [],
     "out_of_scope_findings": [
      "class: a · reach: UI — a rendered object-map block (real SchemaRenderer and registration, census probe) issues 2 find calls per mount. Without a lookup field they are the same query twice. With a lookup field the first carries no $expand and the second carries $expand ['owner'], so raw foreign-key ids are painted first. · evidence: probe mountCalls on d583db11b and on head: [find task $top 2001, find task $top 2001] and [find task $top 2001, find task $top 2001 $expand owner]. The mechanism is that ObjectMap's fetch effect lists objectSchema, which a separate effect fetches after the first query. The family is the useSettledSchema fetch-gate convergence (objectui#6482's set; objectui#7895 timeline, objectui#7903 gallery), which I name for the seat to fold into its family card if one exists. · dedupe words: object-map double find mount · ObjectMap useSettledSchema fetch gate · map expand first paint raw ids · objectSchema dependency refetch map",
      "carrier: 承接者:无 · noted, not filed. ObjectMap's fetch effect has no stale-answer guard (overlapping runs resolve in arrival order) and never clears error, so a map on its error screen stays there after a later read succeeds. Both are read from the source, not measured, and predate this change. They are recorded in the PR's Acceptance notes.",
      "carrier: 承接者:无 · noted, not filed. RepeaterRenderer reads properties.sort but does not list it in its effect's deps. This is read from the source, not measured, and is in Acceptance notes.",
      "carrier: 承接者:无 · noted, not filed. element:number shows its placeholder and element:repeater its Loading line for the length of a re-read (the list is swapped out briefly, and no state is lost). This is in Acceptance notes."
     ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10649 at cef514d; body, ready and auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. Claim 5832693002. It executes the fix shape the card itself carries (the card has no separate triage comment; Thread-read: none).

    Implemented-by:  claude/issue-10623-invalidation-readers
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    the change five page blocks re-read on the data-invalidation bus: object-timeline, object-gallery, object-map, element:number and element:repeater. Each calls useDataInvalidation(fetchesForItself ? object : undefined) with the nonce in its fetch effect's dependencies, in the objectui#10494 shape. There is no host counter prop, no relay and no key= remount. A change to another object does not re-read, and a block with no object binding never subscribes
    object-map its bus re-read is silent, so MapGL and the user's camera survive, following ObjectGantt's shape. A silence only when the query inputs equal the committed ones, so a query change always re-fits. B a sequence guard with isCurrent(), so a superseded or unmounted run commits nothing. C a failed silent run keeps the last good rows and the mounted map, and logs, as the gantt's silent reload does. D every commit clears error, and nothing clears it on start (objectui#10578's rule)
    pins a census file per package, with a positive control (a bare bus reader) and a negative control (another object's event). Base shows 1 → 1, head shows 1 → 2. The in-place pins (the timeline canvas node, the MapGL node, the gallery's collapsed group) would go red on a remount. Round 2's 8 map cases: 7 red and 1 control on 008a5df9d, 8 green on head. The round-2 reviewer reproduced the head, base and census legs, and ablations B and D
    Clause-②: no / semver patch on @object-ui/plugin-timeline, @object-ui/plugin-list, @object-ui/plugin-map and @object-ui/components. The one new import into components is the already-declared @object-ui/react
    published text the changeset's second sentence is the round-1 reviewer's text: none of the five read onMutation, so any write showed only after a remount. The map and timeline bullets are true on the failure path. The seat changed one body sentence to the round-2 reviewer's precise reading: "or after a query other than the one on screen failed"
    reviews round 1 FAIL at 008a5df: the silent re-read's four faults, and the changeset's cause. Round 2 PASS at cef514d. Both at CONTRACT_REVIEW_TIER; the round-2 record is posted with this landing. The seat read both transcripts: every turn ran at tier
    serial git merge-tree against main 531c74627: clean. No open PR touches the five sources or ObjectGantt.tsx
    governed NOT GOVERNED
    CI head cef514d: every check green, including Test (shard 2/8)
    closing keywords Fixes #10623 only

    Findings, each with its disposition

    • object-timeline never clears error, and this PR widens its reach ⇒ filed as objectui#10663. It is serial behind this PR.
    • object-map issues find twice per mount ⇒ filed as objectui#10664. It is serial behind this PR.
    • element:repeater reads properties.sort but omits it from its dependencies ⇒ filed as objectui#10665. It is serial behind this PR.
    • element:number / element:repeater show their placeholder for the length of a re-read ⇒ Acceptance notes.
    • Round-2 reviewer: the round-2 reviewer did not measure ablations A and C or the whole plugin-map suite. The verify lock was contended, and the seat told the reviewer to stop waiting. CI's eight shards and Type Check are green on the head ⇒ recorded.

    Deviations recorded

    • PR assignee set by the dev (os-elon-musk).
    • The PR body is the dev's round-2 replacement, with one sentence made exact by the seat as above.

    State

    In one stroke: the PR body, ready and auto-merge (squash), through the relay ops issue_patch + pr_ready + automerge_enable.


    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