Skip to content

fix(timeline): honor startDateField when rendering a ListView timeline #3129

Closed

Description

@os-zhuang

Reproduction

  1. Run HotCRM with the published console and open crm_campaign/view/campaign_timeline.
  2. The view declares timeline.startDateField: "start_date" and timeline.endDateField: "end_date"; every campaign has both values.
  3. The timeline renders all seven records under No date.

The same start_date / end_date fields render correctly in the Calendar and Gantt views, so this is isolated to the timeline path.

Expected

The timeline should bucket records by their configured start date and render the corresponding date label.

Context

Found while recapturing the HotCRM marketing screenshot library after objectstack-ai/hotcrm#581. A metadata-only attempt to include the two date fields in the timeline view projection did not change the published-console behavior.

Activity

  1. self-assigned this
    on Aug 1, 2026
  2. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    分诊:pm:queue。 ⛔ 本仓不打 domain:*(objectui 整仓座位)。

    判级理由

    具体缺陷,复现明确(HotCRM crm_campaign/view/campaign_timeline,视图已声明 timeline.startDateField: "start_date" / endDateField: "end_date" 且七条记录都有值,timeline 全部落在 No date 下)。隔离证据强:同样两个字段在 Calendar 与 Gantt 视图渲染正常 ⇒ 缺陷被钉在 timeline 这一条渲染路径上,不是元数据或数据问题。

    立单者还排除了一个假说:纯元数据的补救(把两个日期字段加进 timeline 视图投影)不改变已发布 console 的行为 —— 这条阴性结果同样有价值,它把修复面从元数据推到了渲染器代码。

    一句话验收标准:timeline 按 startDateField 分桶并渲染对应日期标签。

    落点锚定理由(⛔ 不按标题词汇猜)

    startDateField 在 origin/main 的消费方集中在本仓 packages/app-shell(views/InterfaceListPage.tsx、views/CreateViewDialog.tsx、providers/MetadataProvider 一族)⇒ 修复落在本仓的 timeline 渲染分支,不跨仓、不触 packages/spec(spec 已正确声明这两个键,缺陷在消费侧)⇒ 不走跨座位转移。

    过时前提检查

    git -C /home/user/objectui fetch origin main 已跑,startDateField 消费方健在。⚠️ 本单立于 2026-08-01,距今 5 天 ⇒ 派发前请在当前 main + 当前已发布 console 上重跑一次复现;正文的读数取自「已发布的 console」,而本仓与 objectstack 的 console pin 之间存在刷新滞后,重跑时请注明用的是哪一侧。

    查重

    三仓搜 timeline / startDateField / No date 分桶:无重复单。上游 objectstack-ai/hotcrm#581 是发现场景(重拍营销截图库时撞见),不是同题单。

    ⚠️ 给接手方的一条

    Calendar / Gantt 已经把同一对字段读对了 —— 先读那两条路径怎么解析,再改 timeline。三条视图各自解析同一组视图键正是「一个词汇在多处各自实现」的形状;若发现三者确实各写了一份,请在修复中把 timeline 接到已有的那份上,并在单上记一句,⛔ 不要写第四份。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    Release board: target:v17 applied — class ①, the ListView timeline ignores the view's declared timeline.startDateField and buckets every record under "No date" even though the same fields render correctly in Calendar and Gantt, so a declared, published view type does not work at all. One-time stock pass per objectstack#6904 (maintainer directive 2026-08-09).


    Generated by Claude Code

  4. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    Claim-status inquiry (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, 2026-08-10): this card is assigned to os-zhuang but the thread has no claim comment, and git ls-remote shows no branch matching issue-3129 and no PR. It is a class-① target:v17 release blocker, so it cannot sit in an unreadable claim state.

    • If you are actively on it: please post the standard claim comment (session ID + branch) so the claim is legible.
    • If the assignment is a leftover (e.g. from the triage pass rather than a claim): please drop the assignee, and the objectui seat will dispatch it in the current v17 push.

    Per the stale-claim protocol, if there is no response within ~24h the seat will treat the claim as released and dispatch (this note starts the window; a live branch with commits always stops it).


    Generated by Claude Code

  5. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    🕒 pm:on-hold (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, 2026-08-10)


    Generated by Claude Code

  6. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    Hold lifted + claim (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, 2026-08-10).

    Provenance for the state change and the assignee takeover — maintainer, 2026-08-10, in the seat session chat, verbatim: 「objectui#3129(timeline 不认 startDateField)和 #3055(记录页审批入口)两张 v17 class-① 卡挂在 os-zhuang 名下,这两个任务你接着处理吧,os-zhuang本人已经确认过了」. This supersedes the same-day 「先暂停不处理」 hold (recorded above) and resolves the cross-account assignment: per the maintainer, os-zhuang has personally confirmed releasing the claim. Assignee moved os-zhuang → this seat on that basis.

    Claim: PM loop round 3 (v17 board, class ①; direct maintainer instruction — dispatched above the five-concurrent soft target)
    Session: session_017Qqyix2QcnpUC9XeYVDzx3
    Branch: claude/issue-3129-timeline-startdatefield
    Worktree: objectui-issue-3129
    File surface: the ListView timeline rendering branch in app-shell (locate via startDateField consumers — InterfaceListPage.tsx / ObjectView.tsx face) + tests. ⛔ Region-exclusive vs in-flight siblings: not RecordDetailView.tsx / useRecordApprovals* (#3055), not the create-dialog/form path (#4047), not providers/HTTP client (#4042), not containers.tsx (#4027), not packages/cli / packages/fields.
    Container & model: M, mode:subagent, model: opus
    Serial constraints cleared: no open claim or PR on the timeline path; the 2026-08-06 triage warning stands — Calendar and Gantt already parse the same keys correctly, so the fix must reuse their resolution rather than add a fourth copy.

    Premise re-verified at dispatch: startDateField consumers alive in app-shell on origin/main; no commits touched the timeline face since the triage re-verification.


    Generated by Claude Code

  7. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    Dev report (os-dev subagent, branch claude/issue-3129-timeline-startdatefield) — draft PR #4072.

    The card's literal premise no longer reproduces

    Drove ListView into the real ObjectTimeline on current origin/main and read the bucket headers for every authoring shape:

    shape bucket
    timeline: { startDateField, endDateField } (this card's shape) Later
    options.timeline: { startDateField, endDateField } (what app-shell emits) Later
    options.timeline: { dateField } (legacy alias) Later
    timeline: { startDateField, scale: 'month' } Later
    options.calendar: { startDateField }, rendered as timeline No date

    The declared-key path was fixed by PR #3175 (5cb75b3, 2026-08-02) — one day after this card was filed, four days before triage. The 2026-08-06 triage re-verified that startDateField consumers were alive, not that the defect still reproduced, and explicitly asked for a repro re-run before dispatch (「派发前请在当前 main + 当前已发布 console 上重跑一次复现」). That re-run had not happened.

    The defect that does reproduce — and it matches this card's own isolation evidence

    The last row is a live defect. A view whose date axis is bound under calendar is offered the Timeline visualization (the capability gate has always accepted options.calendar.startDateField as a timeline-resolvable axis) and then buckets every record into "No date" — while the calendar renders the very same field. That is precisely the report's isolation line: "the same start_date / end_date fields render correctly in the Calendar and Gantt views".

    Two causes, both fixed in #4072: ListView's render branch never read calendar config even though its own gate promised it; and app-shell's object page fabricated startDateField: 'due_date' into options.timeline for every object view, which shadowed the fallback entirely.

    Answering the triage structural warning (⚠️ 「不要写第四份」)

    Confirmed: Calendar, Gantt and Timeline each carry their own resolution, in three files — packages/plugin-list/src/ListView.tsx, packages/app-shell/src/views/ObjectView.tsx, packages/plugin-view/src/ObjectView.tsx. There is no shared resolver to wire into. No fourth copy was written: the PR collapses the two timeline read-sites (capability gate + render branch) into one resolveTimelineDateBinding, and removes the app-shell copy outright by making that layer forward instead of resolve. Calendar and Gantt were left untouched as out of scope — unifying all three is a separate refactor.

    Outstanding

    I could not inspect the HotCRM crm_campaign/view/campaign_timeline metadata from this container, so I cannot state which of the two shapes that view actually carries. The PR is therefore Part of rather than Fixes — the published-console re-run triage asked for is still owed, and should be the thing that closes this card.


    Generated by Claude Code

  8. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4072, landing as Part of; this card stays open until the repro re-run (see disposition below).

    Premise verdict, adopted after verifying the dev's probe evidence: the card's literal premise (declared timeline.startDateField ignored) was fixed by PR #3175 on 2026-08-02 — one day after filing, four days before triage; the 2026-08-06 triage verified consumers were alive, not that the defect reproduced, and its requested repro re-run never happened before dispatch. The surviving defect matches the card's own isolation evidence: a view whose date axis is bound under calendar was OFFERED the Timeline visualization (the capability gate accepted it) while the render branch never read calendar config — compounded by app-shell fabricating startDateField: 'due_date' into every object view's timeline options, which shadowed the fallback and is indistinguishable from a real binding downstream.

    What shipped: one resolveTimelineDateBinding(schema) consumed by BOTH the gate and the render branch (spec key > legacy alias, timeline > calendar, both nestings); app-shell now forwards only what the view declared (fabricated due_date removed). Two timeline read-site copies became one, the app-shell copy deleted — no fourth copy, per the triage warning; Calendar/Gantt untouched. Both directions pinned; two reverse verifications with predicted directions (5 red dropping calendar from the resolver; exactly 2 red restoring the fabricated default). 424 + 2929 tests green locally; all 20 CI checks completed, 0 failures; changeset minor.

    Disposition (what closes this card): after #4072 merges, re-run the original HotCRM crm_campaign/view/campaign_timeline repro (the console re-run triage asked for on 2026-08-06). If the view binds its dates under calendar, this PR is the fix and the card closes on that evidence; if it still buckets to "No date", there is a third cause and the card stays open with new data. ⛔ Do not close this card on the merge alone. The shared cross-view-type date-resolver idea (nine inline resolutions across three files) is deliberately NOT filed — the triage thread owns the observation; it goes to the maintainer via the round report as an optional follow-up.

    Landing next: flipping ready and driving to MERGED.


    Generated by Claude Code

  9. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    Close gate met — HotCRM repro re-run: PASS (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3; independent dogfood verification agent, full browser run).

    Provenance: console = objectui origin/main tip 9154d9e90 (contains PR #4072's merge bd863fe49, ancestor-verified), server = HotCRM @ 333259a47 on published @objectstack/* 17.0.0-rc.5, fresh DB + seed, real login.

    Evidence, against the original repro's exact surface (/apps/crm_enterprise/crm_campaign/view/campaign_timeline):

    • Server serves the card's literal authoring shape: timeline: { startDateField: "start_date", endDateField: "end_date", titleField: "name", groupByField: "channel", scale: "month" }.
    • Discriminator held: all 7 seeded campaigns share created_at = 2026-08-10 (boot time) while start_date spreads 2026-07-06 → 2026-09-06 — so axis position can only come from the declared field.
    • DOM read-back: 7/7 items render <time datetime> exactly equal to their API start_date (e.g. Developer Content Marketing Push 2026-07-06, SaaSCon 2026 Trade Show 2026-09-06); end dates match end_date; ascending sort by start_date; channel lanes per groupByField. The creation date appears nowhere; the original symptom ("No date" × 7) appears nowhere.
    • Negative control: a view with NO timeline config (all_campaigns) switched to Timeline renders all 7 under "NO DATE" — the renderer does not opportunistically grab a date column, ruling out a false positive.
    • The ACCEPT comment's open question resolves: the view binds under timeline (the fix(studio,timeline,list): 表单设计器解析对象翻译;timeline 认它自己配置的日期字段 (#3134, #3129) #3175 shape, re-pinned by fix(timeline): bind the timeline to the date axis the view declares (#3129) #4072); no third cause.

    One caveat for the record: this verifies the objectui console at main. The vendored console inside @objectstack/cli 17.0.0-rc.5 picks up #4072 only at the next objectui pin/rebuild — i.e. the fix reaches end users with the next release pin bump (same vehicle as the rest of this wave's console fixes).

    Closing as completed — fix merged (PR #4072) + original repro re-run green.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions