Uh oh!
There was an error while loading. Please reload this page.
feat(types,plugin-gantt): declare the flattened GanttConfig face ObjectGantt reads (#6051) - #6472
Merged
Merged
Conversation
…ctGantt reads (#6051) `getGanttConfig` has two branches. When `startDateField` and `endDateField` are both present at the TOP level it builds its config from top-level keys and returns early; otherwise it reads the `gantt` block. Everything the first branch reads was undeclared, and unlike #5903's ten none of it was behind a cast: `BaseSchema` carries `[key: string]: any` and the helper's parameter was `ObjectGridSchema | any`, so `schema.colorField` type-checked as `any` with no syntax to grep for. The census is therefore an AST enumeration, not a compile-and-observe — an index signature absorbs exactly the evidence an annotation would have produced. - Re-derived on the post-#5903 tree: 47 top-level keys read, 19 declared, 28 residue. #5903 absorbed NONE of the card's 24; the residue is four larger because #5903 retyped `ObjectGanttProps.schema` to `ObjectGanttSchema`, which is correct and is why `staticData`/`filter`/`sort` (declared on `ObjectGridSchema`) now need declaring here, plus `gantt` itself. - The 24 flattened members are DERIVED from `GanttConfig`, so the flat spelling cannot fork from the block spelling; the invariant is pinned in the type system (every `GanttConfig` key is declared at the node's top level). - Nine members move out of `plugin-gantt`'s package-private `GanttConfigEx` into `@object-ui/types`' `GanttConfig` to make that derivation possible. A move, not new vocabulary: the `gantt` block already honoured all nine. - `getGanttConfig(schema: ObjectGanttSchema)` — without it the reads would still land on the index signature. - The zod mirror gains the same 28 at the same requiredness, spec-modelled ones from `GanttConfigSchema.shape` by reference, so `zod-mirror-parity` stays at zero drift and no ledger entry is added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
Contributor
✅ 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
|
…6475) PM ruling 2026-08-26 on #6051: `gantt` is the one key of the measured 28 whose declaration would NOT have been additive, and a published CLI's refusal behaviour gets decided on its own card rather than inside a 27-key declaration PR. The other 27 are unchanged and land here. Severed: `gantt?: GanttConfig` on `ObjectGanttSchema` and its `SpecGanttConfigSchema.extend(GanttConfigExtensionFields)` mirror entry. With no mirror entry a block rides through `.passthrough()` unvalidated; with one it is parsed against the spec's `GanttConfigSchema`, which REQUIRES startDateField / endDateField / titleField — and `ObjectGanttSchema` reaches the CLI's `validate`/`check` through `AnyComponentSchema`, so a block missing one of the three would move from "accepted, then warned about at runtime" to "refused at authoring time". - The omission is recorded where someone would look for the key: a comment on the declaration, a comment in the mirror, and the `ObjectGanttProps.schema` JSDoc. All three name #6475 and say `getGanttConfig` still reads and honours the block, so none of them can be read as "nothing reads `gantt`". - Today's behaviour is PINNED rather than left implicit — the test that used to assert the block face accepts the flat vocabulary now asserts the mirror has no `gantt` entry and that a block missing the required trio (and even a wrong-typed block) parses green. The omission is a measured state. - `GanttConfigExtensionFields` stays: it is still the single source the flat face is built from, and the one line #6475 would need. Its doc no longer claims it feeds two faces. This change is now additive-only end to end: 27 new optional keys, no narrowing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
Contributor
✅ 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
|
This was referenced Aug 26, 2026
os-support-ai
marked this pull request as ready for review
August 26, 2026 02:55
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 26, 2026
os-support-ai pushed a commit
that referenced
this pull request
Aug 26, 2026
…mber The docblock said the restated members are kept for ONE reason — their JSDoc being the only prose in this repo for what the renderer does with each key. Measured on main, that is false twice over: four of the twelve carry no JSDoc here at all (parentField, baselineEndField, assigneeField, effortField), and since #6472 the flattened face ObjectGanttSchema in @object-ui/types documents all twelve, so the prose is not unique either. The reason that actually reaches every member is already in the same docblock, stated as a mechanism rather than as a reason: naming the local half gives ObjectGantt.configPin.test.ts two independent operands. For parentField it is the only reason, so a reader applying the ONE-reason test literally concludes that member is deletable. State both reasons as reasons, name parentField as the worked example, and correct the neighbouring NARROWINGS claim that the pin test already measures as false. Comment-only; no declaration changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
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.
Fixes#6051
getGanttConfig(packages/plugin-gantt/src/ObjectGantt.tsx) has two branches.When
startDateFieldandendDateFieldare both present at the TOP level itbuilds its config from top-level keys and returns early; otherwise it reads
the
ganttblock. Everything the first branch reads was declared by nothing.The concealment is an index signature, so the obvious method does not work
objectui#5903's ten keys were hidden by
(schema as any).K— a syntax a regexfinds. These are hidden by
BaseSchema's[key: string]: any(objectui#5155'sstructural ceiling) reached through a parameter typed
ObjectGridSchema | any:schema.colorFieldtype-checked asanywith no cast anywhere.So "annotate the parameter and see what errors" is a blind instrument here — the
index signature absorbs every literal name, and the annotation compiles clean
while enforcing nothing (the class objectui#6373 records). The census below is an
AST enumeration of every top-level key read off the
schemaprop inpackages/plugin-gantt/src/**(non-test), strippingas/ parenthesis /non-null wrappers and following local aliases.
Re-derived on today's
main— the residue grew, it did not shrinkThe card was filed before objectui#5903 landed (PR #6053,
00f3eb5b9), so thepopulation was re-measured rather than inherited. The dispatch presumed the
card's 24 was stale because #5903's declaration work had absorbed part of it. It
had not — the residue moved in the other direction:
schema(AST)objectui#5903 absorbed NONE of the card's 24. Its ten (
skipWeekends,holidays,persistLayout,viewName,navigation,markers,criticalPath,showBaselines,readOnly,mobileReadOnly) are disjoint from them.Why four more, and why #5903 is the cause. The card scored "declared by
neither
ObjectGanttSchemanorObjectGridSchema" over the flat branch only. PR#6053 retyped
ObjectGanttProps.schemafromObjectGridSchematoObjectGanttSchema— correct, and it is exactly what makes these reads resolveagainst this interface — but
staticData,filterandsortwere declared onObjectGridSchemaand are not on this one, so that retyping is what droppedthem out of the reachable declared surface.
gantt, the block face, sat outsidethe line range the card cited and is declared by neither. All four have live read
sites; three of them are declared here and
ganttis severed (below).Clause ② — this widens a published surface, in TWO places
1.
ObjectGanttSchemagains 27 keys. 24 flattenedGanttConfigmembers,every one with a read site in
getGanttConfigand a consumer downstream:colorField·borderColorField·dependenciesField·parentField·typeField·lockField·objectField·summaryExtent·defaultCollapsedDepth·tooltipFields·baselineStartField·baselineEndField·groupByField·resourceView·assigneeField·effortField·capacity·quickFilters·autoZoomToFilter·timeSegments·interactions·exportFileName·timeZone·dependencyTypesplus the three query keys the fetch path reads —
staticData(getDataConfig),filter($filter) andsort($orderby; both named byOBJECT_GANTT_DATA_SOURCEinindex.tsx).2.
GanttConfigitself gains nine members, and it is exported BY NAME from theentry file (
packages/types/src/index.ts:382):lockField,objectField,summaryExtent,defaultCollapsedDepth,borderColorField,dependencyTypes,timeZone,exportFileName,interactions. The entry file's own diff is emptyonly because the export list already named the type — the widening happened at
the declaration, so an empty entry-file diff is not the Clause-② test on its
own; reachability from the entry plus a grown shape is.
Every one of the 36 additions is additive: all optional, nothing previously
legal loses its slot, and no verdict was "declare a key nothing reads" — every
key has a read site plus a consumer. There is no narrowing anywhere in this
change.
gantt— the 28th key — is severed to #6475 on purposeThe block face is a genuine read:
getGanttConfig's second branch honours it infull. It is not declared here because it is the one key whose declaration would
not have been additive. It has no mirror entry today, so a block rides
through
.passthrough()unvalidated; declaring it asGanttConfigmeans it getsparsed against the spec's
GanttConfigSchema, which requiresstartDateField,endDateFieldandtitleField— andObjectGanttSchemais amember of
AnyComponentSchema, so that reachessafeValidateSchemaand with itthe CLI's
validate/check. A block missing one of the three would move from"accepted, then warned about at runtime" to "refused at authoring time".
PM ruling (2026-08-26): a published CLI's refusal behaviour gets decided on its
own card, where a reviewer can see what they are approving, rather than inside a
27-key declaration PR. #6475 carries the complete measurement — including the
case FOR enforcing it (the renderer already feeds that block to
GanttConfigSchema.safeParseand logs[ObjectGantt] Invalid gantt configuration, so enforcing restoresdeclared = enforcedrather than inventinga contract) and the zero-casualty in-repo scan, with its own limit stated: 5095
tracked files measures this tree, and authored metadata outside it is precisely
what an in-repo census cannot see.
The omission is recorded where someone would look for the key — a comment on the
declaration, a comment in the mirror, and the
ObjectGanttProps.schemaJSDoc,all three naming #6475 and all three saying the renderer still reads and honours
the block. And it is pinned, not left implicit: the test asserts the mirror
has no
ganttentry and that a block missing the required trio (and even awrong-typed block) parses green today. The omission is a measured state.
Resolution style — declared, never widened
No index signature, no cast, no
??alias was added. Mirroring how objectui#5903landed:
GanttConfig(GanttConfig['K']),the same type the
ganttblock carries, so the flat spelling cannot forkfrom the block spelling;
SpecGanttConfigSchema.shapeby reference (exactly asviewModealreadydid), and objectui's own ten live in one field map;
zod-mirror-paritystays at zero drift for this pair — no
KnownDrift, noUnmirroredDeclaredentry;
getGanttConfig(schema: ObjectGanttSchema)— without retyping the parameterthe reads would still land on the index signature, "the same invisibility in
different syntax" that PR feat(types,plugin-gantt): declare the ten gantt keys ObjectGantt reads through a cast #6053 called out.
Making the derivation possible moved nine members out of
plugin-gantt'spackage-private
GanttConfigExinto@object-ui/types'GanttConfig. A move,not new vocabulary — the
ganttblock already honoured all nine, and a typeprivate to the plugin can be referenced by neither authoring face.
The invariant is pinned in the type system, with no key list to maintain:
Add a member to
GanttConfig(or to the spec'sGanttConfigSchemait derivesfrom) without declaring the flattened spelling, and this stops compiling naming
the missing key.
DeclaredKeysis applied to both sides deliberately: thespec's schema is
$loose, soGanttConfigcarries an index signature of its ownand bare
keyof GanttConfigresolves tostring— measured, when the pin wasfirst written that way it made the
Excludeunconditionallystring. Two indexsignatures, two chances for the same vacuity; both are pinned.
Ghost-assertion guard — every pin was proved to fail
Type-check coverage measured, not assumed:
tsc -p packages/types/tsconfig.test.json --listFiles(512 files) listsgantt-flat-config-declared-keys.test.ts,src/objectql.tsandsrc/zod/objectql.zod.ts.Each mutation was proved on disk by counting the target text before/after — never
by an editor's exit code — and each leg ran under a script carrying
trap … EXIT INT TERMwith absolute paths. Nodistis involved on any leg:both tests import their subjects by relative path inside
packages/types/src,so no stale build could make a mutation invisible.
A — remove the TS declaration of
summaryExtent.Predicted: RED, with MOREdiagnostics naming the key. On-disk proof
before=1 after=0. Observed exit 1,3 errors, all three naming it:
B — remove
capacityfrom the zod mirror.Predicted: RED in bothinstruments. On-disk proof
before=1 after=0. Observed runtimeAssertionError: mirror is missing capacitypluscapacity accepted "one"(2 failed | 10 passed, exit 1), and compile-time
zod-mirror-parity.test.ts(1203,14): error TS2322: Type '"objectql.zod.ts#ObjectGanttSchema"' is not assignable to type 'never'.C — the severed-state pin: INJECT a
ganttentry into the mirror.Predicted: RED — the pin asserts the mirror has no
ganttentry. On-disk proofbefore=0 after=1. ObservedAssertionError: expected [ Array(65) ] to not include 'gantt'(1 failed | 11passed, exit 1). The severance is enforced, not just described.
All three restored by hash, not exit code —
git checkout HEAD -- <abs path>then HEAD-blob vs
git hash-object:b84200d0…and13559a75…(twice), MATCHeach time, and
git diff HEADempty afterwards.Verification (all at
2760b32a6, tree clean)pnpm exec vitest run packages/types/ packages/plugin-gantt/ packages/cli/Test Files 126 passed (126)·Tests 1389 passed (1389)· exit 0pnpm exec turbo run type-check --concurrency=2(repo-wide)Tasks: 81 successful, 81 total· exit 0pnpm exec turbo run build --filter='!@object-ui/site' --concurrency=2Tasks: 43 successful, 43 total· exit 0pnpm check:doc-snippetsEvery covered documentation snippet compiles against the built types· exit 0pnpm check:readme-exportscheck-readme-exports: OK· exit 0pnpm check:spec-symbolsspec symbol derivationOK · exit 0check:doc-types·check:doc-fences·check:phantom-deps·check:self-import·check:designer-field-key-parity·check:i18n-keys·check:published-dist·check:node-esm-loadpnpm check:control-bytesOK (scanned 5336 tracked text file(s))check-changeset-presence.mjs·check-changeset-no-major.mjspnpm --filter @object-ui/types run lint·--filter @object-ui/plugin-gantt run lint0 errors(warnings only, ungated)packages/cliis in the test union becausesafeValidateSchemaandAnyComponentSchemamake it the only runtime consumer of the changed mirror.Repo-wide
pnpm testwas not run locally and is left to CI, which shards it.Deliberately out of scope, filed separately
ObjectGanttSchemastill does not declare theganttblock face — declaring it enforces the spec's required trio and changes the CLI's refusal behaviour (severed from #6051) #6475 — declaring theganttblock face, and the CLI refusal change that comeswith it. Severed from this PR by PM ruling; carries the full measurement.
ganttblock — the opposite of the precedence ruled forplugin-map, and unruled here #6469 — the flat spelling silently outranks theganttblock, the opposite ofthe precedence maintainers ruled for
plugin-map(objectui#5018 / PR fix(types,plugin-map): declare what ObjectMap reads; themapblock outranks the flat spelling #5156),and never ruled here. This PR changes precedence in neither direction.
dependencyFieldis a declared legacy alias in@object-ui/typeswith no spec counterpart — deprecate in place (removal excluded, deferred to enforce-or-remove) #6470 —dependencyFieldis a renderer-side||alias for the spec'sdependenciesField, the lenient fallback AGENTS.md commandment #0.1 bans.GanttConfigExstill restates 11 members the spec'sGanttConfigSchemadeclares — two declarations of one key, and the comment that justified them was stale #6471 —GanttConfigExstill restates 11 spec-declared members; their JSDoc isthe only prose describing them, so they were left rather than deleted.
None of those four is addressed here — each remains open.
Shadow check: this diff touches
packages/types/src/objectql.ts, notpackages/types/src/data-display.ts(#6004's file), and no file underpackages/plugin-view,packages/plugin-grid,packages/plugin-list,packages/core/src/utilsorpackages/app-shell.Generated by Claude Code
Generated by Claude Code
Generated by Claude Code