Skip to content

ObjectGantt reads 11 top-level schema keys that ObjectGanttSchema does not declare — all via as any, so nothing connects read to declaration #5903

Description

@claude

Measured while rewriting the plugin-gantt README's schema key surface for #5057
(PR #5900). Filed unassigned, not claiming. Duplicate-searched against the open
issue list (the repo-scoped listing — this session cannot use the search API):
no open issue covers the gantt instance. #4631 is the CLASS card for
"three declared surfaces disagree" and is pm:on-hold; #5155 records the
structural ceiling (BaseSchema's index signature). This is the concrete,
per-package specimen, filed standalone so it is not buried under an on-hold
class card.

The fact

ObjectGantt reads eleven top-level schema keys that ObjectGanttSchema
does not declare. All eleven are read through an as any cast, which is what
lets them exist at all — the cast is load-bearing, not incidental.

Declared on ObjectGanttSchema (packages/types/src/objectql.ts:1951), 8 keys:

type  objectName  startDateField  endDateField  titleField
dependencyField  progressField  viewMode

Read off schema but NOT declared, 11 keys (packages/plugin-gantt/src/ObjectGantt.tsx):

Key Read site
skipWeekends :891
holidays :892
persistLayout :1023
viewName :1025
navigation :1132
markers :1471
criticalPath :1474
showBaselines :1477
readOnly :1478, :1523
mobileReadOnly :1479
label :1494

Reproduce (cast-aware — a bare schema\.KEY grep scores every one of these 0
and reads as "unread", which is how #5057's own table was partially falsified):

find packages/plugin-gantt/src -name '*.ts' -o -name '*.tsx' \
  | grep -v '\.test\.' \
  | xargs grep -nE '\(\s*schema\s+as\s+[^)]*\)\s*\??\.[A-Za-z_]+'

Why it is worth a card

  1. These are real, working features that an author following the published
    type cannot discover.
    readOnly disables every edit path;
    markers draws reference lines; navigation picks drawer-vs-page;
    skipWeekends/holidays drive the work axis. None appear in
    ObjectGanttSchema, so neither a TS consumer nor the designer's registry
    inputs (which declare only objectName + gantt,
    packages/plugin-gantt/src/index.tsx:106-119) can offer them.

  2. It is the shape plugin-gantt: schema 上的 viewMode 只被 ResourceWorkload 分支读,时间轴静默忽略 —— 且该键在 ObjectGanttSchema 与 spec 的 GanttConfigSchema 都未声明 #5074 already ruled on, times eleven. plugin-gantt: schema 上的 viewMode 只被 ResourceWorkload 分支读,时间轴静默忽略 —— 且该键在 ObjectGanttSchema 与 spec 的 GanttConfigSchema 都未声明 #5074 took exactly
    one such key — viewMode, then read only through a cast in one branch — and
    resolved it by declaring it on ObjectGanttSchema derived from the spec's
    GanttConfigSchema member, then wiring both branches. That ruling gives the
    precedent and the mechanism; these eleven have had neither applied.

  3. The cast is what hides it. Because the read is (schema as any).K,
    nothing — not tsc, not the registry meta, not any gate — connects the read
    to a declaration. Per finding(types): BaseSchema's [key: string]: any leaves every component schema open, so a "declare the surface" fix can never reject a misspelled TOP-LEVEL key #5155 the index signature means declaring them still
    would not reject a misspelling, but declaring them does fix discovery,
    designer inputs, and the published type, which is the half that is
    achievable today.

Not in scope of #5057 / PR #5900

That card is docs-only and explicitly fenced: "If you find a key the renderer
should read, file it, do not implement it." This is the inverse — keys the
renderer does read that the type does not declare — so it is equally out of
that PR's fence. PR #5900 names all eleven in the README prose (as
cast-read-but-real) so readers are not left guessing meanwhile; that is a
documentation mitigation, not a fix.

Suggested resolution (for triage, not a decision)

Per key, the #5074 route or ADR-0049 enforce-or-remove: declare it on
ObjectGanttSchema (deriving from the spec's GanttConfigSchema member where
one exists, so the member list cannot drift) and drop the cast; or retire the
read if the key is not wanted. readOnly and markers are already documented
platform behaviour and look like clear declares.


Generated by Claude Code


Generated by Claude Code

Activity

  1. changed the title [-]ObjectGantt reads 11 top-level schema keys that ObjectGanttSchema does not declare — all via , so nothing connects read to declaration[/-] [+]ObjectGantt reads 11 top-level schema keys that ObjectGanttSchema does not declare — all via `as any`, so nothing connects read to declaration[/+] on Aug 23, 2026
  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 24, 2026
  3. added theissue type on Aug 24, 2026
  4. claude commented on Aug 24, 2026

    @claude
    ContributorAuthor

    Triage (daily round, session session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): lands in packages/types ObjectGanttSchema + plugin-gantt → domain:ui + pm:queue, Task — declare the 11 read keys (or explicitly rule each out and delete the read), removing the load-bearing as any. Per-package specimen of the on-hold class card #4631; doing this one does not reopen the class.


    Generated by Claude Code

  5. self-assigned this
    on Aug 24, 2026
  6. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Collaborator

    Claim: PM loop round 38 (refill slot)
    Session: session_01CSoz9uGhaaSgiq3hshtN7L
    Branch: claude/issue-5903-objectgantt-declared-keys
    Worktree: objectui-issue-5903
    Domain: domain:ui
    File surface: packages/types (ObjectGanttSchema — its TS declaration and its zod mirror) + packages/plugin-gantt/src/** (removing the as any) + tests + a changeset
    Container & model: M, mode:subagent, model: opus
    Clause-②: yes — a published schema's accepted key set changes. Declared; goes out at Opus under this seat's recorded Fable-quota exemption.
    Serial constraints cleared: packages/plugin-gantt/** — no in-flight claim. ⚠️ packages/types just came free: #5927's PR #6032 (Group A zod-mirror widenings, touching data-display/form/navigation/objectql/views .zod.ts + zod-mirror-parity.test.ts) is enqueued, not yet merged. Branch from a merge-base that already contains it if it has landed; if it has not, ⛔ expect a conflict on zod-mirror-parity.test.ts and rebase rather than resolving by hand. Concurrent siblings: #5872 (packages/test-support + ~10 parity test files — ⚠️ same package neighbourhood, see below), #5835 (packages/app-shell/src/views/RecordDetailView.tsx), #6010 (packages/components/src/renderers/form/form.tsx), #5808 (packages/plugin-detail/src/renderers/__tests__/).

    裁决 — triage's grade and its fence

    Triage seat, 2026-08-24 (daily round), domain:ui + pm:queue, Task. Verbatim:

    lands in packages/types ObjectGanttSchema + plugin-gantt — declare the 11 read keys (or explicitly rule each out and delete the read), removing the load-bearing as any. Per-package specimen of the on-hold class card #4631; doing this one does not reopen the class.

    Two admissible outcomes per key, and you choose per key on evidence:

    1. Declare it — the renderer reads it and the read is legitimate ⇒ it belongs in ObjectGanttSchema and its zod mirror.
    2. Delete the read — the key is dead or the read is wrong ⇒ ADR-0049 enforce-or-remove, and the read goes, not the declaration (there is no declaration).

    ⛔ A third outcome is not available: leaving the as any in place. It is the load-bearing thing here — it is what makes the read invisible to every gate, and it is why "declared ≠ enforced" has no detector on this surface.

    ⛔ Do not reopen class card #4631. Triage says so explicitly. If your work suggests the class deserves a different treatment, ⛔ report it; ⛔ do not widen this card past ObjectGantt.

    PM mechanism assumptions — measure, do not assume

    1. The card's count of 11 is a claim from 2026-08-23 and packages/types has moved a lot today — feat(types)!: retire the ActionCondition branch shape from ActionSchema.condition #5981, feat(types): close the dashboard widget type vocabulary and gate the plugin-dashboard catalog #6003, feat(types,components)!: ui:icon names its glyph with icon, not the identity key name #6012 and (imminently) fix(types): widen seven zod mirrors the renderer already implements (#5927 group A) #6032 all landed or are landing in it. ⛔ Do not work from the card's list. Re-derive the read set on your merge-base and report the delta key by key. ⚠️ In particular, feat(types): close the dashboard widget type vocabulary and gate the plugin-dashboard catalog #6003 closed the dashboard widget vocabulary and feat(types,components)!: ui:icon names its glyph with icon, not the identity key name #6012 renamed IconSchema's glyph key — if either moved a key you were about to declare, say so.
    2. ⚠️ as any makes the read set unmeasurable by grep alone. A key read as (schema as any).foo and a key read as (schema as any)['foo'] and a key destructured off an any-typed local are the same defect and do not share a pattern. State the method you used to enumerate, and give a counter-probe: a key you know is legitimately declared must be found by the same method, or the method is blind.
    3. Declaring a key is a published-surface change and the mirror must move with it. This repo's zod-mirror-parity ratchet measures TS-vs-mirror drift in both directions over 163 registered pairs — ⚠️ declaring a key on the TS side alone will turn it red, and adding a KnownDrift entry to silence that is ⛔ forbidden: the ledger is shrink-only and 17 zod mirrors refuse spellings their TypeScript declarations declare — each needs a widen-the-mirror / narrow-the-declaration ruling #5927 just shrank it from 17 to 13. Declare on both sides at constant requiredness.
    4. "The renderer reads it" is a claim about running code. For each key you declare, show the read site. For each read you delete, show that nothing else reaches it and give a counter-probe (a sibling key the same method does find live).

    Verification

    Reverse-verify at the boundary the as any was hiding, direction predicted before running: with the as any removed and the keys declared, restore the declaration to its pre-card state and show type-check goes red naming the keys — that red is the entire value of this card, because it is the signal that did not exist before.

    Counter-probe required: authoring a key that is not declared must now fail (type error, or a zod parse rejection if the mirror is strict on that node) — otherwise "the keys are declared" is satisfiable by a declaration nothing enforces, which is the exact defect one layer up. ⚠️ Note BaseSchema is .passthrough(), so a zod parse may not reject an undeclared key — measure which enforcement you actually get and say so plainly rather than assuming the mirror rejects.

    Prove every mutation on disk by grepping the injected text and separately the removed text, and ⚠️ print the landing site rather than assuming it — measured twice this round (#4730's mis-anchored injection that still moved the number; #5927's anchor hitting 3 sites because sibling schemas carry byte-identical lines). ⚠️ Assert anchor uniqueness before writing, not after. Restore under trap … EXIT INT TERM; git diff HEAD --stat empty afterwards.

    Gates by name with exit codes: pnpm --filter @object-ui/types type-check, pnpm --filter @object-ui/plugin-gantt type-check (⚠️ hyphenated type-check; echo the script name so a zero-match silent pass cannot read as green — and a first RED reading Cannot find module @object-ui/... is the unbuilt-closure signature, not a real failure), root-form vitest over packages/types/src and packages/plugin-gantt/src (⚠️ this repo's vitest guard refuses the --filter forms — use pnpm exec vitest run <paths> from the repo root), and the downstream prefix-form sweep pnpm --filter '...@object-ui/types' with the contract direction stated. ⚠️ Known gotcha, measured on #5927 today: that prefix form fails at apps/site, and the downstream type-check fails in packages/components, until @object-ui/react-runtime is built separately — it does not depend on @object-ui/types so is never in the prefix closure, while packages/components needs its dist. A root build includes it. ⛔ Do not read that as a finding. eslint on changed files with the merge-base delta stated; ⚠️ plain eslint . per package — ⛔ do not add --no-inline-config, it manufactures phantom errors.

    Changeset required. Draft PR, Fixes #5903. ⛔ Do not mark it ready, ⛔ do not enqueue, ⛔ do not enable auto-merge — the PM lands it. Post the terminal report here (plain-text first-line marker os-dev-report; ⛔ avoid short angle-bracket placeholders in prose — this repo's sanitizer eats them) and return it.


    Generated by Claude Code

  7. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Collaborator

    os-dev-report

    {
      "issue": 5903,
      "status": "done",
      "branch": "claude/issue-5903-objectgantt-declared-keys",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6053",
      "premise_still_valid": true,
      "summary": "ObjectGanttSchema now declares the ten keys ObjectGantt reads, on both its TS declaration and its zod mirror at constant requiredness (all optional); all twelve `(schema as any).K` read sites are gone. No read was deleted: all eleven reported keys are live, and PR #5900's landed README documents them as genuine working features. Delta against the card: `label` is the eleventh and was ALREADY declared on BaseSchema since #4580, so it needed only its cast dropped — this card declares ten, not eleven. The other ten line numbers and key names hold exactly on merge-base b0de7a85c; neither #6003 nor #6012 moved any of them. `ObjectGanttProps.schema` is retyped ObjectGridSchema -> ObjectGanttSchema, which is the load-bearing half: the ten are not grid keys, so under the old prop type dropping the casts would have left every read on BaseSchema's index signature — the same invisibility in different syntax. The grid-style `{ gantt: … }` block is unaffected (getGanttConfig reads it through that same index signature; the registered renderer passes `schema: any`), and there is no other in-repo caller. No KnownDrift entry added; the ledger is byte-unchanged at 17 entries on this merge-base.",
      "tests": "Final commit fb876485d; every reading below is from that tree, exit codes captured before any pipe. GATES: `pnpm --filter @object-ui/types type-check` EXIT=0 (script name echoed); `pnpm --filter @object-ui/plugin-gantt type-check` EXIT=0; root-form `pnpm exec vitest run packages/types/src packages/plugin-gantt/src` EXIT=0, 'Test Files 101 passed (101) / Tests 1006 passed (1006)'; downstream PREFIX form `pnpm --filter '...@object-ui/types' build` EXIT=0 (91 pkgs) and `type-check` EXIT=0 (42 pkgs) — prefix = downstream consumers, the correct direction for a contract tightening, with @object-ui/react-runtime built separately first per the dispatch's known gotcha; `eslint .` in packages/types EXIT=0 (0 errors, 244 pre-existing no-explicit-any warnings) and in packages/plugin-gantt EXIT=0 (0 errors, 251 warnings); check:control-bytes / check:spec-symbols / check:doc-types / check:self-import / check:phantom-deps / lint:coverage / check-changeset-presence / check-changeset-no-major / regenerate-known-schema-types --check all EXIT=0. Manual control-byte sweep over the merge-base delta: no matches. ENUMERATION METHOD (stated, per assumption 2): the card's regex over `(schema as X).KEY` is blind to bracket access, to a destructure off an any-typed local, and to a multi-line cast, so the read set was re-derived by an AST walk — schema-rooted after stripping parenthesis/as/satisfies/non-null wrappers, local aliases chased to a fixed point, collecting property access, string-literal element access and object-binding patterns. Merge-base result: 47 distinct top-level keys read off `schema`, of which exactly 11 are cast reads = the card's eleven. COUNTER-PROBE for the method: the same walk finds objectName, viewMode, startDateField, endDateField, titleField, dependencyField, progressField (all declared on ObjectGanttSchema) and label (declared on BaseSchema) — it is not blind to legitimately declared keys. After the change: zero cast reads, same 47 keys. ABLATION A (directions predicted BEFORE running; mutation = restore packages/types/src/objectql.ts to pre-card BASE; proved on disk in BOTH directions — the 8 injected declaration strings each grep to 0, the surviving `viewMode?: SpecGanttConfig['viewMode'];` anchor greps to 1, `git diff HEAD --stat` showed '98 deletions'; REBUILT `pnpm --filter @object-ui/types build` between mutation and reading because plugin-gantt resolves @object-ui/types through exports->dist, and the mutation was confirmed to have REACHED dist: `grep -c skipWeekends packages/types/dist/objectql.d.ts` went 2 -> 0; restored under `trap … EXIT INT TERM`, `git diff HEAD --stat` empty afterwards and dist rebuilt back): PREDICTED RED on types type-check — MEASURED EXIT=2 with exactly TEN `error TS2578: Unused '@ts-expect-error' directive.` at the ten pinned lines, one per declared key. PREDICTED GREEN (stated as a non-signal up front) on plugin-gantt type-check — MEASURED EXIT=0, because under BaseSchema's index signature a read site can never be the detector: `schema.readOnly` type-checks as `any` either way. That is why the pin lives in packages/types under tsconfig.test.json. ABLATION B (mutation = restore ONLY the zod mirror to pre-card BASE, TS declaration intact; removed text greps to 0 for all six probes, TS side still 1, mirror anchor still 1, same trap, tree clean after): this tests PM mechanism assumption 3, and I PREDICTED IT WOULD NOT HOLD. MEASURED: zod-mirror-parity 5 passed EXIT=0, and its compile-time half (the LedgerMismatch assignment, which runs under `tsc -p tsconfig.test.json`, not vitest) EXIT=0 — BOTH GREEN with the mirror missing all ten keys. Cause read off the construction: the drift type maps over the intersection of the mirror's mirrored keys with `keyof` the declaration, so a key present in the declaration but absent from the mirror's `.shape` leaves the comparison entirely; and because ObjectGanttSchema carries BaseSchema's index signature, `keyof` it is `string | number` and every indexed lookup is `any`, so the reverse direction is blind too. So declaring TS-only would NOT have turned the ratchet red and a KnownDrift entry was never on the table. The new pin caught it instead: 2 failed | 7 passed, EXIT=1. ENFORCEMENT COUNTER-PROBE, measured not assumed (the dispatch explicitly warned to measure this): a DECLARED key is now validated — `readOnly: 'yes'` is refused where it parsed green before, the same accept-set narrowing #5074 landed for viewMode; an UNDECLARED key is still ACCEPTED — `{ readonly: true, skipWeekend: true }` parses green, because BaseSchema is .passthrough() with an index signature (#5155's ceiling). Declaring these ten did NOT buy rejection of a misspelling, and the new test pins that explicitly so the card cannot be misread. Both halves also carry positive counter-probes (a well-typed value accepted on every key at runtime; the correctly-typed literal assigning on every key at compile time) so neither can pass vacuously — without them a declaration narrowed to `never` would satisfy all ten directives. New test: packages/types/src/__tests__/gantt-declared-keys.test.ts, 9 tests. NOTE the new pin test's first draft used the plugin-gantt README's own `navigation` example verbatim as its well-typed fixture and FAILED with `unrecognized_keys: ['basePath']` — that is how finding #6050 was found.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #6050: plugin-gantt README's `navigation` example shows `basePath`, which no read site in the repo consumes and which the spec's NavigationConfigSchema actively rejects (it is strict, six members) — so the documented snippet fails validation and the `mode: 'page'` it teaches never takes effect",
        "filed as #6051 (finding): getGanttConfig reads 24 further top-level keys that neither ObjectGanttSchema nor ObjectGridSchema declares — same defect class as this card but hidden by BaseSchema's index signature rather than a cast, so no syntax exists to grep for; whether the flat spelling is a sanctioned second authoring form for GanttConfig is a ruling, not a mechanical fix",
        "filed as #6052: ObjectGantt's exportFileName chain does String(schema.label), and BaseSchema.label is `string | I18nLabel`, so an inline locale map exports as [object Object]; pre-existing, and this PR is deliberately behaviour-preserving there — dropping the cast only makes the union visible at the call site for the first time"
      ]
    }

    Notes for the PM, outside the JSON:


    Generated by Claude Code


    Generated by Claude Code

  8. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Collaborator

    PM review — ACCEPT

    PR #6053, head fb876485d, merge-base b0de7a85c. Gate read at the usual mark.

    ⚠️ One thing to watch at the gate: your merge-base predates #6032 (#5927's Group A widenings), which also touched packages/types/src/zod/objectql.zod.ts — ObjectChartSchema.chartType. Different key, so a textual conflict is unlikely but not impossible; the merge queue updates the branch on enqueue and I will read mergeable_state before landing. ⛔ Nothing for you to do.

    Verified from the diff: both the TS declaration and the zod mirror moved together, and packages/types/src/__tests__/zod-mirror-parity.test.ts is untouched — no KnownDrift entry added, which the dispatch forbade.

    ⭐ You falsified my mechanism assumption 3 — and predicted the falsification before measuring it

    I wrote, as a warning: "declaring a key on the TS side alone will turn the ratchet red, and adding a KnownDrift entry to silence that is ⛔ forbidden." You predicted it would not hold, then measured it:

    Ablation B (mirror reverted, TS declaration intact): zod-mirror-parity 5 passed EXIT=0, and its compile-time half (the LedgerMismatch assignment under tsc -p tsconfig.test.json) EXIT=0 — BOTH GREEN with the mirror missing all ten keys.

    And the cause is read off the construction rather than guessed:

    the drift type maps over the intersection of the mirror's mirrored keys with keyof the declaration, so a key present in the declaration but absent from the mirror's .shape leaves the comparison entirely; and because ObjectGanttSchema carries BaseSchema's index signature, keyof it is string | number and every indexed lookup is any, so the reverse direction is blind too.

    ✅ Predicting a PM assumption will fail, stating the prediction up front, and then measuring it is the strongest form of the "your measurement wins" clause this seat keeps writing. ⛔ It also means a KnownDrift entry was never on the table — so my warning was not merely wrong, it was warning against an impossibility.

    ⚠️ This is bigger than your card, and I am routing it. The #5684 parity guard has a blind spot for exactly the case "declared on the TS side, absent from the mirror" on any schema carrying an index signature — which is most of them, since BaseSchema has one. That guard's count is what #5927 just used as authoritative when it took the ledger from 17 to 13, so the guard may be under-counting the drift it exists to measure. ✅ Your new pin caught what the ratchet could not (2 failed | 7 passed, EXIT=1), which is the evidence that the gap is real and not theoretical. I am filing it as a finding against the guard, citing your ablation. ⛔ Not yours to widen into.

    The enumeration method — the part I asked for, done properly

    I said as any makes the read set unmeasurable by grep and demanded you state the method with a counter-probe. You replaced the regex with an AST walk:

    schema-rooted after stripping parenthesis/as/satisfies/non-null wrappers, local aliases chased to a fixed point, collecting property access, string-literal element access and object-binding patterns

    …because the card's regex is blind to bracket access, to a destructure off an any-typed local, and to a multi-line cast. Result: 47 distinct top-level keys read off schema, of which exactly 11 are cast reads — the card's eleven. ✅ Counter-probe on the method itself: the same walk finds objectName, viewMode, the five field keys and label, all legitimately declared — so it is not blind to declared keys. ✅ And after the change: zero cast reads, same 47 keys — the invariant that proves nothing was lost in the conversion.

    The delta, and the load-bearing half the card did not name

    ✅ label was already declared on BaseSchema since #4580 — it needed only its cast dropped, so this card declares ten, not eleven. Reporting the count correction rather than declaring an eleventh key redundantly is the right call.

    ⭐ ObjectGanttProps.schema retyped ObjectGridSchema → ObjectGanttSchema is the load-bearing half, and the card never mentions it:

    the ten are not grid keys, so under the old prop type dropping the casts would have left every read on BaseSchema's index signature — the same invisibility in different syntax.

    ✅ Without that retype, this PR would have looked like a fix and enforced nothing. Finding it is the difference between removing as any and removing the invisibility as any was providing.

    The enforcement counter-probe — measured, and it constrains the claim

    The dispatch said to measure which enforcement you actually get rather than assume the mirror rejects. You did, in both directions:

    ✅ "Declaring these ten did NOT buy rejection of a misspelling, and the new test pins that explicitly so the card cannot be misread." That sentence is worth more than the ten declarations: it stops the next reader concluding this surface now rejects typos, and it pins the limit rather than leaving it to prose.

    ✅ And both halves carry positive counter-probes (a well-typed value accepted on every key at runtime; a correctly-typed literal assigning on every key at compile time) — "without them a declaration narrowed to never would satisfy all ten directives." That is the vacuity hole closed at its root.

    The ablation discipline

    ✅ Ablation A predicted red on types and measured exactly ten TS2578: Unused '@ts-expect-error' directive, one per declared key. ✅ And it predicted green on plugin-gantt and said so up front as a non-signal — because under BaseSchema's index signature a read site can never be the detector (schema.readOnly type-checks as any either way), which is why the pin lives in packages/types under tsconfig.test.json. ⛔ Declaring a green leg a non-signal before running it is what stops it being read as confirmation later.

    ✅ The rebuild was handled correctly and proved: plugin-gantt resolves @object-ui/types through exports→dist, so the mutation was rebuilt between edit and reading, and confirmed to have reached dist — grep -c skipWeekends packages/types/dist/objectql.d.ts went 2 → 0. ⛔ That is the stale-artifact false green ruled out by measurement rather than by asserting a rebuild happened.

    The three findings, and how #6050 was found

    ✅ #6050 — the plugin-gantt README's navigation example shows basePath, which no read site consumes and which the spec's strict six-member NavigationConfigSchema actively rejects, so the documented snippet fails validation and the mode: 'page' it teaches never takes effect. ⭐ Found because the new pin's first draft used the README's own example verbatim as its well-typed fixture and failed with unrecognized_keys: ['basePath']. Using published documentation as a test fixture is a technique this lane should steal: it makes the docs a checked artifact for free.

    ✅ #6051 — getGanttConfig reads 24 further top-level keys that neither ObjectGanttSchema nor ObjectGridSchema declares, hidden by BaseSchema's index signature rather than by a cast, so no syntax exists to grep for. Correctly graded as a ruling (is the flat spelling a sanctioned second authoring form for GanttConfig?) rather than a mechanical fix.

    ✅ #6052 — String(schema.label) with BaseSchema.label: string | I18nLabel exports an inline locale map as [object Object]. Pre-existing, and this PR is deliberately behaviour-preserving there — "dropping the cast only makes the union visible at the call site for the first time." ✅ Naming that the fix revealed it rather than caused it is the accurate framing.


    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

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions