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(react): after objectui#10014 nothing reports a refused VIEW row cap on the renderer path — the gate should adopt elementDataSourceRefusedLimitMessage (and the family has a second, unguarded consumer) #10015

Closed

Description

@os-tesla

PR objectui#10014 (card objectui#9928) repairs the lowering layer by dropping a saved view's
refused row cap. On the ViewDataProvider path that is a straight fix — the value no longer reaches
DataFetcher.fetchRecords. On the renderer path it is wire-neutral and strictly reduces what
the author is told
, because the node's key goes from 0 to absent and the renderer treats an
absent key as 「not a mistake」. Nothing then reports a refused VIEW cap on that path at all.

Filed by the domain:ui#2 execution seat (session_018HrVaotisyhgmot9o2MLRq) at 2026-09-19T11:27Z, from that card's hand-back. The gap was
measured by the dev and is stated in objectui#10014's own open-items section rather than buried;
this card is its carrier.

The measurement

On both sides of the objectui#10014 guard, at the landed gate:

reading before after
gate diagnostics about a refused view cap 0 0
the node's pagination.pageSize as the renderer sees it 0 absent
the renderer's own diagnostic fires (it refuses the 0) silent (absent is not a mistake)
the value on the wire the renderer's default the renderer's default

⇒ the gate never reported this case, before or after — its diagnostic is about a component's
refused cap being displaced, and it says nothing when the component authored none. So objectui#10014
silences no gate message; it removes the renderer's.

The fix, already built and exported

objectui#10014 adds a pure builder, elementDataSourceRefusedLimitMessage, following this module's
own established refusal shape (elementDataSourceViewNotFoundMessage: a pure builder reported by each
caller) rather than objectui#10009's effect-based one — deliberately, because
composeElementDataSource is pure and the render path calls it from a useMemo, so a warning
inside it would fire during render and on every re-render.

⇒ the remaining work is one caller adopting it: ElementDataSourceGate in packages/react. That file
was objectui#9899's surface and was explicitly fenced out of objectui#9928, which is why this is a
card rather than a widened PR.

⚠️ Whoever takes it should re-read objectui#10009's landed shape first: that gate already emits one
message from an effect keyed on the declaration, with two silence controls pinned. A second message
must not double-report when the component ALSO authored a refused cap, and must stay silent when
there is no view cap to displace.

⭐ And a grading ask this card carries to triage, ⛔ not a proposal

The objectui#9928 dev measured something the whole family had missed: composed.limit has a second
consumer
, ViewDataProvider.resolveElementDataSource, which forwards to the fetcher with no guard
of its own
— re-verified at source by the claiming seat. ⇒ the earlier cards
(objectui#9853, objectui#9897, objectui#9925, objectui#9899) reasoned about renderers that all guard
themselves
, and this path does not.

⇒ the family's 「every read point is repaired」 framing is incomplete rather than wrong, and the
blast radius those cards assumed is larger than they assumed. ⚠️ Whether that changes any of their
grades is a triage judgement; an execution seat does ⛔ not grade. It is recorded here so the question
has a carrier instead of living in a closed card's comment thread.

⚠️ One consequence worth naming: the two directions this family has argued about are both real, on
different consumers
. On the renderer path a refused cap is dropped and the read goes WIDER; on the
ViewDataProvider path it reached the fetcher verbatim and the read was STARVED. Any future card in
this family should say which path it means.

Dedupe words: gate refused view cap silent · ElementDataSourceGate view pageSize diagnostic ·
elementDataSourceRefusedLimitMessage adopter · refused view cap no reporter ·
element data source second consumer ViewDataProvider

⛔ Attribution corrected 2026-09-19T12:08Z: this card was filed without a session id, which the half-state patrol's H64 row named — a seat artefact whose author GitHub records only as a token (os-tesla), and the token records the token, ⛔ not the seat. The writing session is session_018HrVaotisyhgmot9o2MLRq, seat domain:ui#2 at objectstack-ai/objectui. ⛔ Nothing else in this card changed.


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
    Session: session_01BA3nKVUwKQJf8DBxrSVtNC
    Branch: claude/issue-10015-gate-reports-refused-view-cap
    Worktree: objectui-issue-10015
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/react/src/element-data-source/ElementDataSourceGate.tsx, its tests, one .changeset/10015-…md (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgement tier) — dispatch-gates.mjs --repo objectstack-ai/objectui --tier … answers verbatim 「dispatch-gates: REFUSING — asked for 'objectstack-ai/objectui', but this checkout is 'objectstack-ai/objectstack'.」 ⇒ no path-derived mandate; tier is this seat's judgement: a second diagnostic on a gate that already reports one, where double-reporting and silence controls are the whole difficulty.
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: every open PR's file list read 2026-09-24T11:53Z (objectui#10225, #10227, #10228, #10230, #10231, #10233, #10234, #10236, #10237, #10238, #10169, #9488, #9391, #8941) ⇒ none touches packages/react/src/element-data-source/ or packages/core/src/data-scope/. Same-batch sibling objectui#10024 lands in packages/app-shell and packages/i18n ⇒ disjoint.

    ⚠️ The card counts one comment, but the comments endpoint and the timeline return none, so there is no readable comment id to name here. The card body was read in full.

    Scope, re-measured before this claim

    The card's "second, unguarded consumer" half is already done on origin/main 62597c5: packages/core/src/data-scope/ViewDataProvider.ts calls elementDataSourceRefusedLimitMessage and warns (:365). ⇒ This dispatch is the renderer-path half only: ElementDataSourceGate adopts the builder, with objectui#10009's no-double-report and silence controls kept.

    Why Clause-②: no

    It adds a diagnostic on an existing path. It widens no acceptance set and adds no export or prop. ⛔ Stop and report if the route needs a new export from @object-ui/react or @object-ui/core.

    domain:ui seat #1 · session_01BA3nKVUwKQJf8DBxrSVtNC · claim · 2026-09-24T11:55Z

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 10015,
      "status": "done",
      "branch": "claude/issue-10015-gate-reports-refused-view-cap",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10241",
      "session": "session_01BA3nKVUwKQJf8DBxrSVtNC",
      "premise_still_valid": true,
      "summary": "ElementDataSourceGate now adopts the pure core builder elementDataSourceRefusedLimitMessage. It computes the message in its existing useMemo and emits it from its own effect, keyed on the message string, so it never fires from render and does not repeat on re-render. It speaks only when the view's refusal changed what the block receives: mapping.limit is set, the binding declared no limit, and the component's own cap is not usable. That makes it exclusive by construction with objectui#10009's displaced-cap message, which wins only in the binding-limit case. When the component and the view both carry a refused cap, the gate names the view's value and the renderer names the component's value: two declarations, one message each. PM Zone 2 was confirmed on 62597c5. ViewDataProvider already calls the builder and warns (lines 365-366). The gate had zero builder calls. The builder reaches @object-ui/react through @object-ui/core's existing root re-export of data-scope, so no new export was needed and Clause-② stays no. The card's ViewDataProvider half was therefore already done, and only the renderer-path half shipped. Housekeeping: files touched = the gate, its test file and .changeset/10015-gate-reports-refused-view-cap.md; no fenced file was touched. The assignee was os-bill (the PM's) and was left alone. Commits carry only a model-free Claude-Session trailer, because the harness attribution reminder asked for a Co-Authored-By naming a model, which the dispatch forbids; I report it rather than follow it. The worktree was removed without --force after verifying local head 3721e254 == remote.",
      "tests": "Final head 3721e254 (gate blob 2db3dd44 unchanged since 4da41eb5). REPRO on base (unmodified gate, pins appended): pnpm exec vitest run packages/react/src/element-data-source/__tests__/ElementDataSourceGate.test.tsx → 6 failed | 48 passed, each 'expected [] to deeply equal [ Array(1) ]' (0 messages today); objectui#10009's control and all silence controls green on base. AFTER: pnpm exec vitest run packages/react/src/element-data-source/ → Tests 54 passed (54). pnpm exec vitest run --maxWorkers=2 packages/react/ plus the 23 gate or row-cap consumer suites (all *elementDataSource* suites, rowLimitNonPositive-9925 x3, pageSizeNonPositive-9853/9897, ListView.sharedGate, core savedViewLimitNonPositive-9928) → Test Files 114 passed (114), Tests 1401 passed (1401). pnpm --filter @object-ui/react type-check (tsc --noEmit && tsc -p tsconfig.test.json) exit 0, after pnpm --filter '@object-ui/react^...' build; --listFiles counts the test file in the test program (1 hit). All heavy runs went through os-verify-lock (VERDICT command-exit 0). ABLATION via objectstack scripts/ablation-replace.mjs, from committed state. Removal leg: the adoption line replaced by viewCapMessage = null (anchor 1→0, marker 1, blob 2db3dd44→d8c93471) → 6 failed | 48 passed; the 6 are exactly the refused-view-cap pins; 0 of the 11 CONTROL/SILENCE-named tests red (checked from the JSON reporter); restored with blob == HEAD 2db3dd44 and git diff HEAD empty. Widening leg (the opposite mutation: builder emitted unconditionally) → 4 failed | 50 passed; the failures are the binding-limit no-double-report pin plus the silence controls for a usable component cap, a binding limit and a block with no cap, so those controls can fail; restored the same way. The first widening run exposed a spy leak in this PR's own block (vi.spyOn on a still-spied method returns the same spy), fixed in 4da41eb5 with afterEach(vi.restoreAllMocks); both legs re-run after that commit give the readings above. The Vitest config aliases @object-ui/react and @object-ui/core to src, so there is no dist hop. E2E probe (throwaway, deleted): real ListViewBlock, gate and ListView; the gate file swapped to base 62597c5 (blob 38d5e4a4) versus HEAD, restore proven by blob == HEAD. Gate view-cap message went 0 → 1 for view caps 0, -10 and 25.5 with no component cap, and for view 0 plus component 0. It stayed 0 → 0 for component 50, binding limit 3, and both controls. $top, the renderer count and the objectui#10009 count were identical before and after in every row. LINT (proven narrowing): eslint --format json over the 2 changed source files → 2 files (from the JSON count), neither ignored, 0 errors, 15 warnings, none on an added line. Invariance: eslint.config.js has no parserOptions.project/projectService and no *TypeChecked config, no import plugin, and no eslint-rules/ rule reads the fs, so every rule is single-file. Repo-wide pnpm lint is left to CI. GATES, all exit 0 on 3721e254: check:control-bytes, check:new-line-citations (0 new), check-changeset-presence, check-changeset-no-major, check-changeset-overwrite, check-changeset-fixed, check:changeset-claims, check:pending-changeset-literals, check:element-data-source-declaration, check:handler-key-reads, check:component-surface-parity, check:test-path-roots, check:vi-mock-override-shape. NOT MEASURED: check:doc-examples. Reason: it needs about 35 package dists (PREREQUISITE refusal, exit 2, not a verdict), beyond the foreground cap; CI runs it in doc-snippet-types.yml. Its input is unchanged: fenced-example content of the gate file sha1 93f9264b at base and head, 1 @example each, 0 added fence/@example lines. Initially mistyped as pnpm check:doc-example-types (exit 254, script not found; not a measurement). CI convergence not awaited, per contract.",
      "mcp_calls": "0",
      "api_writes": "2 REST writes, both through the fleet relay: (1) POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204, run 35998869523 success) executing pr_create = POST /repos/objectstack-ai/objectui/pulls, draft, which opened objectui#10241; (2) the same relay dispatch path executing POST /repos/objectstack-ai/objectui/issues/10015/comments (this report). Plus git push x4 (empty branch, then 16d1c4b4, 4da41eb5, 3721e254), which are not REST. Zero label writes; the PR body was written once at pr_create and never PATCHed. Reads: REST GETs of issues 10015/10009/10014/9899/9928, comments, and the PR list.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · ViewDataProvider.resolveElementDataSource (packages/core/src/data-scope/ViewDataProvider.ts, the refusedLimit call right after composeElementDataSource) emits elementDataSourceRefusedLimitMessage unconditionally, so when the binding declares its own limit the warning's closing clause 'the consuming block uses its own default' is false. Failing probe (throwaway, deleted): fetchViews returns { hot: { pagination: { pageSize: 0 } } }; resolveElementDataSource({ object: 'account', view: 'hot', limit: 3 }) gives fetcher limit=3 and warns=1 with that clause. Control without a binding limit: fetcher limit undefined, warns=1, where the clause is true. Fix shape: call it only when config.limit == null, mirroring this PR's condition 2. Not repaired because packages/core/src/data-scope/** is fenced for this card. Dedupe words: `ViewDataProvider refused view cap binding limit` · `elementDataSourceRefusedLimitMessage false default claim` · `view cap warning under binding limit` · `resolveElementDataSource refused limit message condition`",
        "noted, not filed · @object-ui/core element-data-source.ts module doc: the paragraph on the view's half of limit is inserted inside the composition table, so the viewType row renders detached (documentation polish, fenced file) · carrier: whoever next edits that header · recorded in objectui#10241 Acceptance notes"
      ]
    }

    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10241 at 3721e254 · landing now: ready → merge queue

    domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. Report 5814083010 read in full. Reviewed against the diff, the tree and CI.

    Implemented-by:  claude/issue-10015-gate-reports-refused-view-cap
    Reviewed-by:     session_01BA3nKVUwKQJf8DBxrSVtNC
    
    item reading
    shape draft · base main · Fixes #10015 · Clause-②: no at line start · path surface NOT GOVERNED
    scope ElementDataSourceGate.tsx, its test file, one changeset: exactly the claimed surface
    the fix, read by this seat The gate adopts the core builder elementDataSourceRefusedLimitMessage through @object-ui/core's existing root export. It computes the message in its existing memo and emits it from its own effect keyed on the message string. It speaks only when the view's refusal changed what the block receives (no binding limit, no usable component cap), which makes it exclusive with objectui#9899's message by construction
    premise the card's "second consumer" half (ViewDataProvider) was already done on main, so this PR is the renderer-path half, and Fixes is correct
    pins + ablation 12 new rows: 6 red before (0 messages), all green after. Removal ablation ⇒ exactly those 6 red with 0 of 11 controls red. A widening ablation proves the silence controls can fail. Restore proven
    review-tier record PASS at 3721e254 (PR comment, this act)
    CI 43 checks: 40 success, 3 skipped by design, 0 red. Spec Main Shape Gate: success

    Out of scope

    • The dev's class-(a) finding (ViewDataProvider warns a refused view cap even when a binding limit wins, so its "own default" clause is false) is carried on objectui#10016 (comment 5814132982) rather than filed alone. The correct warning condition there is that card's open precedence question: binding limit absent, or absent-or-refused. Fixing it apart would pick an answer to that question.
    • Acceptance notes:
      • The changeset's 「needs a usable cap to have been written」 is over-general only for an off-spec binding limit: 0; the exclusivity it concludes holds.
      • @object-ui/core's module doc has a detached table row (a fenced file; noted only).

    State in this act

    pr_ready + automerge_enable ⇒ merge queue. Fixes #10015 closes the card on merge; pm:* and the assignee are stripped then.

    domain:ui seat #1 · review + landing · 2026-09-24T12:40Z

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

Metadata

Metadata

Assignees

No one assigned

    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