fix(timeline): bind the timeline to the date axis the view declares (#3129) - #4072
Merged
Merged
Conversation
…3129) A view whose date axis is bound under `calendar` was OFFERED the Timeline visualization and then bucketed every record into "No date" — while the calendar rendered the very same field correctly. Two read-sites disagreed about what counts as a timeline binding: - ListView's capability gate accepted `options.calendar.startDateField` as a timeline-resolvable axis; the render branch never read calendar config at all, so it fell through to its `created_at` last resort. - app-shell's object page emitted `startDateField: 'due_date'` into `options.timeline` for EVERY object view, declared or not — downstream indistinguishable from a real binding, and always present, so it shadowed the fallback entirely. ListView now resolves the axis once (`resolveTimelineDateBinding`, consumed by both the gate and the render branch, spec key before legacy alias and `timeline` before `calendar` in both nestings), and the object page forwards only what the view declared plus the object's titleField. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #3129 — deliberately not
Fixes. See "Premise verification" below: the card's literal premise no longer reproduces, and the re-run against the published console that triage asked for is still outstanding, so this should not auto-close the card.Premise verification (the issue body is a lead, not a spec)
The card says the timeline ignores the view's declared
timeline.startDateField. On currentorigin/mainthat is no longer reproducible. A probe droveListViewinto the realObjectTimelineand read the bucket headers for every authoring shape:timeline: { startDateField, endDateField }(the card's shape)Lateroptions.timeline: { startDateField, endDateField }(what app-shell emits)Lateroptions.timeline: { dateField }(legacy alias)Latertimeline: { startDateField, scale: 'month' }Lateroptions.calendar: { startDateField }, rendered as timelineNo dateThe declared-key path was fixed by PR #3175 (
5cb75b3, 2026-08-02) — one day after this card was filed and four days before triage. Triage on 2026-08-06 re-verified thatstartDateFieldconsumers were alive, not that the defect still reproduced, and explicitly asked for a repro re-run before dispatch.The defect that does reproduce
The last row above is a live defect, and it is exactly the shape the report isolated: "the same
start_date/end_datefields render correctly in the Calendar and Gantt views". A view whose date axis is bound undercalendaris offered the Timeline visualization and then buckets every record into "No date", while the calendar renders the very same field. Two read-sites disagreed about what counts as a timeline binding:ListView's capability gate acceptedoptions.calendar.startDateFieldas a timeline-resolvable axis. The render branch never read calendar config at all, so it fell through to itscreated_atlast resort.startDateField: 'due_date'intooptions.timelinefor every object view, declared or not. Downstream that is indistinguishable from a real binding, and because it is always present it shadowed the fallback entirely — which is why the calendar-bound case cannot be fixed inListViewalone.Treating a calendar binding as a timeline axis is not a lenient fallback added here: the capability gate has always said so, and
InterfaceListPagederives a timeline's default binding from the very samedefaultCalendarFromObjecthelper it uses for calendars. What was missing is that one of the two read-sites never honoured the promise the other made.Change
packages/plugin-list/src/ListView.tsx— newresolveTimelineDateBinding(schema), the single resolution consumed by both the capability gate and the timeline render branch. Spec key before legacy alias,timelinebeforecalendar, both nestings at each level. The resolved axis is written onto the nested config as well as the flat prop, becauseObjectTimelinepreferstimeline.startDateFieldand app-shell emits a timeline config object for every object view.packages/app-shell/src/views/ObjectView.tsx— new exportedtimelineViewOptions(viewDef, objectDef); the object page now forwards only what the view declared, plus the object'stitleField(the one thing this layer knows andListViewdoes not). The fabricateddue_dateis gone.Answering the triage warning: Calendar, Gantt and Timeline each carry their own resolution, in three files (
ListView.tsx, app-shellObjectView.tsx, plugin-viewObjectView.tsx) — there is no shared resolver to wire into. No fourth copy was written: this collapses the two timeline read-sites into one and removes the app-shell copy outright. Calendar and Gantt were left untouched (out of scope); noted on the issue.Both directions pinned
timeline.startDateFieldstill wins over a calendar one;created_atfallback, and nothing is invented from the object.Reverse verification (direction predicted before running, both observed)
calendarfrom the resolver's source list: 5 red, including the end-to-end bucket assertionexpected [ 'No date' ] to deeply equal [ 'Later' ].startDateField: … || 'due_date': exactly the two "invents NO date field" cases go red (expected 'due_date' to be undefined); everything else stays green, because the fabricated value is only observable when the view declared nothing.Tests
packages/plugin-list+packages/plugin-timeline: 29 files, 424 passed.packages/app-shell: 314 files, 2929 passed, 1 skipped.type-check: 31 tasks successful.lint: 0 errors.Changeset:
.changeset/timeline-binds-to-the-calendar-date-axis.md(minor— records move out of "No date" into real date buckets).Generated by Claude Code