Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Copilot stopped work on behalf of
hotlong due to an error
January 26, 2026 03:25
This was referenced Sep 20, 2026
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Sep 28, 2026
…nd yields to the view's cap (objectui#10016) (objectstack-ai#10389) Fixes objectstack-ai#10016 Clause-②: yes Ruling `5749674763` (batch objectstack-ai#198 item 4, letter **A**) applied. A `dataSource` binding's own `limit` gets the positivity check objectui#10014 gave the saved view's operand. A refused binding cap (`0`, a negative, a fraction, or a non-number) is **not authored**: the chain falls through to `savedViewLimit(view)`, and when that is absent too, to the consumer's own default. The rule is objectui#10009's precedent (a contract-refused value is not authored, so the other source wins), applied to the two operands of one resolver. The defect carried on the thread (`5814132982`) is in the same stroke: `ViewDataProvider` no longer reports a refused view cap when the binding's own usable `limit` is the cap actually used. ## What changed - **`composeElementDataSource`**: `config.limit ?? savedViewLimit(view)` becomes `bindingLimit(config) ?? savedViewLimit(view)`. `bindingLimit` is module-private and applies the same `isUsableRowLimit` predicate `savedViewLimit` uses. `null` stays absent, as `??` made it. - **`elementDataSourceRefusedLimitMessage`** (the channel objectui#10014 built; ⛔ no third channel): it takes two optional trailing parameters, `binding` and `operand` (`'view'`, the default, or `'binding'`). The binding operand has its own sentence, built by a module-private sibling, and that sentence names the binding (`dataSource binding on OBJECT ... declares limit: VALUE`). So the two refusals are told apart when both fire. For the view operand, the builder now applies the condition "the binding supplied no usable cap" itself, so both callers share one copy of it. **No new export**: `@object-ui/core`'s entry list is byte-identical, and a three-argument call answers exactly as before. - **`ViewDataProvider.resolveElementDataSource`** asks both operands and warns once per refused operand on the existing `console.warn` channel. - **`ElementDataSourceGate`** (renderer path): - The view cap that takes a refused binding cap's place is a view-sourced BASELINE. A usable component cap still wins over it, and the gate's `fromView` for the row cap is now "the binding has no usable `limit`". - The binding refusal is reported once per declaration, from an effect, for a block that reads a row cap. - The view refusal's binding condition moved into the builder. - The objectui#9928 test's KNOWN GAP header now points at this card's test file. None of objectui#10014's or objectui#10241's assertions were rewritten. ## Zone 2 readings (measured on `ea02938cd`, the branch base) 1. `element-data-source.ts` held `const limit = config.limit ?? savedViewLimit(view);` in `composeElementDataSource`, `savedViewLimit` reading `savedViewRawLimit` through `isUsableRowLimit`, and `elementDataSourceRefusedLimitMessage(view, viewName, object)`. **Confirmed.** 2. The callers that reported the view refusal: - `ViewDataProvider.resolveElementDataSource`, which called `elementDataSourceRefusedLimitMessage(view, config.view, config.object)` and warned **unconditionally**; - the gate's `describeRefusedViewRowLimit`, which returned `null` on `config.limit != null` or a usable component cap, under `if (limit)`. **Confirmed.** A third caller of the composer exists: `useElementDataSource`, whose consumers are the gate and `element:record_picker`. The record picker reports **neither** refusal today, so by the dispatch's rule it is out of scope. It is listed under Acceptance notes. 3. The builder was reused through a parameter, with no new export. **Confirmed feasible.** The additive parameters sit on an export that has not been released yet: `.changeset/9928-saved-view-limit-non-positive.md` is still pending. 4. The truth table follows. ## Truth table, measured before and after on both callers The instrument was a throwaway probe, not committed. It ran against the base blobs of the three source files, then against HEAD, and the restore was proven by blob hash. "none" = no `limit` reached the fetcher, or no cap was written to the node. Warnings are listed by the operand they name. **`ViewDataProvider`** (fetcher `limit`): | binding | view | before | after | |---|---|---|---| | absent | no cap | none, no warning | none, no warning | | absent | usable 25 | 25 | 25 | | absent | refused 0 | none, view warning | none, view warning | | usable 3 | no cap | 3 | 3 | | usable 3 | usable 25 | 3 | 3 | | usable 3 | refused 0 | 3, **view warning (false claim)** | 3, **no warning** | | refused 0 | no cap | **0** | none, binding warning | | refused 0 | usable 25 | **0** | **25**, binding warning | | refused 0 | refused 0 | **0**, view warning | none, binding + view warnings | **`ElementDataSourceGate`** (`pagination.pageSize` written; component cap C): | C | binding | view | before | after | |---|---|---|---|---| | absent | refused 0 | no cap | **0** | none, binding warning | | absent | refused 0 | usable 7 | **0** | **7**, binding warning | | absent | refused 0 | refused 0 | **0** | none, view + binding warnings | | usable 50 | refused 0 | no cap / usable 7 / refused 0 | **0** (over the usable 50) | **50**, binding warning | | refused 0 | refused 0 | no cap | 0, displaced-cap message claiming "(0) was used" | 0 (the component's own, for the renderer's diagnostic), binding warning | | refused 0 | refused 0 | usable 7 | 0, displaced-cap message | **7**, displaced-cap + binding warnings | | refused 0 | refused 0 | refused 0 | 0, displaced-cap message | 0, view + binding warnings | The probe covered 27 gate rows (C x binding x view, each three-valued). The **18 rows without a refused binding are identical before and after**, in value and in warnings. On the gate, the carried defect's row (usable 3 x refused 0) was already silent, and it still is. **Only the rows with a refused binding change resolution. Everywhere else, the one change is the carried-defect warning on `ViewDataProvider`.** This matches Zone 2 item 4. ## Tests (HEAD `22feffeb2`) - New `element-data-source.bindingLimitNotAuthored-10016.test.ts`. It covers the composer's full table over refused values `0`, `-10`, `25.5`, `"20"`, and the builder, which names each operand, stays silent under a usable binding, and gives two distinct messages when both operands are refused. It also covers every `ViewDataProvider` row, including the dispatch's five minimum pins. - A new block in `ElementDataSourceGate.test.tsx` has every refused-binding row, the component-cap rows, the flat `limit` key, a re-render guard, the carried-defect row, and CONTROL and SILENCE rows. - `pnpm exec vitest run packages/core/src/data-scope/ packages/react/src/element-data-source/`: **8 files, 260 tests passed**. - Union on the final commit, `22feffeb2`: `pnpm exec vitest run` over the two directories above, plus every other test file that names the binding, the gate, the hook, the composer or `resolveElementDataSource` (51 files across plugin-list, grid, kanban, timeline, detail, form, dashboard, map, calendar, gantt, charts, view, components, app-shell, console, types and scripts). The union also includes the two tests that read the touched sources as TEXT: `injected-component-input-6950.test.ts` and `registry-assertion-ratchet-8316.test.ts`. `useResolvedDataSource.schemaRendererContextRead-7209.test.ts` is already inside the gate directory. Result: **61 files, 1203 tests passed**. - `pnpm --filter @object-ui/core --filter @object-ui/react run type-check` passed. Both echoed `type-check: Done`, after `pnpm --filter '@object-ui/react^...' run build` rebuilt the closure from HEAD; the rebuilt core `.d.ts` carries the new `operand` parameter. `tsc -p tsconfig.test.json --listFiles` counts the new core test file (1) and the gate test file (1). ### Ablations (each through `ablation-replace.mjs` WRAP mode: the anchor had to hit x1, the landing was proven by anchor and replacement counts and a blob change, and the restore was proven by blob == HEAD and an empty `git diff HEAD`) `@object-ui/core` is aliased to `packages/core/src` by the root vitest config, and no `dist/` was on disk during these runs. So each mutation reached both suites without a build. - **(a) The binding positivity check was removed** (`bindingLimit` returns `config.limit`). Blob `cc2aa376` went to `302d2fde` and was restored to `cc2aa376`. **35 failed / 225 passed**: every refused-binding row on the composer, `ViewDataProvider` and the gate. **0 of the failures carry CONTROL or SILENCE.** The two gate rows with a usable component cap stayed green, because the gate's own `fromView` condition guards them, hence (c). - **(b) The unconditional view warning was restored** (the builder's `if (isUsableRowLimit(binding?.limit)) return null;` was deleted). Blob `cc2aa376` went to `b7c7c10a` and was restored. **5 failed**: the `ViewDataProvider` carried-defect row, the builder row, the gate's carried-defect row, and two objectui#10241 pins whose guard now lives in the builder. Those two are "the binding's own `limit` wins over the view" and "a binding `limit` displaced a refused component cap".⚠️ The ablation caught a naming error of mine. The gate's carried-defect row was labelled CONTROL, but under this suite's convention a CONTROL passes on both sides of the ablated guard, and this row does not. It was relabelled in a follow-up commit (`22feffeb2`). - **(c) The gate's `fromView` was reverted to presence** (`binding.config?.limit === undefined`). Blob `f2082ce4` went to `c9239886` and was restored. **Exactly 1 failed**: "binding 0 + usable view cap + usable COMPONENT cap ⇒ the component's cap wins over the view baseline". ## Gates (each exit code captured before any pipe) - `pnpm check:control-bytes`: `OK (scanned 8436 tracked text file(s); skipped 85 binary)`. - `pnpm check:new-line-citations`: `VERDICT new-cross-file-line-citations: 0 new citation(s)`. - `node scripts/check-changeset-presence.mjs`: `6 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)`. - `pnpm check:changeset-claims` passed; it flags nothing. `pnpm check:pending-changeset-literals` passed. `node scripts/check-changeset-no-major.mjs` passed. - `pnpm check:test-path-roots` and `pnpm check:element-data-source-declaration` (13 gate-consuming files) passed. - **eslint, narrowed to the diff, as a measurement:** - ① The population, read from eslint's own config: of the 7 changed files, eslint lints 6; the changeset `.md` gets "File ignored because no matching configuration was supplied". - ② `--format json` returned 6 results with **0 errors and 27 warnings**. The base blobs of the same files give 27 warnings too: 13 + 9 + 5 on the three pre-existing files that carry any, and 0 on the new file. - ③ Invariance: type-aware linting is off (`eslint.config.js` has no `projectService`, no `parserOptions.project` and no TypeChecked preset), and no rule under `eslint-rules/` reads the filesystem. So this diff cannot move a verdict on an untouched file. - **NOT MEASURED**, `check:doc-examples` and `check:readme-exports`. Reason: `PREREQUISITE NOT MET`, because 32 packages are unbuilt. The diff touches no `@example` block (0 hits in the diff) and adds no export. The full farm and repo-wide `pnpm lint` are CI's. ## Population, with a lit control (card item 3) The instrument was a throwaway walk at `22feffeb2`, over `git ls-files`: 549 strict-JSON files and 257 JSON fences in `.md`/`.mdx` (changesets and CHANGELOGs excluded), plus a literal scan of `dataSource: { object: ... }` object literals in source. | population | bindings | with a `limit` (CONTROL: usable) | refused | |---|--:|--:|--:| | authored metadata (JSON + fences) | 7 | **5 (5)** | **0** | | source literals | 124 | **11 (11)** | **0** | The control is **lit** in both rows, so this zero is a reading, not a dark instrument. No authored binding in the tree carries a refused `limit`. This agrees with the ruling's facet ②, which said the change is not urgent. **Seat amendment (2026-09-24T21:40Z).** Line 2 now reads `Clause-②: yes`. The exported `elementDataSourceRefusedLimitMessage` gains two optional parameters, which changes the `@object-ui/core` exports-map surface, so a contract review is owed before enqueue (claim `5822089217`, amended). The `@object-ui/react` changeset level moved from `patch` to `minor` at `226994a2f`. ## Acceptance notes - **The write-surface refusal (the ruling's C half):** 承接者:无. It was measured that the spec contract already refuses the value: on `@objectstack/spec` 17.4.0, `PageComponentSchema.safeParse` with `dataSource.limit: 0` gives `too_small` at `dataSource.limit`, and `ElementDataSourceSchema` refuses `0` / `-10` (`too_small`) and `25.5` / `"20"` (`invalid_type`). Whether every write surface runs that parse before persisting was not measured. - **The third caller of the composer, `element:record_picker`** (through `useElementDataSource`). It reports neither the view's refusal nor now the binding's, so by the dispatch's rule it is out of scope. Its resolution follows the ruling for free (`composed?.limit ?? props.limit ?? 50`). Its registry `description` still states the precedence as `dataSource.limit ?? limit ?? 50`, which predates both objectui#9928 and this card. 承接者:无. - **`content/docs/guide/data-source.md`** says "`sort` and `limit` override the view's". That is still true for a usable binding `limit`, and it says nothing about a refused one. It is outside this claim's file surface. 承接者:无. - `ReportView` forwards `dataSource.limit` raw as `$top`, but the report contract declares that key `dataSource?: never` (and the spec's `ReportSchema` refuses it as an unrecognized key), so it is not this binding. It was noted and not pursued. - The objectui#10009 changeset's "an explicit cap on the `dataSource` binding still wins over both" now holds for a **usable** binding cap. This PR's changeset states that explicitly. Changeset: `.changeset/10016-binding-limit-not-authored.md`. `@object-ui/core` is `minor`, following objectui#10014's level for this class, and `@object-ui/react` is `minor` since head `226994a2f` (seat respin; it was `patch`, following objectui#10009 and objectui#10241), because the gate's precedence changes on the refused-binding rows. The changeset states the behaviour change plainly. The implementing session is `https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC` (`domain:ui` seat 1 dispatch, `mode:subagent`). --- _Generated by [Claude Code](https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This branch had an error being deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.