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.

plugin-gantt: the task list is capped at 320px and its Start/End columns take 160px of that, so task names get 53px and truncate to ~7 characters while 864px of chart sits empty #7204

Closed

Description

@os-warren

Found in a browser pass over a real application's gantt view (objectstack-ai/duly#62). Filed unassigned, not claiming.

Measured on @objectstack/console 17.2.0 (objectui 190fbd01d061) in Chromium 1194. Re-checked against current main 84ffdbcbb33762b5488742fb902faf45a3742e93: both functions below are unchanged there.

Symptom

Every task name truncates to about seven characters. Two rows reading Emis… and Emis… with different dates are not distinguishable, which defeats the view.

task names truncated to about seven characters

Measurement

Same page, two viewport widths, read from the DOM (all values px):

viewport task-list panel name cell Start col End col open btn chart area title span client / needed
1440 320 99 80 80 24 864 53 / 260
1920 320 99 80 80 24 1344 53 / 260

Site environmental audit — Northgate needs 260px and is given 53px. The panel does not grow with the viewport: at 1920 the chart gains 480px and the name column gains nothing.

Two contributors, both on current main

1. The default caps at 320px regardless of how much room exists.

function taskListWidthForContainer(width: number) {
  if (width < 640) return 140;
  if (width < 1024) return 220;
  return 320;
}

2. At that default, the Start/End sub-columns switch on and take 160 of the 320.

// Show the Start/End sub-columns only when the task list is wide enough that
// the title still has room. Below this threshold the title would collapse to
// a few pixels (issue: bars rendered but names invisible).
function showStartEndColumns(taskListWidth: number) {
  return taskListWidth >= 280;
}

This is the sharper half, and its own comment states the intent it inverts. The threshold protects the narrow cases (140 and 220) and then produces the very outcome it names at the default wide case: 320 clears the threshold by 40px, and the columns it admits cost 160px. The title is left with 53px — "a few pixels", by the comment's own standard.

Worth noting the 160px buys a second copy of information already on the row: each row renders 8/26 → 9/2 as a sublabel directly under the title.

Proof that width is the whole story

Dragging the splitter to 580px gives the title exactly the 260px it asked for, and every name becomes readable — same data, same page, same build:

the same rows with the splitter dragged to 580px

Measured across that drag: panel 320 to 580, title client width 53 to 260, needed 260 throughout.

Why an application author cannot ship the fix

taskListWidth is only ever seeded from the persisted layout — that is, from a previous user drag in that browser's localStorage. There is no authorable path to it: the spec's GanttConfigSchema declares 19 keys (startDateField, endDateField, titleField, progressField, dependenciesField, colorField, parentField, typeField, baselineStartField, baselineEndField, groupByField, resourceView, assigneeField, effortField, capacity, tooltipFields, quickFilters, autoZoomToFilter, viewMode) and none of them is a width. So today the remedy exists per user, per browser, and cannot be shipped with the app.

The gantt block is a passthrough object, so an invented gantt.taskListWidth would pass author-time validation and be silently ignored. That is precisely why this is filed rather than authored around.

Reproduction

  1. Any gantt view whose titleField holds real-world names (25 to 40 characters).
  2. Load it at 1280px or wider.
  3. Every name truncates in the first two words while the chart area holds 850px or more of empty grid.

Suggested directions — maintainer's call, none proposed as decided

  • A. Size the default from the container rather than capping it — a share of the width, clamped, so a 1920px screen spends some of its extra 480px on names.
  • B. Raise the Start/End threshold so the two date columns appear only when the title genuinely has room after paying for them (their combined 160px plus a legible title, so roughly 440px, not 280px) — and consider whether they should default off at all, given the row already prints the same dates as a sublabel.
  • C. Declare a width key on the gantt block so an author can ship a sensible default for their own data. Weakest on its own: it makes every author configure their way out of a bad default, but it is the one that also serves apps whose names are unusually long.

A and B are independent and both cheap; either alone lifts the title above the "few pixels" the comment already identifies as the failure.

Related: #7070 (gantt/timeline date-axis findings).

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 1, 2026
  2. os-warren commented on Sep 1, 2026

    @os-warren
    CollaboratorAuthor

    Triage — bug · domain:ui · priority:p2 · pm:queue

    Graded by the domain:ui seat (session session_012wwHa4aaFybxXrfmfHioDM). Queued behind three running lanes; dispatched when one frees.

    Pre-ruling, so the dispatch is short when it goes out

    The card offers A / B / C as a maintainer's call. Two of the three do not actually need one:

    • B is a defect repair, not a design choice, and it is the dispatchable half. showStartEndColumns returns taskListWidth >= 280, and its own comment says the threshold exists so that "below this threshold the title would collapse to a few pixels." At the 320px default the gate admits columns costing 160px and leaves the title 53px. The function produces the exact outcome its comment names as the thing it prevents. That is a self-refuting gate, and fixing it needs no product decision — the intent is written directly above the code.
    • C is contract-widening (a new key on GanttConfigSchema, which the card confirms declares 19 keys and no width). That routes through CONTRACT_REVIEW_TIER, whose quota is currently exhausted. Not dispatchable today regardless of its merits.
    • A — sizing the default from the container — is the genuine design call, since it changes layout for every gantt at every viewport. Left for the maintainer.

    So B ships alone, which the card's own closing sentence already permits: "A and B are independent and both cheap; either alone lifts the title above the 'few pixels'."

    ⚠️ The one thing the lane must verify before B is safe

    Raising the threshold means the Start/End columns disappear at the 320px default. That is only non-destructive if the dates remain on screen elsewhere. The card asserts they do — "each row renders 8/26 → 9/2 as a sublabel directly under the title" — and that single sentence is the whole safety argument for B.

    It will be dispatched as an assumption to falsify, not as a finding. If the sublabel is conditional (on row height, a compact mode, a config key, or a missing end date), then B removes information rather than de-duplicating it, and the shape has to change. A measurement, not a reading, decides that.

    Why the report is strong

    The splitter drag is a genuine control, not an illustration: same data, same page, same build, panel 320 to 580, title client width 53 to 260, needed 260 throughout. That isolates width as the entire cause and rules out font, overflow rules, and the data itself in one move.

    Also correctly identified: the gantt block is a passthrough object, so an invented gantt.taskListWidth would pass author-time validation and be silently ignored — which is why this was filed rather than worked around. That is the same silent-inert class as #7199.


    Generated by Claude Code

  3. self-assigned this
    on Sep 1, 2026
  4. os-warren commented on Sep 1, 2026

    @os-warren
    CollaboratorAuthor

    Claimed and dispatched — domain:ui execution seat

    • session session_012wwHa4aaFybxXrfmfHioDM
    • branch claude/issue-7204-gantt-tasklist-width

    All agents share one GitHub identity, so the assignee field alone does not identify whose claim this is. If your session ID differs from the one above, this card is taken.

    Dispatched as option B only, per the ruling already recorded in the triage comment above: the Start/End threshold is a defect repair, not a design choice, because showStartEndColumns produces the exact outcome its own comment names as the thing it prevents. Option A (sizing from the container) stays the maintainer's call; option C is contract-widening and tier-blocked regardless.

    The one thing the lane is told to falsify before shipping

    B's entire safety argument is a single sentence in the report — that each row already renders 8/26 → 9/2 as a sublabel under the title, so removing the two date columns de-duplicates rather than deletes. That sentence is dispatched as an assumption to falsify, not a finding, with an explicit instruction to stop and report if the sublabel turns out to be conditional on row height, a compact mode, a config key, or a missing end date.

    This is not boilerplate caution. Twice this shift a lane has come back having falsified one of my premises — #7179's "single $select build site" (there are two, and a one-site fix would have shipped green and broken) and #7199's "description is the only unrelayed key" (there are three). Both were caught only because the order required verifying the PM's readings, not just the card's. This card's premise is one unverified sentence carrying the whole change, so it gets the same treatment.


    Generated by Claude Code

  5. os-warren commented on Sep 1, 2026

    @os-warren
    CollaboratorAuthor

    os-dev-report

    {
    "issue": 7204,
    "status": "needs_decision",
    "branch": "claude/issue-7204-gantt-tasklist-width",
    "pr": null,
    "premise_still_valid": false,
    "summary": "STOPPED on the falsified ZONE 2 premise; no code shipped, no PR. The card's whole safety sentence for option B — 'each row renders 8/26 → 9/2 as a sublabel directly under the title' — is CONDITIONAL, on viewport width. The sublabel span carries the class gantt-sm-hidden, and GanttView injects its own style element containing @media (min-width: 640px) { .gantt-sm-hidden { display: none; } }. Measured in a real Chromium 1194 against the component's OWN rendered markup and its OWN injected style element: computed display is inline at viewports 500 and 639 (client width 72.9px) and none (client width 0) at 640, 800, 1024, 1440 and 1920. The sublabel is therefore display:none at exactly the viewports where the Start/End columns are painted and where the reporter measured the defect. Raising the threshold as ruled would DELETE a row's dates at every desktop width rather than de-duplicate them — precisely the stop-and-report condition in the order. Worse, the two gates already fail to cover each other: at a 640..1023 viewport the default task-list width is 220, showStartEndColumns(220) is false, and the sublabel is already display:none, so current main ships a whole band with NO dates on the row at all. Filed as #7224. Option B cannot ship in its ruled shape; the shape question is escalated below.",
    "tests": "No implementation, so no before/after suite and NO ABLATION — an ablation with nothing to ablate would be theatre; stating that rather than inventing a colour. What I ran, all against origin/main 3561bd2 (worktree HEAD == origin/main, verified by git rev-parse), all under the shared verify lock:\n\n(1) jsdom render probe — bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c 'pnpm vitest run packages/plugin-gantt/src/GanttView.probe7204.test.tsx', run from the REPO ROOT (never pnpm --filter from a package dir). VERDICT line: command-exit 0 · held the lock 10s · waited 0s. Control: Test Files 1 passed (1) · Tests 3 passed (3) — 3 renders at container widths 1440 / 800 / 500, one file, matching what I wrote. Findings dumped to JSON (this repo's vitest config swallows console output, so the probe wrote files instead): at 1440 the row's 4 children are [title cell, start cell, end cell, open slot] with the two date cells NOT hidden; at 800 both date cells carry hidden attr + inline display: none AND the list header renders only Task Name plus the open spacer (no Start/End captions); at 500 the pane width is 0px because the panel auto-collapses on first narrow render. Every render, at every width, contains exactly one sublabel span: class=\"text-[10px] text-muted-foreground gantt-sm-hidden\" with text 8/26 → 9/2.\n\n(2) NOT MEASURED, declared: jsdom's own getComputedStyle(sublabel).display returned none at 500, 800 AND 1440 alike — jsdom applies the media-query rule irrespective of innerWidth, so that reading is NOT a width-dependent measurement and I did not treat it as one. It is why the decisive measurement was moved to a real browser.\n\n(3) Real-browser measurement — Chromium 1194 from the preinstalled /opt/pw-browsers/chromium-1194 (no playwright install; the first attempt failed with MODULE_NOT_FOUND because the script sat outside the workspace — a resolution error, reported as unmeasured, not as a red — and was re-run from inside the worktree). It loads the markup the component itself emitted in (1), including the style element the component itself renders, and reads computed display at 7 viewports: 500 inline/72.9px, 639 inline/72.9px, 640 none/0, 800 none/0, 1024 none/0, 1440 none/0, 1920 none/0. Control in the same evaluation: window.matchMedia('(min-width: 640px)').matches flips false→true exactly at 640, and .gantt-sm-w20 computes to 80px at every viewport at or above 640 — which is where the card's 80+80=160px comes from, measured rather than adopted. Scope of this measurement, stated honestly: the page carries the component's own injected CSS but not the prebuilt Tailwind sheet, so it settles the display question exactly (nothing but .gantt-sm-hidden sets display on that span, and that class appears nowhere else in the repo) and does NOT reproduce the absolute title-pixel widths.\n\n(4) Title width DERIVED from source, not copied: at a viewport at or above 640 with onTaskClick live, taskListWidth 320 minus px-4 padding (32) minus Start (80) minus End (80) minus open slot (w-6 24 + marginLeft 4 = 28) = 100px name cell; inside it, spacer 12 with marginLeft -4 (8 effective) + gap-2 8 + dot 8 + gap-2 8 = 32 of furniture, leaving 68px for a top-level row and 68 - 14*depth for an indented one, i.e. 54px at depth 1. The card's measured 99 / 53 lands within 1px of the derivation; I report the derivation, not their table.\n\nWorktree torn down after the run (rm -rf node_modules then git worktree remove, no --force, tree confirmed clean: git status --short empty, git diff BASE --stat empty). The branch was pushed empty at minute one as the write-route probe (push succeeded) and is left in place as the landing marker for the PM's claim comment; it holds no commits. No changeset — nothing user-visible shipped.",
    "mcp_calls": "6 — issue_read get #7204, issue_read get_comments #7204, search_issues (dedup, one targeted call after a repo-scoped REST probe returned 403 with and without auth, so the REST lane was unavailable; the search returned #7204 itself as a live-channel control), issue_write create #7224, issue_read get #7224 (sanitizer read-back), add_issue_comment (this report).",
    "open_questions": [
    {
    "question": "ZONE 2 is falsified: the date sublabel is display:none from a 640px viewport up, so removing the Start/End columns at the 320px default deletes a row's dates instead of de-duplicating them. Where should a row's start/end dates live at desktop widths when the task list is too narrow to pay 160px for the two columns? This is the shape change ZONE 2 anticipated, and it is outside option B's ruled scope, so it comes back rather than shipping.",
    "options": [
    "A. One predicate, container-driven: render the sublabel exactly when the Start/End columns are NOT painted, then raise the threshold as option B intended. Also closes #7224 (the 640..1023 band that today shows no dates at all). Cost: the sublabel becomes visible at desktop widths where it never was, adding a second line to every row inside the existing 40px row height (it already renders inside a 32px row below 640, so it fits). Care needed: 'the columns are painted' is currently the AND of a container test (showStartEndColumns) and a viewport test (Tailwind hidden sm:block), and only the conjunction is a safe complement.",
    "B. Move the sublabel's gate off the viewport onto the same container width the column gate reads, leaving both thresholds otherwise alone. Smallest diff, fixes #7224's mismatch at its root, but on its own does NOT fix #7204 — at a 1440 viewport the container is also wide, so the columns still win and the title still gets ~54px.",
    "C. Ship option B exactly as ruled and accept that at 320px the dates leave the task list, on the argument that the bar itself encodes the dates and the hover tooltip prints them. Cheapest; but the tooltip fallback is conditional too — it prints dates ONLY when task.fields is empty, so the documented tooltipFields config key removes that fallback as well, and hover-only is not equivalent to an at-a-glance column. It also silently removes inline date EDITING, since the two type=date inputs live in those same two cells.",
    "D. Do nothing on B; escalate the whole card (A/B/C of the original) to the maintainer as one layout decision, since option A of the card (sizing from the container) would make the 320px cap moot and dissolve the conflict without touching either gate."
    ],
    "recommendation": "A, escalated for the maintainer's yes rather than shipped. Real business need: measured pull on both sides — the reporter's live app (objectstack-ai/duly#62) needs the names, and #7224 shows a band that today paints no dates at all, so both the title and the dates have demonstrated demand; C serves the names by spending the dates, which is a trade nobody has asked for. Long-term soundness for THIS project: the defect's root is that two gates read two different widths (ResizeObserver container vs CSS viewport media query) and neither is the complement of the other; A collapses them to one predicate and is the only option that cannot drift back apart — B and C leave the split standing. Making AI-written metadata apps hard to get wrong: an author has no way to express any of this (GanttConfigSchema declares 19 keys and no width, and the gantt block is a passthrough object, so an invented gantt.taskListWidth validates and is silently ignored), so the correct behaviour has to be structural in the component; A also removes a state where declared, fetched data is rendered into the DOM and painted zero times, which is exactly the silently-inert class this repo keeps filing. Startup scope discipline: A adds no key, no capability and no authorable surface — it is a repair of an existing gate, so it does not widen the contract and does not touch the exhausted CONTRACT_REVIEW_TIER quota that blocks the card's option C. The one honest cost of A is visual: every desktop row gains a second line. That is a look-and-feel change across every gantt, which is why it is the maintainer's yes and not mine."
    },
    {
    "question": "If the shape above is settled and the threshold still needs raising, what should it be? The card suggests 'roughly 440'; that number is not derived, and no single constant is correct.",
    "options": [
    "Derived floor: 32 (px-4 both sides) + 160 (Start 80 + End 80, measured) + 28 (open-details slot w-6 plus its 4px margin, present whenever onTaskClick is live) + 32 (title-cell furniture: 8 effective spacer + 8 gap + 8 dot + 8 gap) = 252 of fixed cost, so threshold = 252 + MIN_TITLE, and MIN_TITLE = 160 (reusing TASK_LIST_MIN_W, the only existing 'narrower than this is useless' constant in the file) gives 412.",
    "The card's 440, which implies MIN_TITLE = 188.",
    "Make it a function rather than a constant, because the per-row indent is depth * 14 and is unbounded — no single threshold keeps a depth-4 row legible."
    ],
    "recommendation": "The derived 412, with the caveat stated in the third option. It is reproducible from the source (every term traced to a line) rather than estimated, and it reuses the file's own floor constant instead of inventing a second magic number. But note that at 412 the columns would be off at ALL THREE defaults (140 / 220 / 320) — the threshold would only ever be cleared by a user drag — which is exactly why the shape question above has to be answered first: a threshold that is never cleared by default is a removal of the feature by arithmetic, and the ruling explicitly did not ask for that."
    }
    ],
    "out_of_scope_findings": [
    "filed as #7224: plugin-gantt shows NO dates on a task-list row at any viewport between 640px and 1023px — the Start/End columns are gated off by the container-driven showStartEndColumns(220) and the date sublabel is gated off by the viewport-driven @media (min-width: 640px) rule, so the two gates leave an uncovered band on current main. Also holds at 1024px and above whenever a persisted drag left the panel narrower than 280. Same two functions as #7204; probably wants to be decided together with it."
    ]
    }


    Generated by Claude Code

  6. os-warren commented on Sep 1, 2026

    @os-warren
    CollaboratorAuthor

    ⛔ Option B CANNOT ship as I ruled it — my ruling was wrong, and the lane proved it

    domain:ui seat, session session_012wwHa4aaFybxXrfmfHioDM. No code shipped, no PR, and that is the correct outcome. Clearing pm:dispatched; re-grading to needs-user-decision.

    What I ruled, and why it was wrong

    I ruled option B a defect repair, not a design choice — on the argument that showStartEndColumns produces the outcome its own comment names as the thing it prevents. That reading of the gate still stands.

    What does not stand is the safety argument I built on top of it. B is only non-destructive if removing the two date columns de-duplicates the dates rather than deleting them, and the card's one sentence for that was:

    each row renders 8/26 → 9/2 as a sublabel directly under the title

    That sentence is conditional, and it is false at exactly the widths where this defect lives. The sublabel span carries gantt-sm-hidden, and GanttView injects its own style element containing @media (min-width: 640px) { .gantt-sm-hidden { display: none; } }.

    Measured in a real Chromium against the component's own emitted markup and its own injected style element:

    viewport sublabel display client width
    500 inline 72.9px
    639 inline 72.9px
    640 none 0
    800 / 1024 / 1440 / 1920 none 0

    So at every desktop width — including the 1440 and 1920 where the truncation was reported — the sublabel is already display: none. Raising the threshold as I ruled would delete a row's dates, not de-duplicate them. It would have traded a truncated name for a missing date and shipped looking like a fix.

    This is the third PM premise a lane has falsified this shift, and the first where the falsified premise was load-bearing for a ruling rather than a mechanism note. The order said a clean stop on a falsified premise is a full success. It is, and this is what one looks like.

    The measurement discipline is why this was caught rather than confirmed

    ⭐ jsdom would have given a false confirmation, in the reassuring direction. getComputedStyle(sublabel).display returned none at 500, 800 and 1440 alike, because jsdom applies the media rule irrespective of innerWidth. Reported as NOT MEASURED rather than as a result, and the decisive reading moved to a real browser. Had that been taken at face value, the conclusion would have been "the sublabel is never visible" — different from the truth, and it would have sent the fix somewhere else entirely.

    Two more things done right:

    • No ablation was invented. With nothing implemented there was nothing to ablate, and the report says so — "an ablation with nothing to ablate would be theatre" — rather than manufacturing a colour to fill the field.
    • The title width was derived from source, not copied from the card. 320 − 32 padding − 80 Start − 80 End − 28 open slot = 100px name cell; minus 32px of furniture = 68px, or 54px at depth 1. That lands within 1px of the card's measured 53 — an independent derivation agreeing with the report, which is worth more than adopting its table.

    A live defect found on the way: #7224

    At any viewport between 640 and 1023, the default task-list width is 220, so showStartEndColumns(220) is false and the columns are off — while the sublabel is already display: none from 640 up. A row shows no dates at all. That is on current main, today, and it also holds at 1024+ whenever a persisted drag left the panel under 280.

    The root is that two gates read two different widths — a ResizeObserver container measurement and a CSS viewport media query — and neither is the complement of the other. That is the actual defect behind this card, and it is a better description of the problem than the one the card (or I) had.

    Escalated, not decided

    The shape question goes to the maintainer with a recommendation rather than being ruled from this seat, because the leading option changes how every gantt row looks:

    Recommended: option A — render the sublabel exactly when the Start/End columns are not painted, then raise the threshold. One predicate instead of two that can drift apart; it also closes #7224 at its root. Cost, stated plainly: every desktop row gains a second line. That is a look-and-feel change across every gantt, which is why it needs a yes rather than my say-so.

    ⛔ Option C — ship B and let the dates leave the task list — is now clearly wrong, and the lane found the reason: the hover-tooltip fallback prints dates only when task.fields is empty, so the documented tooltipFields key removes that fallback too. It would also silently remove inline date editing, since the two type="date" inputs live in those very cells.

    On the threshold, if one is still needed: the derived floor is 412 (32 padding + 160 columns + 28 open slot + 32 furniture + 160 reusing TASK_LIST_MIN_W), not the card's suggested 440, which is not derived from anything. ⚠️ But note what 412 implies: the columns would be off at all three defaults (140 / 220 / 320), so the threshold would only ever be cleared by a manual drag — a removal of the feature by arithmetic, which is not what the ruling asked for. That is another reason the shape has to be settled before any number is.

    Carried to #5560.


    Generated by Claude Code

  7. removed their assignment
    on Sep 1, 2026
  8. os-project-manager commented on Sep 2, 2026

    @os-project-manager
    Collaborator

    Maintainer ruling recorded — Y: one container-driven predicate, and the task-list default sized from the container (ruled together with #7224)

    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 #5 in which this card and #7224 were items 2 and 3, presented as one ruling with the recommendation Y (fallback X). Verbatim reply: 「同意」.

    Ruled: Y. Three parts, one PR:

    1. One predicate, container-driven. The date sublabel renders exactly when the Start/End columns are not painted, and both read the same container width — the viewport media rule on gantt-sm-hidden goes; "the columns are painted" stops being the conjunction of a container test and a viewport test. This closes plugin-gantt: between 640px and 1023px the task-list rows carry NO dates at all — the Start/End columns are gated off and the date sublabel is display:none from 640px up #7224's 640–1023 band (and the dragged-narrow case at any width) at the root.
    2. The task-list default is sized from the container, a clamped share rather than the 320px cap: at 1440 and wider the panel takes enough of the extra width that a 25–40 character title is legible while the Start/End columns stay on; the exact share and clamp bounds are the dev's to derive and pin at 1280 / 1440 / 1920, with 320 as the floor.
    3. The Start/End threshold is the derived 412 (32 padding + 160 columns + 28 open-details slot + 32 title furniture + 160 minimum title, each term traced to source), not the estimated 440. With part 2 in place the desktop defaults clear it; below it the sublabel carries the dates, so inline date editing stays reachable at desktop defaults.

    Option X (predicate + threshold only) is the recorded fallback; option C (an authorable width key) is not taken — no new key. The gantt-block passthrough (an invented gantt.taskListWidth validates and is ignored) is noted as the same silent-inert class as #7199 and is not repaired here.

    Execution: domain:ui lane, one dispatch for both cards (same file, same two functions), branch may continue from the empty probe claude/issue-7204-gantt-tasklist-width. Layout only, no contract change ⇒ Clause-② no. Tests: the #7224 viewport table in a real browser (sublabel visible iff columns hidden, at 500 / 639 / 640 / 800 / 1024 / 1440 / 1920), and the title client width at the three desktop widths above the legibility floor.

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


    Generated by Claude Code

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

    @os-project-manager
    Collaborator

    Claim: domain:ui execution seat, PM session session_01EMrWaQw3XS5DxTHxp4yRyC, R2. Branch continues from the existing empty probe claude/issue-7204-gantt-tasklist-width. Dispatching to an os-dev subagent at opus. One dispatch covering #7204 AND #7224, per the ruling ("same file, same two functions").

    Comments re-read before claiming — all five, including the previous seat's own retraction. ⛔ No competing claim with a different session ID; the branch holds no commits.

    Ruling taken as given: Y (maintainer 2026-09-02, verbatim 「同意」, comment 5507936333). Clause-② no — layout only, no contract change, so no tier review is owed on delivery. ⛔ Option C (an authorable width key) is not taken; option X is the recorded fallback only.

    Serial constraints cleared

    resource status
    packages/plugin-gantt/src/GanttView.tsx free
    packages/plugin-gantt/src/ObjectGantt.tsx ⛔ HELD by PR #7391 — do not touch
    console eager-closure byte budget not expected to move; ⚠️ to be measured, not assumed

    ⭐ The disjointness was re-verified, not inherited. My own seat note flagged that the "disjoint from ObjectGantt.tsx" judgement predated the ceiling ruling. Measured against PR #7391's branch: it touches 6 files under packages/plugin-gantt/, and GanttView.tsx is not among them — zero hits, with a firing control (33 changed files total). So the two can run side by side.

    ⚠️ On bytes: plugin-gantt is not one of the three chunks in PER_CHUNK_GZIP_CEILINGS and did not appear in the gate's largest-eager-chunks list, so this should be byte-neutral to the framework ceiling that #7399 is blocked on. ⛔ That is a reason to expect a clean result, not a substitute for measuring one — I recorded a correction against myself this shift for calling a card byte-neutral on exactly that kind of inference. The dispatch requires the measurement.

    What the previous round bought, and why this dispatch is short

    This card was dispatched once before and came back with no code and a falsified premise — correctly. Option B's entire safety argument was one sentence ("each row renders the dates as a sublabel"), and it is false at every desktop width: the sublabel carries gantt-sm-hidden, and GanttView injects @media (min-width: 640px) { .gantt-sm-hidden { display: none } }. Raising the threshold as first ruled would have deleted a row's dates while looking like a fix.

    ⭐ That round also produced the trap this one must not fall into: jsdom gives a false confirmation here, in the reassuring direction — it applies the media rule irrespective of innerWidth, returning display: none at 500, 800 and 1440 alike. The decisive reading has to come from a real browser. It is carried into the dispatch as a named hazard.

    And it produced #7224 as a live defect on main: between 640 and 1023 the columns are gated off by a container test while the sublabel is gated off by a viewport test, so a row shows no dates at all. Two gates reading two different widths, neither the complement of the other — that is the actual root, and it is why the two cards ship together.


    Generated by Claude Code

  10. os-project-manager commented on Sep 3, 2026

    @os-project-manager
    Collaborator

    os-dev-report

    {
      "issue": 7204,
      "also_fixes": 7224,
      "status": "done",
      "branch": "claude/issue-7204-gantt-tasklist-width",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7420",
      "premise_still_valid": true,
      "summary": "All three parts of the 2026-09-02 ruling (option Y) landed in one PR on packages/plugin-gantt/src/GanttView.tsx. (1) The date sublabel now renders on exactly the complement of the Start/End columns, both from the same container-derived taskListWidth: the '@media (min-width: 640px) { .gantt-sm-hidden { display: none } }' rule and the class it hid are gone, and the two date cells lose their 'hidden sm:block' so 'the columns are painted' is no longer a conjunction of a container test and a viewport test. (2) taskListWidthForContainer takes 3/8 of the container clamped to [320, 560] from 1024 up, replacing the flat 320 cap. (3) showStartEndColumns compares against a derived START_END_COLUMNS_MIN_W = 32 + 160 + 28 + 32 + 160 = 412, each term a named constant traced to the markup that spends it. ObjectGantt.tsx (held by PR #7391) was not touched; content/docs/releases was not touched; no gate was weakened. A side effect worth naming: the header captions and the data cells now agree, where before a sub-640px viewport with a wide task list printed Start/End captions over cells that were not painted.",
      "tests": "All readings at commit b5f659e72 (branch head). RED-THEN-GREEN: origin/main's GanttView.tsx was restored over the committed fix; mutation proved on disk by blob hash (on-disk 8e7b4d8bc16a129ef58a3c58aba0b641d09be46c == origin/main blob, != HEAD blob e0cca573472e532521294bed7b067e3f49e1fffc) plus two anchored controls (gantt-sm-hidden lines back = 2, START_END_COLUMNS_MIN_W lines = 0); ablated run 'Test Files 1 failed (1) / Tests 14 failed (14)', VERDICT command-exit 1. Restore proved by STATE, not exit code: 'git diff HEAD' empty and on-disk blob == HEAD blob, under trap ... EXIT INT TERM with absolute paths. GREEN: same file 'Test Files 1 passed (1) / Tests 14 passed (14)'; whole plugin-gantt suite 'Test Files 59 passed (59) / Tests 471 passed (471)', VERDICT command-exit 0. All vitest runs went through os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=dev-7204-gantt. type-check: 'pnpm --filter @object-ui/plugin-gantt run type-check' exit 0 (script name echoed, so not a zero-match run), after 'pnpm --workspace-concurrency=2 --filter @object-ui/plugin-gantt^... run build' built the dependency closure (before that build it failed with TS2307 on every @object-ui import). lint: package lint exit 0, 0 errors; GanttView.tsx 19 warnings before and 19 after with an identical rule multiset, measured by linting the restored pre-fix blob, so the change adds none; the new test file adds 0. Gate family derived from the touched paths, all exit 0 with the exit captured before any pipe: check:control-bytes, check:i18n-keys, check:i18n-drift, check:i18n-dead-keys, check:vi-mock-specifiers, check:vi-mock-inherit, check:readme-exports, check:shell-escape-residue, check:sdui-registration-pins, check:element-data-source-declaration, check:eager-closure. NOTE on check:readme-exports: it first exited 1 with '5 self-import(s) could not be judged ... not on disk -- run pnpm build first' for @object-ui/cli and @object-ui/plugin-ai. That is an unbuilt-artifact reading, not a red gate; after building those two packages it exits 0. Reported rather than silently re-run. Control-byte self-scan over the diff and the PR body: no matches (grep exit 1), with a firing control on a file carrying a deliberate 0x01.",
      "browser_viewport_table": {
        "provenance": "REAL CHROMIUM, NOT jsdom. Chromium 1194 at /opt/pw-browsers/chromium (preinstalled; playwright install was never run), driven by Playwright against a Vite dev server rendering the real GanttView with the repo's own Tailwind build and the component's own injected style element. The measurement script was run from INSIDE the worktree (running it from outside gives ERR_MODULE_NOT_FOUND for 'playwright', which is unmeasured, not a red). Every viewport-dependent number below is a browser reading; nothing here was taken from jsdom, which applies the media rule irrespective of innerWidth and would have confirmed 'the sublabel is never visible'.",
        "legend": "pane = .gantt-task-list painted width; sub = date sublabel painted; cols = both Start/End cells painted",
        "container_equals_viewport": [
          { "viewport": 500,  "before": { "pane": 140, "sub": true,  "cols": false }, "after": { "pane": 140, "sub": true,  "cols": false } },
          { "viewport": 639,  "before": { "pane": 140, "sub": true,  "cols": false }, "after": { "pane": 140, "sub": true,  "cols": false } },
          { "viewport": 640,  "before": { "pane": 220, "sub": false, "cols": false }, "after": { "pane": 220, "sub": true,  "cols": false } },
          { "viewport": 800,  "before": { "pane": 220, "sub": false, "cols": false }, "after": { "pane": 220, "sub": true,  "cols": false } },
          { "viewport": 1024, "before": { "pane": 320, "sub": false, "cols": true  }, "after": { "pane": 384, "sub": true,  "cols": false } },
          { "viewport": 1280, "before": { "pane": 320, "sub": false, "cols": true  }, "after": { "pane": 480, "sub": false, "cols": true  } },
          { "viewport": 1440, "before": { "pane": 320, "sub": false, "cols": true  }, "after": { "pane": 540, "sub": false, "cols": true  } },
          { "viewport": 1920, "before": { "pane": 320, "sub": false, "cols": true  }, "after": { "pane": 560, "sub": false, "cols": true  } }
        ],
        "xor_violations": { "before": 2, "before_at": [640, 800], "after": 0 },
        "container_decoupled_from_viewport_after_fix": [
          { "viewport": 1440, "container": 800,  "pane": 220, "sub": true,  "cols": false },
          { "viewport": 1920, "container": 700,  "pane": 220, "sub": true,  "cols": false },
          { "viewport": 500,  "container": 700,  "pane": 220, "sub": true,  "cols": false },
          { "viewport": 800,  "container": 1440, "pane": 540, "sub": false, "cols": true  },
          { "viewport": 1024, "container": 1097, "pane": 411, "sub": true,  "cols": false },
          { "viewport": 1024, "container": 1098, "pane": 412, "sub": false, "cols": true  }
        ],
        "raw_computed_reads_before_fix": "getComputedStyle(sublabel).display was 'block' with width 91px at 500 and 639, and 'none' with width 0 at 640, 800, 1024, 1440 and 1920; window.matchMedia('(min-width: 640px)').matches flipped exactly at 640; each .gantt-sm-w20 cell measured 80.0px at every width from 640 up, which is where 80 + 80 = 160 comes from."
      },
      "part2_derivation": {
        "rule": "taskListWidthForContainer(w) = w < 640 ? 140 : w < 1024 ? 220 : round(clamp(w * 0.375, 320, 560))",
        "share": "3/8 = 0.375",
        "clamp": [320, 560],
        "why_share": "The title's space with the columns painted is pane - 1 (the pane's own border-r, inside its border-box width) - 32 (sm:px-4) - 160 (the two date cells) - 28 (open-details slot) - 32 (title furniture) = pane - 253. A 40-character title MEASURES 262px in the row's sm:text-sm font (Chromium), so 'legible at 1440' needs pane >= 515, i.e. a share of at least 515/1440 = 0.3576. 3/8 is the smallest clean fraction above it and puts 1440 at 540, leaving 287px.",
        "why_floor_320": "The ruling's floor and the previous fixed default, so no container gets a narrower pane than before.",
        "why_ceiling_560": "560 - 253 = 307px of title, which still clears the measured 262px after one level of the row's depth*14 indent (262 + 14 = 276). Past that, more pane width buys no legibility the ruling asked for and the timeline - the view's primary content - pays for it. The ceiling binds from a 1494px container up.",
        "measured_widths": [
          { "container": 1280, "pane": 480, "clears_412": true, "name_cell": 259, "title_space": 227, "fits_25_char_154px": true, "fits_40_char_262px": false },
          { "container": 1440, "pane": 540, "clears_412": true, "name_cell": 319, "title_space": 287, "fits_25_char_154px": true, "fits_40_char_262px": true },
          { "container": 1920, "pane": 560, "clears_412": true, "name_cell": 339, "title_space": 307, "fits_25_char_154px": true, "fits_40_char_262px": true }
        ],
        "412_is_cleared_at_all_three_desktop_widths": true,
        "legibility_floor": "Defined as the ruling's own lower bound, 25 characters, which measures 154px. Any pane that clears 412 leaves the title at least 412 - 253 = 159px, so the threshold itself guarantees the floor - the ruling's 160px minimum-title term is worth almost exactly 25 characters, measured.",
        "derivation_reproduces_the_cards": "At pane 320 the same arithmetic gives 67px for a flat leaf, 53px at depth 1 (67 - 14) and 63px for a summary row (its w-4 toggle costs 4 more than a leaf's w-3 spacer). Chromium read 67, 53 and 63. #7204 reported 53. No term is off by a pixel."
      },
      "byte_reading": {
        "gate": "check:eager-closure exit 0",
        "per_chunk": {
          "framework": "511.5 KB / 511.7 KB ceiling, headroom 0.2 KB",
          "vendor-objectstack": "926.1 KB / 944.3 KB ceiling, headroom 18.2 KB",
          "ui-components": "387.3 KB / 389.6 KB ceiling, headroom 2.3 KB",
          "aggregate": "3178.3 KB / 3191.4 KB budget, headroom 13.1 KB"
        },
        "measured_delta_vs_pre_fix_baseline": {
          "eagerGzipBytes": { "branch": 3254545, "baseline": 3254545, "delta": 0 },
          "eagerRawBytes": { "branch": 11175307, "baseline": 11175307, "delta": 0 },
          "eagerChunkCount": { "branch": 48, "baseline": 48, "delta": 0 },
          "per_chunk_gzip_deltas": "none - every one of the 48 eager chunks identical, no chunk on one side only"
        },
        "method": "The console was built twice through turbo (both builds exited 0 - a failed ablation build reads a stale artifact and reports a stripped input as free), once on the branch and once with GanttView.tsx restored to origin/main under a trap-guarded ablation with the mutation proved on disk by blob hash and the restore proved by git diff HEAD being empty. No ceiling was raised and no copy was trimmed.",
        "firing_control": "The zero is a reading, not a stale artifact: the entry chunk hash DIFFERS between the two builds (index-DUp49asL.js on the branch vs index-XRCMG0Ou.js on the baseline), so the two builds genuinely produced different bundles and the eager closure still weighed the same. No gantt chunk appears in the eager set at all."
      },
      "deviations": [
        "The ruling's 412 does not count the pane's own 1px border-r, which sits inside its inline border-box width, so the true break-even is 413. 412 is implemented AS RULED; the effect is that at exactly pane 412 the title gets 159px rather than the 160 the term names. Flagged, not silently corrected.",
        "New behaviour the ruling did not describe: between a 1024px and a 1097px container the Start/End columns are now OFF where they used to be on, and the dates ride the sublabel (0.375 * 1024 = 384 < 412). It is a net gain - the title goes 67px to 291px and the dates stay visible - but #7204's table never visits that band, so it is called out and pinned in the tests at 1097/1098.",
        "Below a 640px viewport the 412 terms are cheaper than 412 assumes: .gantt-sm-w20 (80px) and the open-details slot (hidden sm:block) only apply from 640px up, so the columns cost 128 and the slot 0. The threshold is conservative there, never permissive. Making those unconditional would be a narrow-layout change nobody asked for; left alone.",
        "depth * 14 is unbounded, so no single default keeps a deeply nested row legible. Stated as a known limit in the source comment, the test file and the PR, per the dispatch - not chased.",
        "The three sub-1024 stepped defaults (140 / 220) were left unchanged; the ruling only asked for the container-sized default at 1440 and wider, with 320 as the floor."
      ],
      "mcp_calls": "5 - issue_read(#7204), create_pull_request(#7420), issue_write(create #7421), and the two add_issue_comment calls carrying this report (#7204 and #7224). Every other GitHub read (the ruling comments, #7224 and its comments, the 290 open issues for the duplicate check, the PR body read-back, the label read-back on #7421) went through the container's unauthenticated REST channel, probed green at the start of the run.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #7421: `taskListWidth_LEGACY_REMOVED` in GanttView.tsx is a declared-and-never-read `const null` left behind by a finished refactor - one hit repo-wide, its own declaration, with a comment that reads like a live to-do. Unassigned, label `finding` only.",
        "NOT filed, already open as #7228: `columnWidthForContainer` branches on container width and returns 110 from all three arms. Found adjacent to my edit; the duplicate check hit it, so no second card was created.",
        "NOT filed, covered by the ruling: the `gantt`-block passthrough (an invented `gantt.taskListWidth` validates and is silently ignored, same silent-inert class as #7199) is recorded on #7204 and explicitly out of scope here."
      ]
    }

    Generated by Claude Code

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

    @os-project-manager
    Collaborator

    ACCEPT — all three ruled parts delivered, both cards closed. One deviation accepted with the alternative recorded.

    domain:ui execution seat, PM session session_01EMrWaQw3XS5DxTHxp4yRyC, R2. PR #7420 at b5f659e72. ⛔ Not yet enqueued — 23 checks green including Bundle Analysis, 4 test shards still running. Enqueue requires every check green.

    #7224 is measured closed, not argued closed

    The defect was that two gates read two different widths and neither was the complement of the other. The real-browser table gives it as one number:

    XOR violations (sublabel visible ⇎ columns painted)
    before 2 — at viewports 640 and 800
    after 0

    And the two gates are now provably one predicate: with the container decoupled from the viewport, a 1440px viewport with an 800px container renders the sublabel, and an 800px viewport with a 1440px container paints the columns. Before, the viewport decided one and the container the other. The 412 boundary is pinned at container 1097 → sublabel, 1098 → columns, which is the threshold itself under test rather than a nearby proxy.

    ⭐ Provenance is stated where it matters: real Chromium 1194, driven against a Vite dev server with the repo's own Tailwind build and the component's own injected style element, run from inside the worktree. The dispatch named jsdom as a hazard that would have confirmed "the sublabel is never visible" at every width; the report says explicitly that nothing here came from it.

    The derivation reproduces the card to the pixel

    At pane 320 the same arithmetic yields 67px for a flat leaf, 53px at depth 1, 63px for a summary row (its w-4 toggle costs 4 more than a leaf's w-3 spacer). Chromium read 67, 53, 63. #7204 reported 53. "No term is off by a pixel." An independent derivation agreeing with the original report is worth more than adopting its table — which is what the dispatch asked for.

    The share is derived, not chosen: a 40-character title measures 262px in the row's font, so legibility at 1440 needs pane ≥ 515, i.e. share ≥ 0.3576; 3/8 is the smallest clean fraction above it. The ceiling 560 is where more pane buys no legibility the ruling asked for and the timeline — the view's primary content — starts paying.

    ⚠️ The deviation that needed a decision, and my ruling on it

    Between container 1024 and 1097 the Start/End columns are now OFF where they used to be ON (0.375 × 1024 = 384 < 412). The dev flagged and pinned it rather than letting it pass.

    This matters because ruling Y part 3 asserted a property: "With part 2 in place the desktop defaults clear it; below it the sublabel carries the dates, so inline date editing stays reachable at desktop defaults." In that band the defaults do not clear 412, so inline date editing — the two type="date" inputs live in those cells — is not reachable there. The ruling's stated premise does not hold across the whole band it speaks about.

    ⭐ I am accepting it, and here is the reasoning rather than a verdict. At container 1024 before this PR: columns on, title 67px — that is precisely the defect #7204 was filed about, truncating names to ~7 characters. After: title 291px, dates still on screen via the sublabel, editing moved off that band. So this is not a regression against the card's intent; it is the card's intent applied at a width the ruling's own table never visited. And the share was explicitly delegated — "the exact share and clamp bounds are the dev's to derive and pin" — so choosing 0.375 is inside the delegation, not outside it.

    The alternative, recorded so it is a one-line change if the maintainer prefers the asserted property: the smallest share that makes 1024 clear 412 is 412/1024 = 0.4024, so 13/32 = 0.40625 would give 1024 → 416, 1280 → 520, 1440 → 560 (clamped), 1920 → 560. That preserves "editing reachable at every desktop default" and still clears the 515 legibility floor. Its cost is ~40px of timeline at 1280 and ~20px at 1440. ⛔ I am not making that change on my own say-so, because it trades away primary-content width the dev deliberately protected — but it is a single constant, and the pins at 1097/1098 already localise it.

    The other deviations, all correctly handled

    • ⭐ 412 vs 413: the ruled 412 does not count the pane's own 1px border-r, which sits inside its border-box width, so the true break-even is 413 and at exactly pane 412 the title gets 159px rather than the 160 the term names. Implemented as ruled and flagged, not silently corrected — exactly right. A dev quietly "fixing" a ruled number is worse than a 1px gap.
    • Below 640px the 412 terms are cheaper than 412 assumes (gantt-sm-w20 and the open-details slot only apply from 640 up), so the threshold is conservative there, never permissive. Making them unconditional would be a narrow-layout change nobody asked for; left alone. Correct.
    • depth * 14 is unbounded, so no single default keeps a deeply nested row legible — stated as a known limit in source, tests and PR, per the dispatch. ⛔ Not chased.
    • A side effect worth having: the header captions and the data cells now agree. Before, a sub-640 viewport with a wide task list printed Start/End captions over cells that were not painted.

    The byte reading, and why its control is the good part

    check:eager-closure exit 0 — framework 511.5 / 511.7 KB, headroom 0.2 KB, unchanged. Measured, not assumed: two full console builds, branch vs. GanttView.tsx restored to origin/main, both exiting 0.

    eagerGzipBytes   branch 3254545  baseline 3254545  delta 0
    eagerChunkCount  48 / 48, every one of the 48 identical
    

    ⭐ And the zero has a firing control: the entry chunk hash differs between the two builds (index-DUp49asL.js vs index-XRCMG0Ou.js). So the two builds genuinely produced different bundles and the eager closure still weighed exactly the same — which rules out the failure mode a delta of 0 usually hides, reading the same artifact twice. plugin-gantt's own dist did grow (166.93 → 167.03 KB) and no gantt chunk appears in the eager set at all.

    ⇒ This is the measurement that also validates passing over #7178: the same ~100 bytes lands harmlessly in a lazy plugin and would have landed in the contested framework chunk from packages/core.

    Also correct

    check:readme-exports first exited 1 with "not on disk — run pnpm build first" and was reported as an unbuilt-artifact reading, not a red gate, then cleared by building. Second dev today to get that distinction right. ObjectGantt.tsx (held by PR #7391) untouched, as required.

    Filed: #7421 — taskListWidth_LEGACY_REMOVED, a declared-and-never-read const null left by a finished refactor, with a comment that reads like a live to-do. ⭐ And a duplicate was correctly not filed for columnWidthForContainer returning 110 from all three arms — the dedup hit #7228, which is already open.


    Generated by Claude Code

  12. os-project-manager commented on Sep 3, 2026

    @os-project-manager
    Collaborator

    LANDED — verified BY CONTENT on origin/main with live controls, not by the PR's head SHA

    domain:ui execution seat, PM session session_01EMrWaQw3XS5DxTHxp4yRyC, R2. PR #7420 merged through the merge queue at 00:56:06Z; origin/main is now bf244f400. pm:dispatched stripped from both #7204 and #7224 in the same stroke (bug, priority:p2, domain:ui retained — belonging, not state).

    The probes, each with a control that must fire

    probe reading on origin/main control
    START_END_COLUMNS_MIN_W (the derived 412) present — declared at 153, read at 173 file length 5326 lines ⇒ the probe can see the file
    taskListWidthForContainer (part 2) present — defined at 137, called at 989 as above
    gantt-sm-hidden — the class the viewport rule hid 0 gantt-sm-w20 = 6 ⇒ the zero is a reading
    ObjectGantt.tsx (held by PR #7391) last touched by e75f4c986 (#7338), not by this landing —

    ⭐ The reading that would have been wrong from a count

    min-width: 640px still returns 2 occurrences, which read as a bare number says the media rule survived — the opposite of the truth. Reading the lines:

    • line 166 is a comment recording why the old design was wrong: "Previously the columns were gated by this test AND a sm: viewport test while the sublabel was gated by a (min-width: 640px) media rule alone. Two gates on two different widths are not complements…"
    • line 3346 is a different media block — .gantt-sm-h50 { height: 50px } and .gantt-sm-w20 { width: 80px }, row height and date-cell width. ⚠️ That second one must still exist: the 80 + 80 = 160 term in the derived 412 is .gantt-sm-w20.

    ⇒ The rule that gated date visibility is gone (its class is at zero); what remains is documentation of the old design and unrelated sizing. This is the fifth time this shift a bare count would have produced the opposite conclusion — recorded again because it keeps arriving in new clothes.

    What landed

    #7204: the task list is no longer capped at 320px — taskListWidthForContainer takes 3/8 of the container clamped to [320, 560] from 1024 up, so a 40-character title (measured at 262px in Chromium) fits at 1440 and wider while the Start/End columns stay on. The threshold is the derived 412, each term traced to the markup that spends it.

    #7224: closed at the root. The date sublabel and the Start/End columns now read the same container width, so they are complements by construction — XOR violations went 2 → 0, and the boundary is pinned at container 1097 → sublabel / 1098 → columns rather than at a nearby proxy.

    Carried forward

    • ⚠️ Between container 1024 and 1097 the columns are now off where they used to be on, so inline date editing is not reachable in that band — the ruling's part 3 asserted it would be. Accepted with reasoning on the ACCEPT comment (5518472054): at 1024 before this PR the columns were on and the title was 67px, which is the defect this card was filed about. The one-constant alternative (share 13/32) is recorded there if the maintainer prefers the asserted property.
    • ⚠️ 412 vs 413: the ruled 412 does not count the pane's own 1px border-r, so at exactly pane 412 the title gets 159px rather than the named 160. Implemented as ruled and flagged rather than silently corrected.
    • finding(plugin-gantt): taskListWidth_LEGACY_REMOVED is a declared-and-never-read const null left behind by a finished refactor #7421 filed — taskListWidth_LEGACY_REMOVED, a declared-and-never-read const null with a comment that reads like a live to-do. Unassigned, finding only; grading is triage's.

    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

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions