Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-list,plugin-view): retire the 'created_at' timeline date-axis floors so the refusal becomes reachable - #7500
Conversation
…xis floors objectui#7070 step ③ of the maintainer ruling of 2026-09-01 (总监批 #28): house posture 日期轴永不虚构 — a date axis is never fabricated. `ListView` and `plugin-view`'s `ObjectView` both floored `startDateField` at 'created_at' for a timeline view that declared no axis. `ObjectTimeline` reads that flat prop at the tail of its resolver chain, so the faces answered "the axis is bound" for every view and the refusal screen step ① installed (objectui#7459, landed as 20cb8db) was unreachable from either route — which is why ①② changed nothing a user could see. Both branches now forward a declared axis or no key at all. The floor was never a harmless default: the $select projection is collected from the DECLARED timeline / options.timeline blocks and never from this prop, so an undeclared view rendered a timeline bound to a column the query had not requested and bucketed every record into "No date". The ruling also explicitly replaces the written decision that stood on the deleted ListView line, and asks the objectui#3129 note at the app-shell face to be realigned to the new reality once ③ lands; both are done here. `titleField` keeps its 'name' floor at both faces — it is not a date axis. Tests: the two face-level pins measure the prop; a new console-level test composes the real ListView / ObjectView with the REAL ObjectTimeline and asserts the refusal on screen, since neither package can see the join. Part of #7070 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…n test `tsc --noEmit` in apps/console reads it as TS6133; the automatic JSX runtime makes the import unnecessary. Caught by the package's own type-check, not by vitest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-project-manager
commented
Sep 3, 2026
✅ Landing — 32/32 green, Clause-② no verified from the diff, ready + auto-merge
Checks, read by name29 success, 3 skipped by design ( Landing authority
What I verified by content, not from the report
The control is non-zero and unmoved, so the probe discriminates and the change is surgical.
The Why this step matters more than its diff size suggests#7459 installed ⭐ The ablation's DOM dump under restored floors is the evidence that matters: Two things the implementer did that I want on the record
Card state
Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Part of #7070
Step ③ of the maintainer ruling of 2026-09-01 (总监批 #28, comment 5494805467): house posture 日期轴永不虚构 — a date axis is never fabricated.
ListViewandplugin-view'sObjectViewstop flooring the timeline axis at'created_at'.Steps ①② are landed (
20cb8db9b, PR #7467, card #7459) and this branch is cut from exactly that commit. ⛔ Nothing inObjectTimeline, the refusal screen or the locale key is touched here.The two lines
packages/plugin-list/src/ListView.tsx:2496startDateField: dateBinding.startDateField || 'created_at'packages/plugin-view/src/ObjectView.tsx:1354startDateField: ...startDateField || ...dateField || 'created_at'Line numbers re-derived on
mainat20cb8db9b, not inherited from the card.created_atoccurs 4 times inListView.tsxand 1 inObjectView.tsx; those are occurrences of the string, not floors. Read per line, exactly one floor per file:ListView.tsx:1753—s.add('created_at')inknownObjectFields. A platform-column reference, kept.ListView.tsx:1785— prose explaining thePLATFORM_RECORD_COLUMNScarve-out, kept.ListView.tsx:2496— the floor. Deleted.ObjectView.tsx:1354— the floor. Deleted.titleFieldkeeps its'name'floor at both faces: not a date axis, and the same display-name rung the gallery / kanban / gantt branches carry. Pinned as scope in both new suites.Why it was never a harmless default
ObjectTimelinereads the flatschema.startDateFieldat the tail of its resolver chain —plugin-timeline's own CONTROL block proves that rung is fully honoured — so a floor here answered "the axis is bound" for every view and the refusal step ① installed was unreachable from either route. That is exactly why #7459's own measurement recorded that nothing began refusing when ①② landed.And the invented axis was never fetched: the
$selectprojection is collected from the DECLAREDtimeline/options.timelineblocks (collectViewFields), never from this prop. An undeclared view therefore got a timeline bound to a column the query had not requested and bucketed every record into "No date" — the screen that looks built, is wrong, and gives the author no signal.The ruling also explicitly replaces the written decision that stood on the deleted
ListViewline ("created_atstays the last resort for a view that declares no date axis anywhere"), and asks the objectui#3129 note at the app-shell face to be realigned once ③ lands. Both are done here; the app-shell edit is comment-only.⭐ Blast radius — measured, not asserted
Which authored surfaces in this repo reach these floors: zero. Which begin refusing: zero.
Every sweep below was run with a control that FIRED in the same run, because a zero from a grep is not a reading.
examples/schema-catalog/src/schemas/plugin-timeline/*.jsondocuments andpackages/types/examples/data-display-examples.jsonare directtype: 'timeline'schemas carrying authoreditems/events— they never traverseListVieworgenerateViewSchema, andhasAuthoredItemsshort-circuits the refusal predicate (!hasAuthoredItems && !startDateField) regardless. Control: the identical regex for"calendar"fires oncomponents-complex-view-switcher/view-switcher.json:24.'calendar'fires on six sites.appearance.allowedVisualizationsnaming timeline in authored metadata: zero.Two product routes were then measured rather than reasoned about, and both are why the radius is small:
CreateViewDialog'sREQUIRED_FIELDStable liststimeline.startDateFieldamong "the sub-config keys a view type must have set before a record can be persisted", with a smart default auto-picked from the object's real date fields (PRIMARY_DATE_PREFERRED). Anything created in-product carries a declared axis.InterfaceListPage:413-414fillstimelinefromdefaultCalendarFromObject(objectDef)when timeline is whitelisted — a field the object declares, orundefined. So such a page refuses only when the object carries no date field at all, which is the correct answer: there is no axis to declare. This is derivation from schema, not fabrication, and the fix(timeline): honor startDateField when rendering a ListView timeline #3129 note already cites it as the good shape.What is left is metadata authored outside those paths — hand-written or migrated views. Those begin refusing, which is what the ruling ordered, and the refusal names the keys to declare so an affected view reports its own fix. The changeset carries the migration note.
⛔ No replacement fallback was added anywhere.
Verification
Red-then-green, taken naturally first. Both face-level suites were written and run against the UNMODIFIED tree at
20cb8db9bbefore the first source edit:plugin-view3 failed / 5 passed,plugin-list3 failed / 14 passed — the failures exactly the "invents no axis" cases, every render proof and CONTROL green in the same run. After the edit: 25/25.Ablation, for the console composition test, which was written after the fix and so had never been observed red. Both floors restored, proven on disk in both directions before the run (removed-text 0 AND injected-text 1 per file, plus a blob-hash change on each),
trap ... EXIT INT TERMwith absolute paths, restore proven by state (git diff HEADempty + blob equality against the HEAD blob), never by an exit code. No build/distis involved — vitest resolves@object-ui/*to siblingsrc/through the root config's alias table — so there is no stale-artifact leg to prove.Under the restored floors: 9 failed / 24 passed. The 9 are precisely the "invents no axis" and "refuses on screen" cases at all three levels; every render proof and every CONTROL stayed green in the mutated world. The DOM dump from that run is the point of the exercise — it shows
data-testid="timeline-canvas"and the textSpring Launch: a healthy, convincing, entirely wrong timeline, not an error. That is what the pin has to be able to fail on.ListView.tsx— the identical conditional-spread text also buildsresolvedTimeline's nested config a few lines up, which this card does not touch. The assertion refused and the run aborted rather than mutating the wrong line; both anchors are now two-line, pinned by thetitleField:rung that follows the floor.Refusal asserted positively. Every block opens with a render-proof row, and each absence marker is asserted PRESENT by a control in the same run — a test that only says "no timeline" is satisfied by a component that threw.
Test files
packages/plugin-list/src/__tests__/ListView.timeline-binding.test.tsx— the fix(timeline): honor startDateField when rendering a ListView timeline #3129 case that pinned the floor is rewritten, not deleted (it is where the retirement has to become visible); a new step-③ block adds the render proof, the two absence cases and the scope controls; a capability-gate block records, in both directions, that the ADR-0047 toggle is unchanged by this step — unlike the gantt face, the Timeline gate always readresolveTimelineDateBindingand never the flat floor.packages/plugin-view/src/__tests__/ObjectView.timelineBinding-7070.test.tsx— new; the sibling ofObjectView.ganttBinding-7070/calendarBinding-7029.apps/console/src/__tests__/timelineAxisRefusalReach-7070.test.tsx— new, and the reason it lives in the console: it is the only package depending onplugin-list,plugin-viewandplugin-timeline, so it is the only place the join can be measured. Real faces, real renderer, no stub. Its rows deliberately carry a realcreated_atcolumn so a restored floor renders a convincing chart rather than an empty one.Runs — union re-run after the final commit, at
64d8fbe2a:pnpm exec vitest run packages/plugin-list packages/plugin-view packages/plugin-timeline apps/console— 207 files / 2357 tests passed.views/ObjectView*— 290 passed. The app-shell edit is comment-only; the full 608-file suite is CI's.plugin-timeline,plugin-ganttrefusal pin,reactsmoke,corenormalize-list-view,typestimeline pins, schema-catalog render) — 29 files / 991 passed.type-checkforplugin-list,plugin-view,app-shell,console— green. Each package chainstsc -p tsconfig.test.json, and both new package-level test files were confirmed present in that program with--listFiles(1 hit each) rather than assumed. It caught a real defect: an unusedReactimport in the console test (TS6133), fixed in64d8fbe2a.check:control-bytes,check:vi-mock-specifiers,check:vi-mock-inherit,check:phantom-deps,check:self-import,check:published-tsconfig-exclude,check:side-effects-array,check-changeset-fixed,check-changeset-no-major,check-type-check-coverage,check-lint-coverage— all report their own OK line. These scan tracked files, so they were re-run after committing; the counts moved with the new files (control-bytes 6157 to 6160 tracked text files; vi-mock 4224 to 4226 source files and 122 to 123 judged call sites), which is what makes the greens readings rather than scans that never saw the diff.Lint — narrowed, and the narrowing is measured.
eslint --no-inline-configover the 6 changed.ts/.tsxfiles: 0 errors, 424 warnings, all pre-existing repo-wide classes (no-explicit-any,react-refresh/only-export-components); the siblingObjectView.ganttBinding-7070.test.tsxproduces 4 warnings of the same class, so the new files add no new one. Three pieces of evidence that this excludes nothing: the receiving population came from eslint's own config (all 6 files were linted, none silently ignored); the count of 6 is read from--format json, not asserted; andeslint.config.jssets noparserOptions.project/projectService, so linting here is not type-aware and a change in one file cannot move the verdict on a file it does not touch.--max-warningsis deliberately unset in this repo, perlint.yml.Clause ②, re-declared from this diff
no. The seven files this diff touches are two renderer faces, one comment-only app-shell note, three test files and one changeset. Nothing under
packages/types/, no Zod schema, no spec surface, no authorable key — the view layer stops fabricating; the published accept surface is untouched. Control for that zero: the same path filter matchespackages/types/src/zod/data-display.zod.tswhen fed it.Out of scope, filed
progress/dependenciesfloors still fabricate field names at two faces — flavour 3 of #7070, carded per the 2026-09-01 ruling #7499 — flavour 3 (progress/dependencies), carded per ruling clause 3, unassigned and unlabelled for triage. Filing it is what lets finding(views): the gantt and timeline branches still fabricate date-axis field names on all three faces — and app-shell's own #3129 note certifies them as already fixed #7070 close. Re-measured while filing: that pair survives at two faces, not three — the app-shell face carries neither key. ⛔ The date-axis conclusion is explicitly not applied to it.🤖 Generated with Claude Code
https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Generated by Claude Code