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): useSettledSchema republishes an equal payload as a fresh object, so an adapter swap costs two record queries — the half of commandment #10 it breaches #10106

Closed

Description

@os-tesla

Dedupe words: useSettledSchema def identity · getObjectSchema republish equal payload · duplicate record query on adapter swap · objectui#6862 settled def · provider object two queries

Surfaced by the objectui#10036 dev while removing two useMemo/useCallback identity dependencies from ObjectGantt; the site is outside that card's file surface and it correctly did not reach into it. ⚠️ Wholly 转述 — ⛔ NOT re-run by the domain:ui#2 execution seat. ⭐ It is filed with that label rather than dressed as a seat reading (说明 GG). ⛔ No priority:* / type: the triage seat's sole production. domain:ui inherited from the in-flight parent objectui#10036.

The claim, and the contract it is measured against

Swapping the context adapter on provider object costs TWO record queries, not one:

  • useSettledSchema re-reads getObjectSchema through the new adapter and settles a FRESH def object for a byte-identical answer;
  • objectSchema is a genuine member of the fetch effect's value list, so the query runs again when that fresh object lands.

AGENTS.md §5 commandment #10 states the contract verbatim:

a provider that refetches may not republish an equal payload as a new object, and a consumer that needs stability keys on the data, not on a cache

⇒ the first half of that sentence is what useSettledSchema is reported to breach. Site: packages/react/src/hooks/useSettledSchema.ts.

⭐ Why this is filed now rather than noticed later

The objectui#10036 pin measured it and deliberately did NOT pin the exact count for that case — so ⭐ the pin will ⛔ not go red on the day this is repaired, and the observation would otherwise have lived only in a hand-back. ⚠️ That also means nothing in the tree fails today because of it: this card is the only carrier.

⚠️ It is unchanged by PR #10100 — before and after alike. ⛔ That PR neither caused nor fixed it.

Acceptance

  • The two-query behaviour on an object-provider adapter swap is re-measured first-hand, with a control in the same command (a swap that should cost ONE query, so a 「two」 is a discrimination and ⛔ not a counting artefact).
  • If it reproduces: the repair is at the producer — republish the equal payload as the same object — ⛔ not by making the consumer key on a cache, which is the half of commandment [WIP] Enhance every detail of the designer #10 that names the wrong cure.
  • A pin that pins the COUNT, so the next reader is told by a red test rather than by a card.
  • ⚠️ If it does not reproduce, say so and close not planned with the reading — ⛔ a 转述 card that fails its own re-take is a closed card, not a smaller one.

Generated by Claude Code

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 20, 2026
  2. self-assigned this
    on Sep 20, 2026
  3. os-tesla commented on Sep 20, 2026

    @os-tesla
    CollaboratorAuthor

    Claim: PM loop — 2026-09-20 shift, batch slot 2/2 (ceiling 2)
    Session: session_018HrVaotisyhgmot9o2MLRq
    Branch: claude/issue-10106-settled-schema-equal-payload
    Worktree: objectui-issue-10106
    Domain: domain:ui (triage's; ⛔ not re-routed here)
    Seat: domain:ui#2
    File surface: packages/react/src/hooks/useSettledSchema.ts and its tests. ⛔ Nothing else.
    Container & model: M, mode:subagent, model: opus. ⛔ No tier output is quoted: scripts/pm/dispatch-gates.mjs REFUSES for this repo.
    Clause-②: no — a provider's republish identity. No exported symbol and no key on a published payload moves. ⚠️ The dev re-declares from its actual diff and STOPS if the repair turns out to change an exported shape.

    ⚠️ This card is wholly 转述. This seat filed it from objectui#10036's hand-back and ⛔ re-ran nothing. ⇒ its FIRST deliverable is the re-take, and ⭐ 「it does not reproduce」 is a legitimate, complete outcome — the card says so in its own acceptance: close not planned with the reading. ⛔ A 转述 card that fails its own re-take is a closed card, ⛔ not a smaller one to force through.

    Hot-file serial queue: the only other card in flight is #10039 (packages/app-shell/src/console/ai/** + views/studio-design/**), disjoint from packages/react.

    Claimed at 2026-09-20T14:06Z.


    Generated by Claude Code

  4. os-tesla commented on Sep 20, 2026

    @os-tesla
    CollaboratorAuthor

    os-dev-report
    {
    "issue": 10106,
    "status": "done",
    "branch": "claude/issue-10106-settled-schema-equal-payload",
    "pr": "#10149",
    "session": "session_018HrVaotisyhgmot9o2MLRq",
    "premise_still_valid": true,
    "summary": "The second-hand claim REPRODUCES, re-measured first-hand before any edit. On the real consumer (ObjectGantt, instrumented adapter, provider object), swapping the adapter when the new adapter answers byte-identically as a FRESH object cost 2 find calls carrying the SAME expand set [['owner'], ['owner']]; the control in the same command — both adapters returning the SAME def object — cost 1; a second control with a genuinely different def cost 2 with the second call widening to ['owner','account']. So 'two' is a discrimination, not a counting artefact. Repaired at the PRODUCER per AGENTS.md section 5 commandment #10: useSettledSchema now keeps the object consumers already hold when a refetch's answer is value-equal, via a module-private isEqualPayload and a settle() closure; nothing moved into a consumer's dependency list and no cache was added. After the repair the subject reads 1 and both controls are unchanged. A COUNT pin with its controls ships in packages/react/src/hooks/tests/useSettledSchema.equalPayload-10106.test.tsx. No exported symbol and no key on a published payload moves; Clause-② re-declared as 'no' from the actual diff.",
    "tests": "RED-before: the pin run against the hook proved byte-identical to origin/main IN THE SAME COMMAND (git hash-object == HEAD blob == bb550f98d) -> 2 failed | 3 passed; the two subject assertions failed and all three controls passed. GREEN-after: same pin with the repair on disk (blob f287de8b6, printed in the same command) -> 5 passed. ABLATION from the committed state (settle bail-out replaced by the pre-repair unconditional republish): mutation proved by content AND hash, never by exit code — injected-marker count 1, removed-guard count 0, blob f287de8b6 -> ae320ff3b; pin went 2 failed | 3 passed (same two, controls green). Restore proved the same way: blob back to f287de8b6 and git diff HEAD empty; the script carried an absolute-path trap on EXIT/INT/TERM. SUITES at fe23217 with a clean tree printed in the same command: packages/react in full plus every useSettledSchema consumer pin (gantt fetch gate, calendar, kanban x2, gallery, timeline, tree, view) and the two doc-example reader gate suites -> 115 files / 1353 tests passed. TYPE-CHECK @object-ui/react: tsc --noEmit and tsc -p tsconfig.test.json both clean; the test project SELECTS the new pin (--listFiles finds it). GATES re-run LAST at fe23217 with nothing unstaged, all exit 0: check:control-bytes, check:new-line-citations, check:doc-examples, check:doc-example-ids, check:doc-example-readers, check:doc-snippets, check:doc-types, check:readme-exports, check:test-path-roots, check:vi-mock-specifiers, check:unreferenced-sources, check:pending-changeset-literals, check-changeset-presence, check-changeset-fixed, check-changeset-no-major, eslint on both changed files (0 errors; 15 any warnings, the sibling test file carries 16 and the repo sets no --max-warnings, deliberately). NOT-A-VERDICT, recorded as such: check:doc-examples and check:readme-exports first answered PRECONDITION NOT MET / population COLLAPSED with no dist on disk (readme-exports reported typesResolved 0 against a floor of 2). Both were then re-run properly after turbo run build over the filter the gate itself prints; the greens above are from those runs, with a healthy population (readme-exports: 3384 export symbols from 36 of 40 packages, 58 keys compared). CHANGESET decided by running the gate, not from memory: check-changeset-presence exited 1 naming @object-ui/react; a patch changeset was added and it now exits 0. Every heavy run went through /home/user/objectstack/scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-10106; every verdict read from its printed VERDICT line, and every exit code captured after a redirect, never across a pipe. NOT MEASURED locally, declared to CI: the repo-wide pnpm lint / pnpm test / turbo type-check farm runs, which CI owns.",
    "mcp_calls": "0 — no MCP GitHub tool was called, read or write. All GitHub access was curl against the REST proxy.",
    "api_writes": "2 — POST /repos/objectstack-ai/objectui/pulls (draft PR #10149, the only PR-body write; no PATCH followed); POST /repos//issues/10106/comments (this report). NO label write: the dispatch forbade applying any label, and it is also the standing rule here — this repo's skip-changeset label is read by no workflow and exempts nothing, and it would be inapplicable anyway because a real changeset was added. The PR's labels (package: react, tests) were set by the repo's labeler, not by this seat, and were left alone. No ready flip, no auto-merge, no assignee write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: useSettledSchema.test.ts's case 're-fetches when dataSource identity changes even if the key does not' carries a comment claiming 'Ready flips false the instant the source changes, same mechanism as a key change'. The code does not do that — ready is resolution.key === key and an adapter swap leaves the key alone, so ready stays true, which is precisely the mechanism this card rests on. The case's assertions are correct and still pass; only the sentence is wrong. Not filed: a false comment is not one of the three fileable classes (not a reproducible defect, not a breach of a declared contract, not an authoring trap), and the in-place-fix exemption fails its same-defect-class condition. Who will meet it: the next reader of this hook's tests — most likely whoever repairs or reviews an adapter-swap question here, since the sentence argues against this card's own finding. The correct statement is now written and pinned in the new file in the same directory.",
    "noted, not filed: packages/react/README.md documents useSettledSchema with the same recipe and is NOT falsified by this change, so commandment #2 asks for nothing; the new producer guarantee travels to consumers through the changeset body instead. Left untouched because the claimed file surface is the hook and its tests, nothing else. Successor: none required — no sentence in it became false.",
    "noted, not filed: the COUNT is pinned at the hook's boundary through the recipe its own @example publishes. A count pin on a real consumer (ObjectGantt) would be strictly closer to the card's wording, and the throwaway probe that produced the gantt reading showed it is cheap, but packages/plugin-gantt is outside this card's file surface. Successor: whoever lands PR #10100 (objectui#10036) is already in that file."
    ]
    }


    Generated by Claude Code

  5. os-tesla commented on Sep 20, 2026

    @os-tesla
    CollaboratorAuthor

    IMPLEMENTATION ACCEPTED · LANDING HELD. PR #10149 (head fe23217e9, 3 files, +309 −3), reviewed 2026-09-20T14:36Z. NOT governed (0 of 3 paths).

    ⭐ The 转述 claim REPRODUCES — and the control is what makes that a finding

    This card was filed wholly second-hand and its first deliverable was the re-take, with 「it does not reproduce」 named as a complete outcome. It reproduces, and the measurement discriminates:

    leg adapters find calls
    subject new adapter answers byte-identically as a FRESH object 2, carrying the SAME expand set [['owner'], ['owner']]
    control A, same command both adapters return the SAME def object 1
    control B, same command a genuinely different def 2, and the second call widens to ['owner','account']

    ⇒ 「two」 is a discrimination, ⛔ not a counting artefact: one control shows the count can be 1, the other shows a legitimate 2 looks different at the wire. ⭐ And it was taken on the real consumer (ObjectGantt, instrumented adapter, provider object), ⛔ not on a synthetic harness.

    Repaired at the PRODUCER, which is the half of commandment #10 that names the right cure: useSettledSchema now keeps the object consumers already hold when a refetch's answer is value-equal. ⛔ Nothing moved into a consumer's dependency list and ⛔ no cache was added — the other half of the same sentence, avoided.

    The ablation is the discriminating one: the settle bail-out replaced by the pre-repair unconditional republish ⇒ the same two assertions fail and the three controls stay green. Mutation proved by content AND hash, restore proved by blob-equals-HEAD plus empty git diff HEAD, ⛔ never by an exit code.

    ⭐ A COUNT pin ships with it — which is what the card asked for, so the next reader is told by a red test rather than by a card.

    ⛔ Why this is NOT being landed right now

    Two independent reasons, and either alone is sufficient:

    1. 11 of 36 checks are still PENDING at this reading. ⚠️ This seat armed auto-merge over exactly that state earlier today and an advisory check came back red and merged — see objectui#10148. ⭐ 「放行认门禁 job 的结论,⛔ 不认聚合读数」: eleven unreported checks are not conclusions.
    2. Bundle Analysis is RED on main (objectui#10148). Every PR built on that base inherits the red, for bytes that are ⛔ not its own — the gate says so in its own words. ⇒ landing into it would add PRs that merged with a red advisory check, which is how a red gate stops being noticed.

    ⇒ the PR stays draft, alongside #10147 (objectui#10039), until objectui#10148's decision lands. ⛔ Neither is red on its own merits, and ⛔ neither is being reworked.

    Three notes carried, ⛔ none filed, and the first one is sharp

    ⭐ useSettledSchema.test.ts's case 「re-fetches when dataSource identity changes even if the key does not」 carries a comment claiming 「Ready flips false the instant the source changes, same mechanism as a key change」. The code does not do that — ready is resolution.key === key, and an adapter swap leaves the key alone, so ready stays true. ⇒ ⚠️ that sentence argues against this card's own finding, and it is false. ⛔ Correctly not filed (a false comment is none of the three classes, and the in-place carve-out fails its same-class condition) — and the correct statement is now written and pinned in the new file in the same directory, which is the better answer than a card nobody takes.

    packages/react/README.md documents the same recipe and is ⛔ NOT falsified, so commandment #2 asks for nothing; the new producer guarantee travels through the changeset body instead.

    The COUNT is pinned at the hook's boundary via the recipe its own @example publishes; a pin on the real consumer would be strictly closer to the card's wording, and the throwaway probe showed it is cheap — ⛔ but packages/plugin-gantt is outside this card's surface, and the successor is named (whoever is next in that file).

    Two gates answered PRECONDITION NOT MET / population COLLAPSED on an unbuilt tree (readme-exports reported typesResolved 0 against a floor of 2) and are recorded as not-a-verdict, then re-run properly over a healthy population (3,384 export symbols from 36 of 40 packages). ⭐ The greens quoted are from the second runs.


    Generated by Claude Code

  6. removed their assignment
    on Sep 20, 2026
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

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions