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.

min(960px, 60vw) is a four-site house default, not one renderer's choice — the width/size question on #6259 decides all of them #6303

Closed

Description

@os-support-ai

Filed from the #6259 / #6258 dispatch. Out of that card's scope by explicit exclusion (the dispatch named plugin-calendar's navConfig a separate finding to file, not to fix), so recording rather than fixing.

Blocked-by: #6259 — the ruling there decides this; do not action independently.

The fact

The literal min(960px, 60vw) is not local to ObjectGantt. On origin/main @ 090927f4f it is produced in four places:

file line shape
packages/plugin-gantt/src/ObjectGantt.tsx 1150 schema.navigation ?? { mode: 'drawer', width: 'min(960px, 60vw)' }
packages/plugin-kanban/src/ObjectKanban.tsx 606 (schema as any).navigation ?? { mode: 'drawer', width: 'min(960px, 60vw)' }
packages/plugin-kanban/src/ObjectKanban.tsx 899 width={(navigation.width as any) ?? 'min(960px, 60vw)'}
packages/plugin-calendar/src/ObjectCalendar.tsx 389 (schema as any).navigation ?? { mode: 'drawer', width: 'min(960px, 60vw)' }
packages/plugin-calendar/src/ObjectCalendar.tsx 694 width={(navigation.width as any) ?? 'min(960px, 60vw)'}
packages/plugin-detail/src/RecordDetailDrawer.tsx 129 width = 'min(960px, 60vw)' (the component's own prop default)

RecordDetailDrawer also carries the only written rationale for the value (:76-78):

Drawer width — accepts any CSS width value. Defaults to min(960px, 60vw) which fills ~60% of typical desktop viewports (the prior max-w-2xl cap felt cramped on wide screens).

Why it matters as one finding rather than three cards

Every navigation-config site above spells the width with the field the spec deprecated (@deprecated [#2578 → size]), and resolveOverlayWidth gives an explicit width priority over size — so on all three renderers a view declaring no navigation can never be reached by the size bucket system. #6259 asks whether that is deliberate for the gantt. Whatever is decided there applies verbatim to kanban and calendar; splitting it into per-renderer cards would relitigate the same question three times, and fixing one renderer alone would make the three disagree.

Note the two render-site fallbacks (kanban :899, calendar :694) are a third copy of the literal that would survive a change to the navConfig defaults — they are ?? 'min(960px, 60vw)' applied to navigation.width after the hook has already resolved it. Any convergence work has to take those too, or the renderers keep the old width by a different route.

Measurement carried over from #6259

The two spellings are not interchangeable. OVERLAY_SIZE_WIDTHS.lg is min(92vw, 960px); the current default is min(960px, 60vw). They agree only at viewport ≥ 1600px — below that lg is strictly wider (+11% at 1440px, +25% at 1280px, +53% at ≤1044px). So this is a behaviour decision for three renderers, not a spelling cleanup.

No behaviour is reported broken. Recording the shape, not a defect.


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 Aug 25, 2026
  2. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    CollaboratorAuthor

    Unblocked — upstream #6259 closed, and the file face was re-verified on the merged ref before this card was returned to the queue.

    Blocked-by: #6259 resolves: #6259 closed completed on 2026-08-25T13:06:16Z via merged PR #6305, "drop the deprecated width from the drawer default, state the navConfig comment once".

    The unlock scan re-measures the returning card's file face rather than trusting the card, because the merge that closed the upstream is the merge most likely to have swept the sibling sites too. It did not. Read from main through the contents API just now, and every line number in the card's table has moved, so the card's own table should be re-measured rather than trusted:

    file card said on main now state
    packages/plugin-gantt/src/ObjectGantt.tsx 1150 1285 converged — schema.navigation ?? { mode: 'drawer' }, no width
    packages/plugin-gantt/src/ObjectGantt.tsx (render site) — 1724 converged — width={navigation.width as any}, no ?? literal
    packages/plugin-kanban/src/ObjectKanban.tsx 606 693 unchanged — ?? { mode: 'drawer', width: 'min(960px, 60vw)' }
    packages/plugin-kanban/src/ObjectKanban.tsx 899 995 unchanged — width={(navigation.width as any) ?? 'min(960px, 60vw)'}
    packages/plugin-calendar/src/ObjectCalendar.tsx 389 449 unchanged
    packages/plugin-calendar/src/ObjectCalendar.tsx 694 763 unchanged
    packages/plugin-detail/src/RecordDetailDrawer.tsx 129 129 unchanged — and this one is the rationale home, see below

    So the divergence this card predicted has actually happened: one of the three renderers moved, the other two did not.

    What the ruling decided, and what it explicitly did not

    #6305 left its reasoning in the code (ObjectGantt.tsx:1265-1284), and it draws the boundary this card now sits on. Two separable halves, quoted from that comment:

    Decided — the width half, and it is zero-pixel:

    No width is spelled here on purpose. width is @deprecated [#2578 -> size] in the spec that owns this shape, and resolveOverlayWidth gives an explicit width priority OVER size — so spelling it kept the deprecated branch load-bearing on the path most gantts take (no declared navigation), and made the size buckets unreachable there. Omitting both leaves resolveOverlayWidth returning undefined, which is what RecordDetailDrawer's own width default is for; that default is the identical min(960px, 60vw), so this is a zero-pixel change on every viewport.

    Still open — the bucket half, and it is a real behaviour change:

    Deliberately NOT converged on size: 'lg': that bucket is min(92vw, 960px), which agrees with the above only at viewport >= 1600px and is up to 53% wider below it. Whether the renderers should move to the bucket is open (#6259), and spans kanban/calendar/RecordDetailDrawer as one decision (#6303).

    That second paragraph names this card by number as the home of the open decision. The card's own framing — "the width/size question on #6259 decides all of them" — turns out to be broader than what was actually decided: #6259 answered the width half for the gantt and deliberately left the bucket question open.

    Returned to pm:queue on the decided half only. The dispatch will be scoped to it and will forbid the bucket move; the bucket question stays where the code comment puts it — on this card, for a human ruling — and nothing in the dispatched work may pre-empt it. Whether that warrants splitting the card in two is triage's call, not this seat's.

    RecordDetailDrawer.tsx:129 is deliberately excluded from the decided half: it is not a fourth copy of a default that should be deleted — it is the source of the default the other three now delegate to, and it carries the only written rationale for the value (:76-78). Deleting it is what would change pixels.


    Generated by Claude Code

  3. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    CollaboratorAuthor

    Claim: PM session session_011SfZeFWrhGLHmfq61xbz4q (domain:ui execution seat, objectui). Branch claude/issue-6303-kanban-calendar-nav-width. Dispatched this round to one os-dev agent, in its own worktree.

    Scope dispatched: the decided width half only — converge ObjectKanban.tsx and ObjectCalendar.tsx on the shape ObjectGantt.tsx already has after #6305, at both copies each (the navConfig default and the render-site ?? 'min(960px, 60vw)').

    ⛔ Explicitly out of the dispatched scope, and the agent has been told to stop and report rather than absorb it: any move to size: 'lg' on any renderer, and any change to RecordDetailDrawer.tsx's own width default. The bucket question stays open on this card for a human ruling, exactly where ObjectGantt.tsx:1280-1284 says it lives.


    Generated by Claude Code

  4. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 6303,
      "status": "done",
      "branch": "claude/issue-6303-kanban-calendar-nav-width",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6581",
      "premise_still_valid": true,
      "summary": "Converged ObjectKanban and ObjectCalendar on the post-#6305 gantt shape at all four sites: both navConfig defaults become `{ mode: 'drawer' }` and both render-site `width={(navigation.width as any) ?? 'min(960px, 60vw)'}` become `width={navigation.width as any}`. All four line numbers (kanban 693/995, calendar 449/763) were re-measured on origin/main @ b1a732b22 and matched the dispatch reading exactly, so nothing in the order needed correcting. The escalation clause was checked rather than assumed and did NOT trigger: both renderers import RecordDetailDrawer from @object-ui/plugin-detail and render it directly with width={...}, exactly as the gantt does; resolveOverlayWidth returns `undefined` (never `null`), so the drawer's default parameter `width = 'min(960px, 60vw)'` does apply; and navConfig.width / navigation.width have no other consumer in either file. Zero-pixel confirmed. RecordDetailDrawer.tsx untouched, no move to size:'lg'. Both files carry a fitted version of the gantt's rationale comment at the navConfig site plus a short note at the render site, so a future reader cannot read the deleted width as an oversight.",
      "tests": "All measurements anchored at head d8814f129 (the commit after the source commit adds .changeset/*.md and nothing else, proven by `git diff 087514d61 d8814f129 --stat` = 1 file, so the suite/lint runs measured a byte-identical packages/**). Every heavy command ran through scripts/pm/os-verify-lock.sh; exit codes captured by redirect-then-capture, never after a pipe; verdicts quoted are each gate's own printed line. NEW PIN TESTS: `pnpm exec vitest run packages/plugin-{kanban,calendar}/src/Object*.navWidthDefault.test.tsx` -> `Test Files 2 passed (2)` / `Tests 6 passed (6)`, os-verify-lock VERDICT command-exit 0. AFFECTED PACKAGE SUITES: `pnpm exec vitest run packages/plugin-kanban/ packages/plugin-calendar/` -> `Test Files 34 passed (34)` / `Tests 210 passed (210)`, VERDICT command-exit 0. LINT: the full repo farm actually ran, NOT narrowed -- `pnpm exec turbo run lint --concurrency=2` -> `Tasks: 47 successful, 47 total`, VERDICT command-exit 0 (4m26s under the lock). TYPE-CHECK: `pnpm --filter @object-ui/plugin-kanban --filter @object-ui/plugin-calendar run type-check` -> `Scope: 2 of 47 workspace projects`, both echoed `tsc --noEmit && tsc -p tsconfig.test.json` and printed `Done`, exit 0 -- and the zero-match-filter trap was ruled out by that echo. NOT-MEASURED trap ruled out too: `tsc -p tsconfig.test.json --listFiles | grep -c navWidthDefault.test.tsx` = 1 in each package, so the type-check green genuinely covers the new tests. BUILD (dependency closure, required because tsconfig.test.json drops paths and resolves @object-ui/* through built .d.ts): `turbo run build --filter=@object-ui/plugin-kanban... --filter=@object-ui/plugin-calendar... --concurrency=2` -> `Tasks: 15 successful, 15 total`, exit 0. GATE UNION re-run at final head d8814f129, all exit 0 with their own verdict lines: check-changeset-presence (declares 1 changeset), check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check-control-bytes (scanned 5435 tracked text files), check-vi-mock-specifiers, check-lint-coverage (46/46 packages, 0 errors), check-type-check-coverage, check-package-self-import, check-phantom-dependencies. REVERSE-VERIFICATION, direction predicted BEFORE running: restoring the pre-fix expressions should turn ONLY half 1 red in each file, because half 2 and the authored-width control cannot see a re-added `??` fallback (its value is the same string the drawer default produces) -- that blind spot is exactly why half 1 exists. Leg A: both source files restored from merge-base b1a732b22; mutation proved on disk by grep counts of BOTH the injected text (navConfig=1, render=1 in each file) and the deleted fixed-form text (0 in each), never by an editor exit code; result `Test Files 2 failed (2)` / `Tests 2 failed | 4 passed (6)`, and the two failures are both `half 1` while both `half 2` and both authored-width controls stayed green -- prediction matched exactly. Leg B (half 2's own load-bearing check): RecordDetailDrawer's default temporarily moved to `min(900px, 55vw)`, deleted-text count 0 / injected-text count 1 confirmed on disk; result `Test Files 2 failed (2)` / `Tests 2 failed | 4 passed (6)` with the two failures both `half 2` this time and both `half 1` plus controls green -- so half 2 measures the drawer's default rather than restating half 1. Both legs ran under `trap '<restore>' EXIT INT TERM` with absolute paths resolved from `git rev-parse --show-toplevel`, restored via `git checkout HEAD -- <path>` (named ref, never bare, so a polluted index cannot hand the mutation back). Restoration proved by BYTE IDENTITY, not exit code: `git diff HEAD` empty and `git hash-object` back to the HEAD blobs recorded before each mutation (kanban ace66b96, calendar 2592c8aa, drawer da15724a). No dist/ablation asymmetry applies here: the root vitest config aliases @object-ui/plugin-detail straight to packages/plugin-detail/src, so both legs measured source, and the rebuild-before-reading requirement is satisfied by construction rather than skipped. CHANGESET: .changeset/kanban-calendar-nav-width-converge.md with EMPTY frontmatter, matching what #6305 used for the gantt half; check-changeset-presence.mjs before = exit 1 (`4 source file(s) of 2 released package(s) changed, and this change adds no changeset`), after = exit 0 (`Every one of them has an EMPTY frontmatter -- declared as releasing nothing, which is the explicit exemption and a complete answer to this gate`). The body states the change is zero-pixel rather than inventing a behaviour claim. LINT WARNINGS, disclosed: each new test file adds 3 @typescript-eslint/no-explicit-any warnings -- the same rule the gantt pin test triggers; errors are 0 and .github/workflows/lint.yml:20 deliberately sets no --max-warnings.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Notes for the PM beyond the JSON

    Nothing in the dispatch order needed correcting. Every line number matched, and the escalation clause did not fire — the measurement supports the zero-pixel ruling for kanban and calendar just as it did for the gantt.

    One check the order did not ask for, reported because its answer bears on whether this card is now fully discharged on the decided half: a repo-wide sweep for a fifth divergent renderer found none. All three navConfig defaults now read { mode: 'drawer' }; plugin-view/src/ObjectView.tsx (:1901, :1958) already passes navigationConfig?.width with no literal fallback, so it delegates to the drawer too; plugin-tree and plugin-map use useNavigationOverlay but spell no width. After this PR the only remaining producer of min(960px, 60vw) in the repo is RecordDetailDrawer.tsx:129 — the intended single source — plus comment text and the three pin tests' EXPECTED_WIDTH constants. No new issues filed.

    The open half is untouched and stays yours. No renderer was moved to size: 'lg', and RecordDetailDrawer.tsx was not edited. Leg B of the reverse-verification did temporarily mutate that file to prove half 2 is load-bearing; it was restored and the restoration proved by blob-hash identity (da15724a89fbb2a8eb664c97f73f913281c9f6ce), so it is byte-for-byte absent from the PR.

    CI status is in_progress — reporting at draft-PR time per the dispatch contract; CI convergence is the PM's read, not mine.


    Generated by Claude Code


    Generated by Claude Code

  5. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    CollaboratorAuthor

    Post-merge verification — PR #6581 merged 2026-08-26T14:50Z. Measured on main: the dispatched half is clean, and the deferred half needs a new home (#6584).

    The fix — the three renderers agree again

    Read back from main. All three now have byte-equivalent shapes at both sites:

    ObjectKanban.tsx:713    const navConfig = (schema as any).navigation ?? { mode: 'drawer' };
    ObjectCalendar.tsx:466  const navConfig = (schema as any).navigation ?? { mode: 'drawer' };
    ObjectGantt.tsx:1285    const navConfig = schema.navigation ?? { mode: 'drawer' };
    
    ObjectKanban.tsx:1018   width={navigation.width as any}
    ObjectCalendar.tsx:783  width={navigation.width as any}
    ObjectGantt.tsx:1724    width={navigation.width as any}
    

    The literal now occurs in code exactly once across the four files involved — RecordDetailDrawer.tsx:129, which is its home and the value the other three delegate to. Every remaining occurrence in the renderers is prose inside the explanatory comments (kanban 2, calendar 2, gantt 1, all non-code). That is the invariant this card existed to restore, and it holds.

    Both ⛔ exclusions survived

    • RecordDetailDrawer.tsx:129 untouched — width = 'min(960px, 60vw)', still there. Deleting it is what would have changed pixels, and it was excluded by name in the dispatch.
    • No move to size: 'lg'. It appears exactly once per renderer, and every one of the three is the "Deliberately NOT converged on size: 'lg'" comment. Better than compliance: the deliberate non-action is now documented at all three sites rather than only at the one where it was first decided.

    ⚠️ The deferred half lost its tracker — filed as #6584

    Those same three comments name the card that owns the open bucket decision, and post-merge that name is stale in every direction:

    file:line names state now
    ObjectKanban.tsx:712 #6303 closed (this card, by this merge)
    ObjectCalendar.tsx:465 #6303 closed
    ObjectGantt.tsx:1283 #6259 and #6303 both closed

    Nothing is broken in the shipped code; what is missing is a live home for a real published-behaviour question — size: 'lg' is min(92vw, 960px), up to 53% wider below a 1600px viewport, across four record surfaces, and it has to be answered once for all four or the divergence this card closed re-opens.

    Neither PR did anything wrong. #6305 answered the width half and pointed forward at this card; #6581 did the same for the other two renderers; Fixes #6303 then closed the card and took the undecided half with it. The reusable lesson, recorded on #6584: when a dispatch deliberately narrows to the decided half of a question, the open half needs its own card at dispatch time, not at merge time — and the code comments should point at that card from the start. That is on this seat, which wrote the narrowing.

    #6584 is filed unassigned and ungraded for triage.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

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

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions