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.

useSettledSchema was extracted and published with ONE adopter — the convergence #6482 asked for is 1 of 4, and the gantt's ungated double fetch is still live #7225

Closed

Description

@os-warren

Filed by the domain:ui execution seat (session session_012wwHa4aaFybxXrfmfHioDM). Surfaced by the objectui#7210 lane, which measured the gantt half; the convergence half is this seat's measurement. Filed unassigned.

Why this is a new card and not a reopen of #6482

The #7210 lane recommended reopening objectui#6482 rather than filing again, to avoid a fourth card on the same regime. I checked before acting on that, and the situation turned out different from what the recommendation assumed, so I am filing instead. Reasoning, so it can be overruled:

What is actually outstanding is narrower and different from what #6482 says, so it deserves its own scope.

Measured today, on origin/main

useSettledSchema exists and is published — packages/react/src/hooks/useSettledSchema.ts, exported through packages/react/src/hooks/index.ts.

It has exactly ONE non-test adopter. Counted as a file count (grep -rl … | wc -l, files not lines), excluding packages/react/ itself and all test paths:

non-test adopter files 1 — packages/plugin-tree/src/ObjectTree.tsx

Control that the probe discriminates: the same pattern finds packages/react/src/hooks/useSettledSchema.ts, its index.ts export, and its own test file. So the 1 is a reading, not a broken query.

⚠️ I got this wrong on the first attempt: my initial probe searched for useSettledObjectSchema, which is not the symbol's name, and returned zero adopters — a silent zero that read exactly like the alarming answer I was half expecting. It was caught only by running a control that also should have matched and did not. Same class as the rest of this repo's silent-zero catalogue.

So the convergence is 1 of 4. ObjectKanban, plugin-view/ObjectView and ObjectCalendar do not import the hook and still carry their own settled-schema shapes. The one migration that happened, ObjectTree, was the one that had an actual correctness defect (objectui#6481, the unkeyed latch) — so the hook was extracted to fix a bug and the deduplication it was named for did not follow.

And #6482's ask 2 is untouched for the gantt. packages/plugin-gantt/src/ObjectGantt.tsx still holds a local useState for the schema and still lists objectSchema in its fetch-effect dependency list, which is the ungated "two queries, the first unexpanded" regime #6482 tabulated. The #7210 lane re-confirmed it by measurement: two unbounded queries per gantt load, before and after the object-schema read settles, the first carrying no $expand and therefore a wasted round trip.

Why a published export with one adopter is its own problem

This is ADR-0049 territory from an unusual direction. @object-ui/react is a published package, so the hook is a permanent public-surface commitment — which #6482's own body flagged in advance:

A helper would have to live in @object-ui/react … which makes it a new export on a published package — a permanent public-surface commitment. All four plugins already depend on @object-ui/react, so it is feasible; the question is whether it is wanted.

The question was answered "yes", the surface was widened, and three of the four intended callers were left as they were. That is the worst of both: the duplication remains and the API is now owed compatibility forever.

What this card asks for

  1. Migrate the remaining three (ObjectKanban, plugin-view/ObjectView, ObjectCalendar), or decide deliberately that they should not migrate and record why — finding(ui): the schema-settled $expand gate is now four hand copies, and four more views resolve the schema without gating on it #6482 already documented a real obstacle, that ObjectCalendar keys on dataConfig.object ?? schema.objectName and gates one provider branch rather than the whole effect. If that obstacle is what stopped the migration, saying so closes the question honestly.
  2. Separately, gate the gantt. ⚠️ Per finding(ui): the schema-settled $expand gate is now four hand copies, and four more views resolve the schema without gating on it #6482's own finding, whether gating is worth it is a per-component measurement, not an inference — it measured three latency profiles and found the cost differs by component (a discarded round trip in one case, a visible three-step paint in another). Do not gate the gantt because the table says so; measure it.

⛔ Note that gating the gantt is not the same as capping it. objectui#7210 half 2 — whether a non-grid view may fetch unbounded — is an open maintainer decision, and nothing here should pre-empt it. This card is about the duplicate query, not the unbounded one.

Related: objectui#6482 (closed; the origin) · PR #6690 (the extraction) · objectui#6481 (the defect that drove it) · objectui#7210 (where the gantt half was re-measured) · objectui#7222 (the gantt renderer's prop-forwarding asymmetry, same component).


PM state (added by the domain:ui execution seat, 2026-09-02)

Implementation is complete and delivered on PR #7391 — ruling B is implemented: ObjectKanban, plugin-view/ObjectView and ObjectCalendar are migrated onto useSettledSchema, and the gantt's duplicate ungated query is gated (one expanded query per load instead of two). The card is parked on a governance decision, not on outstanding work.

Blocked-by: #7399
Unlock-action: re-check PR #7391

#7399 asks the maintainer to choose how ruled user-facing copy lands against the framework per-chunk eager-closure ceiling. ⚠️ This card carries no user-facing copy of its own — it is blocked only because it shares PR #7391 with #7210, which does. If the decision on #7399 goes a way that keeps #7210 parked for long, splitting this half onto its own PR is the obvious unblock and should be reconsidered then.

Activity

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

    @os-warren
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 7225,
      "status": "done",
      "branch": "claude/issue-7225-settled-schema-convergence",
      "pr": null,
      "premise_still_valid": false,
      "summary": "OUTCOME: option 2 of the card's primary — no migration in this card, with the reason measured — and therefore NO PR, per the dispatch. Every FACTUAL premise of the card holds and I re-derived each one; what does not hold is its ACTIONABLE premise. A standing maintainer ruling recorded on #6482 forbids exactly this card's shape: 2026-08-27, maintainer verbatim (kept unglossed) 「同意,并继续」, accepting the triage facet block whose recommendation reads 「推荐:A(抽 resolution 半边、gate 留组件;迁移随后续卡顺带,⛔ 不做一次性四包重构;未 gate 视图逐组件实测后另立卡)」 — migration of the existing copies RIDES SUBSEQUENT CARDS TOUCHING THOSE COMPONENTS, no one-shot multi-package refactor. The dispatching PM seat's own ACCEPT on #6482 (2026-08-28) restated it in English and named this card's exact shape in advance: 'Migration of the existing three copies rides subsequent cards touching those components, NOT a follow-up refactor card.' #7225 is a follow-up refactor card asking for that migration; neither the card nor my dispatch order mentions the ruling, so I am surfacing it rather than quietly siding with either. Two supporting facts. (a) The strategy the ruling chose has had FOUR DAYS, not five weeks: PR #6690 closed #6482 on 2026-08-28 and today is 2026-09-01 — and it has already converged once exactly as ruled, ObjectTree migrating via its own card (#6481). (b) The migration is not being avoided for difficulty: I MEASURED it by actually performing it on all three components, and it is mechanically clean — 3 files, 17 insertions, 111 deletions, three package type-checks at exit 0 with 0 TS errors, and the three components' full suites at 497/497, byte-identical to their pre-migration baseline of 497/497. So the per-component verdict is the same for all three, and it is the FAVOURABLE one: ObjectKanban, plugin-view/ObjectView and ObjectCalendar ALL fit useSettledSchema, with no mismatch to report. Kanban and ObjectView are literally the hook's own shape (one state of key plus def, render-derived readiness, settle-on-every-exit) because the hook was extracted FROM them; each becomes a one-line call. ObjectCalendar — the component #6482 named as the real obstacle — also fits, via the recipe the hook's own doc comment already prescribes for it by name: pass the data source as undefined for the render that must not read metadata (hasInlineData ? undefined : dataSource), so 'inline value data set' is expressed as 'there is no source to read from' rather than as a second enable flag. The #6482 obstacle was about the GATE half, and gate placement is exactly what the hook leaves local, so it never blocked the resolution half. The one observable delta of the measured migration is a console CHANNEL change in ObjectKanban only: its hand copy logs console.warn('Failed to fetch object def'), the hook logs console.error with a bracketed prefix. Its own gate test silences console.warn without asserting on it and there is no repo-wide fail-on-console-error policy, so it stays green — but a rider card should move that spy. RECOMMENDATION to the maintainer, as an open question below: the ruling's stated worry was the cost of refactoring already-landed fixes; that cost is now measured at zero behaviour delta, so the maintainer may prefer to amend the ruling and take one convergence PR. I did not presume that amendment. Secondary ask discharged by measurement only, nothing changed in plugin-gantt.",
      "tests": "All measurements taken in a dedicated worktree at base f626808d4 (recorded as BASE before the first edit). Control on staleness: origin/main advanced 2 commits to 231d1b93c during the run and `git diff --stat f626808d4 origin/main` over the six measured files (ObjectGantt.tsx, ObjectKanban.tsx, ObjectView.tsx, ObjectCalendar.tsx, useSettledSchema.ts, ObjectTree.tsx) is EMPTY — none of them moved, so the readings still describe current main. Tree left pristine: `git status --short`, `git diff HEAD`, `git diff --cached` all empty; 0 commits on the branch. Vitest run canonically from the repo root (the package-cwd form is refused by this repo's guard, objectui#3378); file counts asserted as the control on every run. (1) ADOPTER COUNT: `git grep -l useSettledSchema` returns 11 files — 3 changesets, README, vitest.config.mts, 3 files inside packages/react (the hook, its barrel, its test), 2 plugin-tree TEST files whose matches I checked line by line and are PROSE-ONLY comment mentions, not imports, and exactly ONE non-test adopter, packages/plugin-tree/src/ObjectTree.tsx (real import on line 24). So: 1, and the naive file-count would have said 3 in plugin-tree. Positive control `useDataRefresh` gives 5 files (fires); negative control `useSettledObjectSchema` (the wrong symbol that produced the card's own false zero) gives 0, reproduced as a genuine zero next to a control that fires. (2) PUBLICATION: measured through the BUILT entry, not an export keyword — loaded packages/react/dist/index.d.ts through the TypeScript checker's getExportsOfModule (295 exports); useSettledSchema true AND the SettledSchema type true; positive controls useDataRefresh and useNavigationOverlay true; negative control zzzNotARealExport false. Package is public (@object-ui/react 17.6.0, exports map to ./dist/index.d.ts), src/index.ts re-exports ./hooks/index.js. The compatibility-commitment framing in the card is CORRECT. A first attempt at this probe exited MODULE_NOT_FOUND because I ran it from outside the repo — reported as my path error, not booked as a red measurement. (3) PRE-MIGRATION BASELINE: the three gate suites gave 3 files / 22 tests / 22 passed. Full suites for the three packages gave 69 files / 497 tests / 497 passed. (4) MIGRATION FIT, measured by performing it under a trap on EXIT INT TERM with absolute paths: mutation proven on disk per file by anchored marker counts AND blob hash against the HEAD blob (kanban 1609c921 to 016986556, view eea2e7fa to e2a0ff0e, calendar da019683 to 41d27a1e), removed form `schemaResolution` at 0 occurrences and exactly 1 call site in each. PREDICTION MISS, reported rather than adjusted: I predicted 2 occurrences of the replacement token per file and kanban showed 3 — my own inserted comment names the symbol; the guard stopped the run, the trap restored, and I widened the assertion to call-site count before re-running. Direction prediction held: type-check exit 0 with 0 'error TS' lines for all three packages (script name echoed in each log as proof it ran, not a zero-match no-op), and 69 files / 497 tests / 497 passed — IDENTICAL to baseline. Diffstat 3 files, 17 insertions, 111 deletions. Restore proven by STATE: git diff HEAD, git diff --cached, git status --short all empty and all three blob hashes byte-identical to HEAD again. (5) DISCRIMINATION CONTROL, so that '497 unchanged' is not a vacuous green: ablated the three gate lines on the UNMIGRATED tree (mutation proven on disk by inserted-marker count 1, removed-form remaining 0, and blob hash vs HEAD for each file). Predicted at least 6 of 22 red; OBSERVED 14 failed / 8 passed of 22, all three suites failing — direction as predicted, magnitude higher, reported as observed. So the suite that stayed green through the migration genuinely discriminates on this behaviour. Restored, state proven empty. (6) GANTT LATENCY PROFILE — see the dedicated field below; measured with a temporary harness that was deleted afterwards (never committed). Scope declaration: this measures the QUERY AND PAINT STATE MACHINE (adapter call order, expand sets, commit sequence) in the jsdom project with GanttView mocked exactly as the existing plugin-gantt tests mock it; it is NOT a wall-clock latency reading and no layout-dependent fact is claimed, so the happy-dom/jsdom clientWidth-0 trap does not apply here. One honest limit: MutationObserver batches, so a frame shorter than one microtask checkpoint would not be recorded — which is why I report profile C as 'no raw-id frame OBSERVED' rather than 'clean'. NARROWING DECLARED: I ran the three affected packages' full suites plus the three gate suites, not the repo-wide farm or repo-wide lint, because this run changes NO committed code — the tree is pristine and there is nothing for CI to gate. No changeset is owed, and I am saying so rather than inventing one, exactly as the dispatch asked. Heavy runs went through the shared verify lock (scripts/pm/os-verify-lock.sh) with a stable slot; one acquisition waited 4m40s behind the objectui-7215 lane and I spent the interval on lock-free work rather than ending a turn.",
      "gantt_measured_latency_profile": "MEASURED, three profiles, ObjectGantt via the registered object-gantt renderer with an instrumented adapter. INVARIANT ACROSS ALL THREE: 2 `find` calls and 1 `getObjectSchema` call per load, and the expand sets are [null, ['owner']] — the FIRST query carries no $expand and the second carries it. That is #6482's ungated 'two queries, the first unexpanded' regime, confirmed by direct measurement rather than inferred from the dependency list. PROFILE A, schema 5ms / find 80ms (schema read faster): adapter order find:issued(UNEXPANDED), schema:issued, schema:settled, find:issued(EXPANDED), find:settled(UNEXPANDED), find:settled(EXPANDED). Paint sequence PLACEHOLDER, OTHER, EXPANDED-ROWS. The unexpanded round trip IS discarded — its rows never paint — so this is #6482's 'bought and thrown away' case, BUT it is not a clean discard: there is a third, intermediate frame in which the loading placeholder is already gone and no rows have arrived, i.e. the user sees an EMPTY chart. PROFILE B, schema 80ms / find 5ms (schema read slower): paint sequence PLACEHOLDER, RAW-ID-ROWS, PLACEHOLDER, EXPANDED-ROWS — the full THREE-STEP PAINT, the same worst case #6482 measured on ObjectCalendar: raw foreign-key ids painted, back to the loading placeholder, then the expanded rows. PROFILE C, 30ms / 30ms: PLACEHOLDER, EXPANDED-ROWS, no raw-id frame observed (see the batching limit above; the adapter order shows the unexpanded find DID settle before the schema, so the raw frame likely existed below observation granularity rather than not at all). VERDICT for the next card: the gantt's profile is NOT the mild kanban-style discard — it reaches ObjectCalendar's visible three-step paint whenever the metadata read is the slower of the two, which is the common case on a cold MetadataCache. On #6482's own per-component standard that is the profile where gating pays. I did NOT gate it, as instructed. Nothing here touches whether a non-grid view may fetch unbounded — every query measured above was unbounded before and after, and #7210 half 2 stays entirely open.",
      "zone2_assumptions": "(1) 'exactly one non-test adopter' — CONFIRMED by my own probe with a firing positive control and the card's own false-zero symbol as negative control; refined, since a naive file-count would report 3 in plugin-tree (2 are prose-only comment mentions). (2) 'the three still carry hand-rolled equivalents' — CONFIRMED by reading their code, not by absence-of-import; STRENGTHENED: they are not merely equivalent, they are the hook's exact shape, because the hook was extracted from them. None of the three has been rewritten into a shape where the question stops applying. (3) 'genuinely published' — CONFIRMED and upgraded from a source-level export to a built-entry measurement with controls; the card's severity framing stands. (4) 'ObjectTree is a good model for the others' — FALSIFIED, and in the direction that matters. ObjectTree was the ONLY one of the four whose pre-migration shape was defective (two independent useStates, the unkeyed latch of #6481), so its adoption was a BEHAVIOUR-CHANGING bug fix. The other three already hold the correct shape, so their migration is a pure deduplication with a measured ZERO behaviour delta (497/497 identical). Reading across from ObjectTree would have predicted risk that is not there. (5) EXTRA, falsifying the card's and my order's own framing: '#6482's body is a five-week-old snapshot' is FALSE — #6482 was created 2026-08-26T03:26:51Z, which is SIX DAYS ago, and its four-row table is stale in exactly ONE row (plugin-tree, which now uses the hook and is keyed); the kanban, view and calendar rows still describe the code precisely, as I verified by reading all three. That matters because the age-and-drift claim is the card's stated ground for filing new rather than reopening. (6) EXTRA, the load-bearing one: the assumption that the remaining migration is available work is falsified by the standing #6482 ruling described in the summary.",
      "mcp_calls": "6 — issue_read #7225 (get), issue_read #6482 (get), issue_read #6482 (get_comments), one search_issues that FAILED with 'API rate limit already exceeded' (counted), this report comment, and one read-back of it. The direct REST channel was probed and is unavailable to this session (HTTP 403, 'GitHub access is not enabled for this session'), and `gh` is absent — which is why the two findings below are handed to the PM rather than filed: with search rate-limited and REST 403 I could not run the required duplicate check, and filing blind is the worse failure.",
      "open_questions": [
        {
          "question": "The #6482 maintainer ruling (2026-08-27) directs that migration of the settled-schema hand copies rides subsequent cards touching those components and explicitly bars a one-shot multi-package refactor; #7225 asks for exactly that refactor. Should the ruling stand, or be amended now that the refactor's cost is measured?",
          "options": [
            "A. Ruling stands. Close #7225 as answered-by-ruling and attach the measured recipe to it, so each of ObjectKanban / ObjectView / ObjectCalendar converges when its next card touches it. Cost: three copies keep drifting-by-default for an unbounded interval, mitigated by the fact that each is pinned by a suite measured to be strongly discriminating (14 of 22 assertions red on gate removal).",
            "B. Amend the ruling to permit ONE convergence PR covering the three. Cost: one PR touching three packages on a fast-moving main. The ruling's stated worry was the risk of refactoring already-landed fixes (#6271, #6419, #6453); that risk is now MEASURED at zero behaviour delta — 497/497 identical, three type-checks clean, 17 insertions against 111 deletions — which is materially lower than the ruling could have assumed when it was made.",
            "C. Split the difference: amend only far enough to allow ObjectKanban and ObjectView (the two that are the hook's literal shape) in one PR, and leave ObjectCalendar to its next component card because its call passes the data source conditionally and is the one worth a component-holder's eyes."
          ],
          "recommendation": "B, but it is the maintainer's call and I did not presume it. Real business need: no live defect in any of the three, so the pull is drift prevention — real but not urgent, evidenced by exactly one historical drift (#6481) across the family. Long-term soundness: convergence is the agreed destination; only the route is in question, and the route's stated cost is the thing that has now changed. Making AI-written code hard to get wrong: this is the axis that decides it — three remaining hand copies of subtle keyed-readiness logic are precisely where an AI editing one of these components reintroduces #6481's defect, and the hook makes 'ready for the wrong object' unrepresentable rather than merely conventional. Startup scope discipline: neutral to positive, since the export is already published (sunk) and the change REMOVES 94 net lines while widening nothing. The one thing I would not do is B-without-the-ruling: if the maintainer prefers A, that is a coherent answer and the measured recipe makes each rider card nearly free."
        }
      ],
      "out_of_scope_findings": [
        "NOT FILED (see mcp_calls: search rate-limited, REST 403, so the mandatory duplicate check could not run — handing to PM to file). packages/plugin-gantt/src/ObjectGantt.tsx: reload()'s `finally` flips loading/refreshing from a SUPERSEDED response. setData and setError are both guarded by isCurrent(), the finally block is not, so a stale reload's completion clears the loading placeholder while the fresh query is still in flight. MEASURED as profile A's intermediate empty-chart frame, and REVERSE-VERIFIED: wrapping that finally in isCurrent() removes exactly that frame (profile A becomes PLACEHOLDER then EXPANDED-ROWS) and leaves profiles B and C unchanged — mutation proven on disk by marker count and blob hash, restored under a trap with state proven empty. Distinct from gating: it also affects the toolbar refresh and write-readback paths, where two reloads legitimately overlap. Not fixed here — the dispatch says measure the gantt, do not fix it.",
        "NOT FILED (same channel state). packages/plugin-gantt/src/ObjectGantt.tsx: the schema-resolution effect has exits that never settle — `if (!effectiveDataSource) return;`, `if (!resource) return;` and its catch all leave objectSchema at null with no settled signal, and the component carries no settled signal at all. Harmless while the fetch is ungated, but it is the exact trap whoever gates it next will hit: all four sibling components had to add an explicit settle-on-every-exit for precisely this reason, because a gated query waits on readiness and an exit that returns without settling holds it open forever. This belongs in the gating card's body, not in a fix here."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  4. os-warren commented on Sep 1, 2026

    @os-warren
    CollaboratorAuthor

    ⛔ I should not have filed this card — a standing maintainer ruling already forbids its shape. No PR, correctly.

    domain:ui seat, session session_012wwHa4aaFybxXrfmfHioDM. Re-grading to needs-user-decision, clearing pm:dispatched and the assignee.

    The error is mine, and it is worse than a stale premise

    The lane read #6482's comments, which I did not. There is a maintainer ruling there from 2026-08-27 — 「同意,并继续」 — accepting a recommendation that reads, verbatim:

    推荐:A(抽 resolution 半边、gate 留组件;迁移随后续卡顺带,⛔ 不做一次性四包重构;未 gate 视图逐组件实测后另立卡)

    Migration of the existing copies rides subsequent cards touching those components; a one-shot multi-package refactor is explicitly barred.

    And it gets sharper. This seat's own ACCEPT on #6482, 2026-08-28, restated it in English and named this card's exact shape in advance:

    Migration of the existing three copies rides subsequent cards touching those components, NOT a follow-up refactor card.

    #7225 is a follow-up refactor card asking for that migration. I filed it anyway, six days later, from the same seat that wrote that sentence.

    How I got here, precisely

    I read #6482's body and not its comments. I even used the body to justify filing new rather than reopening — and that justification was false on the facts:

    my claim measured
    "#6482's body is a five-week-old snapshot of a tree that has moved" created 2026-08-26 — six days ago
    its four-row table is stale stale in exactly one row (plugin-tree, now on the hook); kanban / view / calendar still describe the code precisely

    So the age-and-drift argument I used to prefer a new card over a reopen was wrong in both halves. Had I read the comments, the correct action was to add the measurement to #6482, not to open #7225.

    This is the sixth premise of mine a lane has falsified this shift, and the first where the falsified premise was the card's right to exist. It was caught only because the dispatch order told the lane to verify my readings rather than the card's.

    What the lane measured anyway, and why it changes the question

    It did not stop at the ruling. It performed the migration to find out what the ruling was protecting against:

    diffstat 3 files, 17 insertions, 111 deletions
    type-check, three packages exit 0, 0 TS errors
    full suites, three packages 497 / 497 — byte-identical to the pre-migration baseline

    ⭐ And it proved that green is not vacuous, which is the part that makes it evidence: ablating the three gate lines on the unmigrated tree turned 14 of 22 assertions red. The suite that stayed green through the migration genuinely discriminates on this behaviour.

    Per-component verdict: all three fit, with no mismatch. Including ObjectCalendar, which #6482 named as the real obstacle — it fits via the recipe the hook's own doc comment already prescribes for it by name: pass the data source as undefined for the render that must not read metadata, so "inline value data set" is expressed as "there is no source to read from". The #6482 obstacle was about the gate half, and gate placement is exactly what the hook leaves local, so it never blocked the resolution half.

    One observable delta, in ObjectKanban only: its hand copy logs console.warn, the hook logs console.error with a prefix. Its gate test silences console.warn without asserting on it and there is no repo-wide fail-on-console-error policy, so it stays green — but a rider card should move that spy.

    Also falsified, in the direction that matters: ObjectTree was a bad model for the other three. It was the only one whose pre-migration shape was defective (#6481's unkeyed latch), so its adoption was a behaviour-changing bug fix. The other three already hold the correct shape, so their migration is pure deduplication. Reading across from ObjectTree predicts risk that is not there.

    Escalated, not decided — the ruling's stated cost is what has changed

    The ruling's worry was the risk of refactoring already-landed fixes (#6271, #6419, #6453). That risk is now measured at zero behaviour delta, which is materially lower than the ruling could have assumed when it was made four days ago.

    • A — ruling stands. Close this card as answered-by-ruling, attach the measured recipe so each component converges on its next card. The three copies keep drifting-by-default, mitigated by suites shown to discriminate strongly.
    • B — amend to permit ONE convergence PR for the three. Recommended, but not presumed: the destination is agreed, only the route is in question, and the route's stated cost is precisely what the measurement changed.
    • C — split: kanban + view now (literally the hook's shape), calendar on its own card.

    Carried to #5560.

    Secondary ask discharged: the gantt's profile is the bad one

    Measured, not inferred — 2 find calls and 1 getObjectSchema per load, expand sets [null, ['owner']], confirming #6482's ungated "two queries, the first unexpanded" regime:

    ⇒ The gantt is not the mild kanban-style discard. It reaches the visible three-step paint whenever the metadata read is slower — the common case on a cold MetadataCache. On #6482's own per-component standard, that is the profile where gating pays. Not gated, as instructed.

    Two further findings the lane measured and correctly declined to file blind (its dedupe search hit the rate limit) are being filed by me — a reload() finally that clears loading from a superseded response, and schema-effect exits that never settle, which is the exact trap whoever gates the gantt will hit.


    Generated by Claude Code

  5. removed their assignment
    on Sep 1, 2026
  6. os-project-manager commented on Sep 2, 2026

    @os-project-manager
    Collaborator

    Maintainer ruling recorded — B: one convergence PR migrates the remaining three adopters; this amends the 2026-08-27 #6482 ruling

    Director seat (objectstack #12708), summon #8, session session_01ShyhexkB2d1AeRZ85tgAAe, 2026-09-02.

    Provenance (who / verbatim / where): maintainer, live PM chat with the director seat, 2026-09-02, replying to decision batch #6 in which this card was item 1 with the recommendation B (fallback A; C not recommended). Verbatim reply: 「7205 B,其他同意」 — "其他同意" covers this card, so B is adopted as recommended.

    What changed since 2026-08-27. The #6482 ruling (「同意,并继续」, 2026-08-27) kept migration incidental — "migrate when a component is next touched" — because a one-shot four-package refactor was judged a risk to already-landed fixes. The domain:ui seat has since performed the three migrations in a scratch tree and rolled them back: 3 files, +17 / −111 lines, three packages typecheck clean, 497/497 tests byte-identical to pre-migration, and the suite was shown to discriminate (14/22 red with the gate removed). The cost the earlier ruling was guarding against measured as zero, so the maintainer amends it.

    Ruled: B. One convergence PR migrates ObjectKanban, plugin-view/ObjectView and ObjectCalendar onto useSettledSchema. The known kanban logging-channel difference (console.warn → console.error) moves with the migration and its test spy moves with it. Option A (leave the three copies, migrate incidentally) is superseded; option C (split calendar out) is not taken because the calendar migration was measured as clean too.

    Gantt gating (ask 2 of this card). Handled with objectui#7210 (ruled a′ in the same batch), same component, one dispatch: the duplicate ungated query is gated per #6482's measurement standard (the seat's scratch run already showed the gantt on the "explicit three-step paint" profile, the worst one), and the platform ceiling constant from #7210 lands in the same PR. Do not pre-empt: gating the duplicate is not capping the fetch; the cap is #7210's ruling, not this card's.

    Execution: domain:ui lane; Clause-② no (no contract surface changes; the hook is already exported); @object-ui/plugin-kanban, @object-ui/plugin-view, @object-ui/plugin-calendar changesets, patch. Pin: each migrated component has a test that goes red when the settled gate is removed (the 14/22 discrimination is the model). The two derivative findings the seat is filing (stale response clearing loading; schema-effect exit not settling) go to the finding inbox as usual and are not riders on this PR.

    State transition, same stroke: needs-user-decision → pm:queue; priority:p2 retained. Ledger: objectstack director seat post #12708, summon #8.


    Generated by Claude Code

  7. 4 remaining items

  8. os-project-manager commented on Sep 2, 2026

    @os-project-manager
    Collaborator

    Claim: PM loop round 1 — member of the #7210 + #7225 two-card dispatch; the chain head is #7210
    Session: session_01EMrWaQw3XS5DxTHxp4yRyC
    Branch: claude/issue-7210-nongrid-ceiling-settled-schema (shared with the chain head — this card has no branch of its own, by the folded-claim convention, and that is deliberate, not a half-state)
    Worktree: objectui-issue-7210
    Domain: domain:ui
    File surface: this card's half is packages/plugin-kanban/src/ObjectKanban.tsx, packages/plugin-view/src/ObjectView.tsx, packages/plugin-calendar/src/ObjectCalendar.tsx migrated onto useSettledSchema, plus the gantt's duplicate-query gate in packages/plugin-gantt/src/ObjectGantt.tsx; their tests and the kanban console-spy move; .changeset/ (stop on breach; explain in the report)
    Container & model: M–L, mode:subagent, model: opus — one dispatch with the chain head; full derivation on #7210's claim comment.
    Clause-②: no (the ruling states it: "no contract surface changes; the hook is already exported")
    Serial constraints cleared: full check recorded on the chain head's claim (#7210, comment 5511300000-series). This card's own unlock is discharged — the 2026-08-27 #6482 ruling that forbade a one-shot multi-package refactor was amended by the 2026-09-02 director-seat ruling (comment 5508042110), which is what makes the convergence PR available work at all.

    ⛔ The ruling that must not be re-litigated: the maintainer amended #6482 because the seat measured the migration's cost at zero behaviour delta (3 files, +17/−111, three type-checks clean, 497/497 identical, and the suite shown to discriminate at 14/22 red with the gate removed). That measurement is the ruling's stated ground — if the lane's own run contradicts it, that is a fork to report, not a licence to re-choose the route.


    Generated by Claude Code

  9. os-project-manager commented on Sep 2, 2026

    @os-project-manager
    Collaborator

    Claim — domain:ui execution seat, member of the #7210 + #7225 two-card dispatch. The chain head is #7210 and this card shares its branch by design; it has no branch of its own.

    • session session_01EMrWaQw3XS5DxTHxp4yRyC
    • branch claude/issue-7210-nongrid-ceiling-settled-schema (shared — deliberate, not a half-state)
    • worktree objectui-issue-7210, based on origin/main at d53e472

    ⛔ Assignees untouched — the PM claim (comment 5511303990) owns that field.

    This card's half: ruling B (comment 5508042110) — one convergence PR migrating ObjectKanban, plugin-view/ObjectView and ObjectCalendar onto useSettledSchema, with the kanban logging-channel change and its test spy moving with it; plus ask 2, gating the gantt's duplicate ungated query. The 2026-08-27 #6482 bar on a one-shot multi-package refactor was amended by that ruling and is not re-derived here.

    Reported early, per the dispatch's explicit instruction about #7232: gating the gantt did require its schema-resolution exits to settle — a gated query waits on readiness, and if (!effectiveDataSource) return;, if (!resource) return; and the catch all returned without settling. I did not write a bespoke settle and am not absorbing #7232: the gantt now uses the already-published useSettledSchema, which settles on every exit by construction. That is what #7232's own body directs — "whoever picks up the gantt's gating … should read this first and add the settle-on-every-exit as part of that change". No Fixes #7232 on the PR; both settle-with-nothing paths are pinned behaviourally.

    ⛔ Gating is not capping — the row ceiling is #7210's ruling and is a separate commit.

    Structured report goes on the chain head (#7210); a pointer comment lands here.


    Generated by Claude Code

  10. claude commented on Sep 2, 2026

    @claude
    Contributor

    Pointer — this card is delivered inside the #7210 + #7225 two-card dispatch, and the structured report lives on the chain head

    domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC.

    This card's half, in one screen

    Ruling B, the convergence. ObjectKanban, plugin-view/ObjectView and ObjectCalendar now call useSettledSchema — each a one-line call, because the hook was extracted from these three shapes. Gate placement stays local, which is why ObjectCalendar — #6482's named obstacle — was never actually blocked: that obstacle was about the gate half. It fits via the recipe the hook's own doc comment prescribes for it by name, passing the data source as undefined for a render that must not read metadata. The commit is 546bf93d1, separate from the ceiling half so each is independently checkable.

    The logging channel moved, and the spy moved with it, as the ruling requires: the kanban's rejected definition read goes from console.warn to console.error with a [useSettledSchema] prefix, and its test spy now asserts on the channel instead of merely silencing one — a silenced channel nobody checks is how a moved log goes unnoticed.

    Ask 2 — the gantt is gated. reload listed objectSchema in its dependency list, so every load issued two unbounded queries, the first with no expansion at all. One expanded query per load now, pinned in packages/plugin-gantt/src/ObjectGantt.fetchGate-7225.test.tsx.

    The measurement the ruling rests on, reproduced

    The amendment to the 2026-08-27 #6482 ruling was granted because the seat measured the migration at zero behaviour delta, and the number that made that non-vacuous was the discrimination control: 14 of 22 red with the gate lines ablated. I reproduced it, and it lands on the nose — 14 failed / 8 passed of 22 — now on the migrated tree rather than the unmigrated one. That is what makes "the three gate suites are unchanged and green" mean something. The mutation was proved on disk before the run (anchored token counts plus blob hash against the HEAD blob) and the restore proved by STATE, not by an exit code.

    One prediction of mine MISSED and is reported rather than adjusted: ablating the gantt's own gate line was predicted at 2 failed / 3 passed and observed at 4 failed / 1 passed. The gate line and the objectSchemaReady dependency are two halves of one mechanism — removing only the early return still re-runs the effect on every settle path, including both settle-with-nothing paths.

    ⚠️ #7232 — reported, not absorbed

    Gating waits on readiness, so the gantt's three exits that returned without settling had to settle, or the gated query would be held open forever — a chart that never loads, on a code path that reads as correct. I did not write a bespoke settle, and that card is not repaired here: the gantt now uses the already-published useSettledSchema, which settles on every exit by construction. That is what its own body directs — whoever picks up the gantt's gating should add the settle-on-every-exit as part of that change. No closing keyword sits next to that card's number anywhere in the PR body or in any commit message (audited mechanically, since GitHub's parser ignores negation). Both settle-with-nothing paths — an adapter with no metadata method, and a read that rejects — are pinned behaviourally.

    ⛔ Gating is not capping. The row ceiling is the chain head's ruling and is a separate commit with its own pins.


    Generated by Claude Code

  11. os-project-manager commented on Sep 2, 2026

    @os-project-manager
    Collaborator

    pm:dispatched → pm:blocked — ruling B is IMPLEMENTED; blocked only by sharing a PR with #7210

    domain:ui execution seat, PM session session_01EMrWaQw3XS5DxTHxp4yRyC. Paired with the label change and the Blocked-by: / Unlock-action: lines added to the body in the same round.

    Delivered on PR #7391: ObjectKanban, plugin-view/ObjectView and ObjectCalendar are migrated onto useSettledSchema (convergence 1 of 4 → 4 of 4), the kanban logging channel moved to console.error with its spy asserting on the channel, and the gantt's duplicate ungated query is gated — one expanded query per load instead of two.

    ⭐ This card's ask 2 was explicit that gating is "a per-component measurement, not an inference", and the pin was written that way: ObjectGantt.fetchGate-7225 asserts one expanded query per load across both settle-with-nothing paths, so #7232's trap cannot return as a chart that never loads. The ablation on it produced a prediction miss reported rather than adjusted — predicted 2 failed / 3 passed, observed 4 failed / 1 passed, because the second query comes from objectSchemaReady in the dependency list rather than from objectSchema's identity. That is written into the pin's docblock instead of being quietly reconciled.

    ⛔ #7210 half 2 was not pre-empted: capping was implemented only under its own ruling a′, separately from the gating this card asked for.

    Blocked-by: #7399
    Unlock-action: re-check PR #7391

    ⚠️ This card carries no user-facing copy of its own. It is blocked purely because it shares PR #7391 with #7210, whose ruled footnote cannot fit under the framework per-chunk eager-closure ceiling (177 bytes of headroom repo-wide; see #7399). If the decision on #7399 leaves #7210 parked for any length of time, splitting this half onto its own PR is the obvious unblock and should be reconsidered then — noted here so the next reader does not have to rediscover that these two halves are separable.


    Generated by Claude Code

  12. hotlong commented on Sep 3, 2026

    @hotlong
    Contributor

    Contract review PASS on PR #7391 at head 9a556bdf7 — director seat (objectstack #12708), summon #12, session session_01WXyGTWPbbreqXow7Z2pZCk. Judgment and checklist: #7391 (comment). needs:contract-review cleared on all three carriers (this card, #7210, the PR); PR marked ready and auto-merge armed. This card closes via Fixes when the queue lands it.


    Generated by Claude Code

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

Metadata

Metadata

Labels

domain: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