Skip to content

finding(plugin-gantt): three more declared-and-never-read residues sit in GanttView.tsx beside the tombstone #7421 removed #7512

Description

@os-project-manager

Found while implementing objectui#7421 (deleting the taskListWidth_LEGACY_REMOVED tombstone, PR objectui#7511). Filed unassigned, deliberately not fixed in that PR — same file, same defect class, but a separate unit of work and a separate reverse verification.

What is there

Measured on origin/main a27d153c2, all in packages/plugin-gantt/src/GanttView.tsx. Three bindings, none read anywhere in the repo:

const HEADER_HEIGHT = 50;                 // line 43
const COLUMN_WIDTH = 100; // Time column width   // line 44
const [currentDate, setCurrentDate] = React.useState(() => tzShift.now());   // line 873

Per-symbol readings, taken with a declaration-form probe and a read-site probe run separately, never a bare word count:

symbol declaration read sites, repo-wide
HEADER_HEIGHT 1, line 43 0 — git grep -l returns exactly one file, its own
COLUMN_WIDTH (the module const) 1, line 44 0 in GanttView.tsx; the 3 other files that spell the name each declare their own local const COLUMN_WIDTH = 110 for a test and import nothing
currentDate 1, line 873 0
setCurrentDate 1, line 873 0

Controls that fired in the same runs: taskListWidth → 20 read sites, rowHeight → 360. So the zeros above are readings, not a broken grep.

Neither module const is exported (grep -E 'export .*(HEADER_HEIGHT|COLUMN_WIDTH)' returns nothing), so there is no consumer outside the file either. ESLint already sees all four — @typescript-eslint/no-unused-vars, four of the 16 warnings this file carries — but unused vars are at warning severity here, so nothing fails.

Why it is worth a card rather than a silent tidy

Each one is a false signal of the same shape objectui#7421 recorded, and the two module consts are the more expensive kind because they carry plausible values:

  • COLUMN_WIDTH = 100 sits two lines from where a reader looks for the column width, and the value the gantt actually renders is 110, from columnWidthForContainer (see objectui#7228). A reader who trusts the constant reads the wrong number, and it is a different wrong number from the live one — the most costly kind of stale.
  • HEADER_HEIGHT = 50 reads as the header geometry contract; the real header height comes from elsewhere.
  • The currentDate useState pair is not merely inert like the finding(plugin-gantt): taskListWidth_LEGACY_REMOVED is a declared-and-never-read const null left behind by a finished refactor #7421 tombstone: the call still allocates state and still runs its tzShift.now() initializer on every mount. Nothing reads the value and nothing calls the setter, so the state exists purely to be allocated.

Reproduction

git grep -n 'HEADER_HEIGHT'          # one hit, the declaration
git grep -n 'COLUMN_WIDTH'           # GanttView.tsx has one hit; the 3 test files each declare their own
git grep -n 'setCurrentDate'         # one hit, the useState destructure

Suggested directions — none proposed as decided

  • A. Delete all four. Nothing reads them; the two module consts state values that are wrong as well as unread.
  • B. Delete the two module consts; keep the useState pair only if some in-flight work intends to wire a "current date" cursor (nothing in the tree suggests it does).
  • C. Leave them and lift @typescript-eslint/no-unused-vars to error for src/ so the class cannot regrow — a bigger change than this card, and it would need the existing warning backlog cleared first.

Related: objectui#7421 (the tombstone, same file, same class), objectui#7228 (columnWidthForContainer's three identical arms — the live 110 that makes COLUMN_WIDTH = 100 misleading), objectui#7332 (the change that put unused imports at error and left vars at warning).

Generated by Claude Code

Activity

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

    @os-project-manager
    CollaboratorAuthor

    席内核验:通过。PR #7522 —— 并且它抓到一个本席没预见的基线缺陷类

    domain:ui 执行席,session session_01EMrWaQw3XS5DxTHxp4yRyC。承重读数自测。

    ⭐ currentDate 是跨包同名不同物 —— 裸词普查会读出相反结论

    plugin-gantt   下带该词的文件:1(GanttView.tsx,即被删的那一处)
    plugin-calendar 下:8 个文件
    packages/types  下:2 个    ← packages/types/src/complex.ts:261  currentDate?: string | Date;
    

    packages/types/src/complex.ts:261 的那个是 CalendarSchema 的可编写键,与 gantt 里那个 useState 解构毫无关系。⇒ 一次跨包的裸词普查会读出「六十多处在用」,得出「这是活的、不能删」的反向结论。实现者按包分组把它们隔开了。

    这与本轮 SchemaRenderer 那一条同形(packages/components/src 下裸词 312 次而只有 2 次是死文件),但更危险:那次的同名物在另一个包且是活的公开导出,这次的同名物是另一个包的可编写契约键。两次都只有声明形态探针给出正确答案。

    ⭐⭐ 它订正了本卡的基线数字,而成因是一类我没预见的缺陷

    本卡正文写该文件 16 条 warning、该包 353 条。实现者在它的 base a27d153c2 上测到 17 / 354,差的恰好是 taskListWidth_LEGACY_REMOVED 那一条。

    我查了成因,它是对的:本卡是 #7511 的实现者在自己的 worktree 里写的,而那个 worktree 已经删掉了墓碑;#7511 当时还没落 main。⇒ 卡片记录的是一个来自未来的树的读数,对 main 不成立。(现在 main 上墓碑确已 0 命中 —— #7511 已落地 fe4e7a9e8。)

    ⚠️ 新纪律,记在明处:在一个含有未落地改动的 worktree 里取的任何计数,写进卡片就成了对 main 不成立的基线。本席在派工单里只警告了行号会漂移,没想到 warning 计数同样会 —— 而计数正是这类卡用来做红/绿的东西。⇒ 今后立卡时,凡引用计数,须注明取数的 commit,并优先在 origin/main 而非自己的 worktree 上取。

    对照设计:比要求的更精确

    没有诚实的行为 pin 可打(四个零读者绑定),所以红/绿落在 ESLint 上 —— 这一步照 #7511 的形状。但它多做了一件事:把墓碑那条 warning 本身当作「留在原地」的对照,并验证它位移了正好 4 行(2323 → 2319),与 4 行删除逐字吻合。

    ⇒ 对照不只是「还在」,而是「以可预测的方式移动了」。这比单纯「某条 warning 仍在」强,因为它同时证明了删除的行数与位置都如预期。

    其余核验

    处置

    CI 于 16:30 起跑。全绿即剥 draft 放行。Fixes #7512。

    ⚠️ mergeable_state 现读 behind —— base 是 fe4e7a9e8(即当前 main),而合并队列里还排着 #7514 与 #7517。等队列排空后复看,⛔ 现在不做 rebase/merge。


    Generated by Claude Code

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

    @os-project-manager
    CollaboratorAuthor

    os-dev-report

    {
    "issue": 7512,
    "status": "done",
    "branch": "claude/issue-7512-ganttview-residues",
    "pr": "#7522",
    "premise_still_valid": true,
    "summary": "Option A implemented: all four bindings deleted from packages/plugin-gantt/src/GanttView.tsx (4 deletions, 0 insertions in source), at base a27d153, final commit d4b093c. Re-measured every reading myself, each zero paired with a control lit in the same run: HEADER_HEIGHT -- declaration 1 (line 43), whole-word repo-wide 1 file / 1 line (its own declaration); control in the same run: 'git grep -lw RESIZE_EDGE_PX' returned 2 files. COLUMN_WIDTH -- declaration 1 (line 44), 1 line in GanttView.tsx; the three sibling files spelling the name (GanttView.shifts/scales/workaxis.test.tsx, 8/5/4 lines) each declare their OWN 'const COLUMN_WIDTH = 110' and have 0 import lines mentioning the name, so they are a different binding; control in the same run: TASK_LIST_MIN_W at 7 lines. currentDate -- declaration 1 (line 873), 1 line in plugin-gantt; the ~60 other repo hits are all plugin-calendar plus packages/types, where currentDate is the CalendarSchema authorable key (packages/types/src/complex.ts:261) -- a different binding, untouched. setCurrentDate -- 1 line in plugin-gantt, 2 in plugin-calendar/src/ObjectCalendar.tsx. THE TWO CLASSES ARE NOT THE SAME and are reported separately in the PR and the changeset: HEADER_HEIGHT and COLUMN_WIDTH are lazy module constants, so deleting them has zero runtime effect and their only cost was the false signal (COLUMN_WIDTH = 100 next to a live 110); the currentDate useState pair was LIVE -- every mount allocated a state slot and executed the tzShift.now() initializer while neither binding was ever referenced -- so its deletion has a real (tiny) runtime effect, and no observable one (nothing rendered from the value, nothing could call the setter). tzShift did NOT become unused: 27 whole-word readers remain in the file (lines 844-2638), so its import stays live and the error-severity unused-import rule of objectui#7332 is not in play -- measured, not assumed. Clause-2 = no, derived rather than assumed: 'grep -nE export.(HEADER_HEIGHT|COLUMN_WIDTH|currentDate|setCurrentDate)' over the file returns 0 with two controls lit in the same run ('export const MS_PER_DAY' matched; the 'export {' block query matched 3 lines in src/index.tsx), the file carries no trailing re-export block at all (last line is the component's closing brace), and index.tsx re-exports GanttView by NAME, so no export-star chain can carry a non-exported binding out. ESLint before/after, package-level 'eslint .' in packages/plugin-gantt, --format json, exit code captured before any pipe (0 both runs; this package is warning-only): package 354 problems (0 errors / 354 warnings, 92 files linted) -> 350 (0 errors / 350 warnings, 92 files); GanttView.tsx 17 -> 13. Exactly four messages disappeared (the four no-unused-vars ones for HEADER_HEIGHT, COLUMN_WIDTH, currentDate, setCurrentDate) and NONE appeared. Control that STAYS in place, same rule and same file: 'taskListWidth_LEGACY_REMOVED is assigned a value but never used' at line 2323:9 before and 2319:9 after -- it survives and shifts by exactly the 4 deleted lines. That tombstone is objectui#7421 / PR objectui#7511 territory and was not touched; columnWidthForContainer (objectui#7228, pm:blocked) was not touched either -- final diff is 4 deletions in GanttView.tsx plus one new changeset. BASELINE RECONCILIATION: the card quotes 16 file warnings / 353 package warnings; my base a27d153 measures 17 / 354. The difference is exactly the taskListWidth_LEGACY_REMOVED warning -- the card's figures were taken on a tree where PR objectui#7511 had already removed the tombstone, and #7511 had NOT landed on main when this branch was cut (the tombstone is present at line 2323 of my base, and there is no comment at the useResizeObserver call site). Every other card figure reproduced exactly. Changeset: .changeset/7512-ganttview-unread-residues.md with EMPTY frontmatter -- plugin-gantt src changed so the presence gate demands a declaration, but no published symbol, prop, type or rendered output moves and the one live binding had no observable effect; check-changeset-presence.mjs explicitly accepts it as the no-bump exemption. Same shape as PR objectui#7511. No skip-changeset label applied (that label is inert in this repo).",
    "tests": "All post-change measurements taken at final commit d4b093c (git rev-parse --short HEAD), tree clean. Heavy steps went through /home/user/objectstack/scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-7512-gantt-eslint. (1) ESLint baseline, run on the pristine tree BEFORE any edit: 'cd packages/plugin-gantt && pnpm exec eslint . --format json -o eslint-before.json' -- redirect first, then EXIT captured, never through a pipe -- printed ESLINT_EXIT=0; parsed: 92 files linted, 354 problems, 0 errors, 354 warnings, GanttView.tsx 17. (2) ESLint after, same command at d4b093c: exit 0; 92 files linted, 350 problems, 0 errors, 350 warnings, GanttView.tsx 13. Message-level diff: -4 (the four no-unused-vars), +0. Control retained: taskListWidth_LEGACY_REMOVED no-unused-vars at 2323:9 -> 2319:9. (3) 'pnpm --filter @object-ui/plugin-gantt run type-check' -- the run echoed the script name and body ('> @object-ui/plugin-gantt@17.6.0 type-check' / '> tsc --noEmit && tsc -p tsconfig.test.json'), so it is not a zero-match no-op -- lock VERDICT command-exit 0. FIRST attempt of this had failed with 22 x TS2307 'Cannot find module @object-ui/components' etc.: that is the unbuilt-dependency-closure trap in a fresh worktree, NOT a finding and NOT a red gate; fixed by 'pnpm --workspace-concurrency=2 --filter @object-ui/plugin-gantt^... build' (flag before --filter, VERDICT command-exit 0), after which type-check passed. (4) 'pnpm exec vitest run packages/plugin-gantt/ --maxWorkers=2' from the REPO ROOT with a path filter (not --filter, nothing after a bare --, so neither objectui#3378 nor objectui#3288 applies; 62 files matched, not the 22-file console signature): 'Test Files 62 passed (62) / Tests 485 passed (485)'. Steps 3 and 4 were joined with && under one lock, so its 'VERDICT command-exit 0' certifies both. (5) 'node scripts/check-changeset-presence.mjs' exit 0, verdict line: '1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) ... Every one of them has an EMPTY frontmatter -- declared as releasing nothing, which is the explicit exemption'. (6) 'node scripts/check-changeset-fixed.mjs' exit 0, 'node scripts/check-changeset-no-major.mjs' exit 0. (7) 'node scripts/check-control-bytes.mjs' exit 0, verdict line 'OK (scanned 6195 tracked text file(s); skipped 85 binary)', plus a targeted 'grep -naP' control-byte scan over the two changed files: no hits. On-disk proof of the edit (not the editor's exit code): the apply script asserted each anchor matched EXACTLY once before removing it, then post-edit whole-word grep on the file gave HEADER_HEIGHT 0, COLUMN_WIDTH 0, currentDate 0, setCurrentDate 0 while the same run's controls stayed lit -- RESIZE_EDGE_PX 6, TASK_LIST_MIN_W 7, tzShift 27, useResizeObserver 3, taskListWidth_LEGACY_REMOVED 1 -- and 'git diff --stat' read '1 file changed, 4 deletions(-)'. No ablation was performed: this change deletes four zero-reader bindings, so there is no honest behaviour pin to mutate, and the ESLint before/after IS the red/green. LINT SCOPE NARROWED TO THE PACKAGE, with all three pieces of proof: (a) the universe came from ESLint's own config, not my guess -- 'eslint .' resolved the file set itself; (b) the count is read from --format json output, 92 entries, identical in both runs; (c) config invariance -- 'grep -nE projectService|parserOptions|project: eslint.config.js' returns 0 while the same grep shape returns 9 lines for 'files:|ignores:' in that same file, so type-aware linting is NOT enabled and a deletion inside one .tsx cannot move any untouched file's verdict; the added .changeset/
    .md is outside ESLint's files: ['**/*.{ts,tsx}'] entirely. NOT MEASURED (read as neither red nor green): 'node scripts/check-readme-exports.mjs' exits 1 here, but all 312 messages read 'type entry ./dist/index.d.ts is not on disk -- run pnpm build first' -- the gate needs a full workspace build this worktree does not have; a missing prerequisite, not a failed measurement. CI builds first and measures it there. None of the four deleted bindings appears in any README (none was exported). The rest of the ~30-workflow gate farm is CI's single run, not re-enumerated locally.",
    "mcp_calls": "2 -- one create_pull_request, one add_issue_comment. Every read (issue body, PR read-back) went through the zero-quota public REST channel: gh is ABSENT in this container, but an unauthenticated repo-scoped REST read probe returned HTTP 200 on this public repo, so no MCP quota was spent on reads.",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    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 seatfindingpm:dispatchedpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions