Uh oh!
There was an error while loading. Please reload this page.
fix(app-shell,plugin-list,plugin-view): no invented gantt date field names, so the refusal screen becomes reachable - #7110
Merged
Conversation
…names, so the refusal screen becomes reachable A view carrying no `gantt:` block used to have a complete-looking date axis synthesized for it: all three faces floored `startDateField` at 'start_date' and `endDateField` at 'end_date' — field names no view had written and most objects do not carry. `ObjectGantt.getGanttConfig` takes its flat branch as soon as BOTH date props are present, so the fabricated pair short-circuited the renderer's own refusal screen, which has existed all along and was simply unreachable from every route. The same fabrication answered ADR-0047's capability gate in `ListView.availableViews`, so the Gantt toggle was live on every object view in the product. The premise was MEASURED before anything was deleted, because #7029's mechanic is only correct where a refusal path exists and that had never been established for this renderer: on the unmodified tree, `ObjectGantt` REFUSES an absent binding — it does not render empty and does not throw. Pinned as the seam in `plugin-gantt/src/ObjectGantt.unconfiguredRefusal-7070.test.tsx`. All three faces now forward only what the author declared. The app-shell inline branch becomes `ganttViewOptions`, the sibling of `calendarViewOptions` and `timelineViewOptions`. Also corrects the objectui#3129 note at the top of `app-shell/ObjectView.tsx`, which certified the gantt branch below it as already using the safe two-rung shape. It did not. The note now states each sibling branch as measured, and says explicitly which fabrication REMAINS — the timeline 'created_at' floor at the two plugin faces — rather than staying silent about it. The #7062 scan control that anchored on this face's 'start_date' floor is re-expressed rather than deleted: a machinery control on the permanent 'name' floor, plus a same-class control on the gallery branch's surviving 'image' floor, with a hand-off note for whoever retires that one. Out of scope, left in place: `progressField` / `dependenciesField`, and the timeline 'created_at' posture conflict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…ed object page emits
The negative capability-gate case supplied no `options.gantt` at all, so it
answered "no binding declared" rather than the question objectui#7070 actually
asked: does the gate drop the Gantt toggle once the OBJECT PAGE stops
fabricating? The fabrication the gate used to read came from `options.gantt`,
which after the fix is a bag that still EXISTS (`{ titleField: 'name' }`) but
carries no axis. That shape is now pinned directly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDMContributor
✅ 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-warren
marked this pull request as ready for review
September 1, 2026 01:23
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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 #7070
Gantt views no longer render on invented date field names. This is the "separately
reported" half PR #7062 fenced out: the same class it deleted from the calendar
branch, on the same three faces.
delete the fabricated literal upstream and let the renderer's own refusal answer —
is only correct where a refusal path exists, and #7070 was filed precisely because
that had never been established for this renderer. Deleting blind on the strength
of #7062's success is the specific mistake the card exists to prevent.
THE GATE — what
ObjectGanttdoes with an absent bindingIt REFUSES. Not render-empty, not throw.
Measured on the unmodified tree at
2c3cd1b75, before the first source edit:getGanttConfigreturnsnullfor a schema carrying neither aganttblock norBOTH flat date props, and the early return renders "Gantt configuration required.
Please specify startDateField, endDateField, and titleField." The measurement was
written as a real render (5 cases, all passing against the untouched component) and
kept as the regression —
plugin-gantt/src/ObjectGantt.unconfiguredRefusal-7070.test.tsx,the mirror of #7062's
ObjectCalendar.unconfiguredRefusal-7029. Nothing inplugin-gantt's source changed; only its props did.Second premise — ADR-0047's capability gate: yes, it drops the toggle for free.
ListView.availableViewsreadsschema.gantt?.startDateField || schema.options?.gantt?.startDateField. The fabrication it used to read came fromthe object page, which put
options.gantt.startDateField: 'start_date'on everyview in the product — so the Gantt toggle was live product-wide. After the fix that
bag still exists but carries no axis (
{ titleField: 'name' }), and the gate stopsresolving. No second mechanism was needed. Pinned on that exact bag, not on an
absent one.
A third measurement, load-bearing for the scope split: leaving the out-of-scope
progress/dependenciesfloors in place does NOT keep the refusal unreachable.getGanttConfiggates on the two DATE fields alone, so the surviving pair cannotresurrect a config. Pinned.
The three faces
Line numbers re-derived at
2c3cd1b75— every number in the card is stale, because#7062 edited these very files after it was written.
2c3cd1b75packages/app-shell/src/views/ObjectView.tsx:2259-2260:2302-2303packages/plugin-list/src/ListView.tsx:2310-2313:2325-2328packages/plugin-view/src/ObjectView.tsx:1347-1350:1362-1365The app-shell inline branch becomes
ganttViewOptions, the sibling ofcalendarViewOptionsandtimelineViewOptions— declared block spread whole (abare whitelist here once dropped every field past
colorFieldand flattened thechart), title floored at
'name', no date field invented.titleFieldkeeps itsfloor deliberately: a display-name default is not a date axis, and it is the rung
timelineViewOptionsalready carries.⭐ The half that matters more than the fabrication
The #3129 note at the top of
app-shell/ObjectView.tsxcertified the brokenbranches as already fixed. It retires the
'due_date'literal, explains why thepattern is harmful, and then says the gantt branch below "already uses" the safe
two-rung shape. It did not — it used exactly the one-rung fabrication the note
declares retired. The note is the authority a future fixer consults, and it vouched
for the lines that were broken.
The note now states each sibling branch as measured, not as a family, and says
explicitly what is still false rather than staying silent: the timeline
'created_at'floor at the two plugin faces — the very literal #3129 retired atthe app-shell face — is untouched here and routed to a ruling.
The live control this fix turned red — re-expressed, not deleted
#7062 left
expect(CODE.filter((l) => l.includes("'start_date'")).length) .toBeGreaterThan(0)in its own scan test so the scan could not silently become aphantom check. This fix removes that anchor (
'start_date'in app-shell CODE:1 → 0). ⛔ Deleting it would make the scan pass over nothing — the exact class this
card family is about. It is re-expressed as two controls with different jobs:
'name', which is deliberately permanenthere (the display floor the fix(timeline): honor startDateField when rendering a ListView timeline #3129 note describes as what stays). It cannot go
red as a side effect of the next fix in this family.
imageField: … || 'image'floor: the same one-rung fabrication class, stillpresent, out of scope for both ObjectView invents
startDateField: 'due_date'/titleField: 'name'for views with nocalendar:block — the guess short-circuits ObjectCalendar's own refusal screen and piles every record on today #7029 and 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. It carries a hand-off notetelling whoever retires
'image'to re-anchor rather than delete, and whatto do if nothing of the class remains.
The new app-shell scan carries a third case asserting the filter reads CODE and not
the prose that records the deletion — without it, the tripwire would go red on a
correct tree if the comment-stripping ever broke, inviting someone to weaken it.
Reverse verification
Direction predicted before running, then observed. Fix committed first, then
mutated (
|| 'start_date'/|| 'end_date'restored on all three faces),mutation confirmed on disk (app-shell CODE
'start_date'count 0 → 1; deletedforms count 0; all three blob hashes ≠ their HEAD blobs), run, then restored and
the restore proven byte-identical to the HEAD blobs (
git hash-object==git rev-parse HEAD:PATHfor all three,git diff HEADempty). The mutation scriptcarried an absolute-path
trap … EXIT INT TERM; it fired once on an ambiguousanchor — the calendar branch above uses the identical conditional-spread idiom, so
the script refused rather than mutating the wrong branch.
ObjectGantt.unconfiguredRefusal-7070(it pins the renderer, which the mutation does not touch) and the re-expressed
ObjectView invents
startDateField: 'due_date'/titleField: 'name'for views with nocalendar:block — the guess short-circuits ObjectCalendar's own refusal screen and piles every record on today #7029 control (now independent of the gantt fabrication — which is exactly theproperty that makes it durable).
the point: a fix that emitted an empty config for every view would also pass a
refusal-only test.
One harness bug was caught by a declared-config CONTROL going red:
viewOptionsinplugin-viewiscurrentNamedViewConfig?.options || activeView, so a rawviewsentry takes the
activeViewleg and my first spelling ({ options: { gantt } })read
undefinedeverywhere. Written that way, the negative case would havepassed while measuring nothing at all. Corrected, and the reason recorded in the file.
Verification
Union re-run on the final commit
be5648cc8(not on an earlier tree):pnpm exec vitest runon the 9 affected files — 9 files, 62 tests passed, exit 0packages/plugin-view/ packages/plugin-list/ packages/plugin-gantt/— 141 files, 1450 passed, exit 0packages/app-shell/(full, 11m22s) — 587 files, 5755 passed, 1 skipped, exit 0type-checkfor all four packages — exit 0,Scope: 4 of 47 workspace projects,each echoing
tsc --noEmit && tsc -p tsconfig.test.json→ the new test files aretype-checked, not excluded
check:control-bytes→✅ OK (scanned 5888 tracked text file(s))check:vi-mock-specifiers→✅ OK;check:vi-mock-inherit→✅ OK (114 call site(s) on @object-ui/react judged, 114 inherit, 0 auto-mocked)check-changeset-presence→✅ 8 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s);no-major/fixed/overwrite→ ✅Declared narrowing — eslint. Run in the plain form on the 8 changed files rather
than repo-wide (
turbo run lint): 8 files judged (count read from--format json), 0 errors, 425 warnings — all pre-existingno-explicit-anyon theas anyidiom the sibling #7029 test files use. The narrowing excludes nothing:this config enables no type-aware linting (no
projectService/parserOptions.projectin
eslint.config.*), so this diff cannot move the verdict on any file it does notcontain. CI runs the full farm regardless.
Scope — deliberately left open on #7070
'created_at'posture conflict. Not resolved in eitherdirection, and
'created_at'deleted from neither face. See the report comment onfinding(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 — the measurement there materially narrows the question.
progress/dependencies. Not date axes, different absent-valuesemantics. Left in place and now pinned, so the scope is visible rather than
implicit.
Hence
Part of #7070, not a closing keyword: the card stays open for those.Generated by Claude Code