Skip to content

finding(types): BaseSchema closes with [key: string]: any (packages/types/src/base.ts:467), so NO annotation on any node schema can catch a misspelled metadata key — measured green by a planted probe while the type, optionality and payload-member probes all went red #7927

Description

@claude

Finding (observation, awaiting first grading). Measured by the os-dev seat working objectui#5174 batch 13 (PR to follow). ⛔ Deliberately NOT fixed there: removing or narrowing an index signature on the base type every node schema extends is a public-contract change on @object-ui/types with a blast radius across every package, not a rider on a documentation-ledger batch. Unassigned and bare — domain:* and grading are triage's.

What is true today (measured at origin/main db2c20d08)

packages/types/src/base.ts:467 closes BaseSchema (lines 70–468) with:

  /**
   * Additional properties specific to the component type.
   * This index signature allows type-specific extensions.
   */
  [key: string]: any;

Every authored node schema in the system extends BaseSchema. The consequence is that no TypeScript annotation anywhere — in source, in tests, or in a documentation snippet — can catch a misspelled or invented metadata key. The annotation checks the key's type and never its name.

The measurement, not an inference

Batch 13 annotated packages/plugin-calendar/README.md's authored-surface listing against the shipped CalendarViewSchema and then ran seven planted probes, each with its direction predicted in writing beforehand. Six were predicted RED and came back RED. One was predicted GREEN, and came back GREEN:

  • P5 — inside the annotated block, rename the key titleField to titleFieldd. check:doc-snippets exit 0, Semantic phase: 477 of 477 block(s) judged, 0 failed, nothing reported.

The complement bounds it from the other side, so this is not a claim that nothing is checked:

  • P1 — view?: CalendarViewMode narrowed to view?: 'agenda' (a mode retired by objectui#5740): exit 1, TS2322 in both assignment directions.
  • P7 — data?: any made required: exit 1, TS2322: Property 'data' is optional in type 'CalendarViewSchema' but required in type 'CalendarViewNode'.
  • P6 — event.title misspelled event.titel on the contextually-typed CalendarEvent payload: exit 1, TS2551. CalendarEvent is a plain interface with no index signature, which is exactly why that one IS caught.

⇒ Types are checked. Optionality is checked. Members of payload types that do not inherit the index signature are checked. Key spelling on any BaseSchema descendant is not, and cannot be.

Why this is worth a card rather than a footnote

  1. It is the reason a whole class of gate is structurally blind. objectui#5174's ledger burn-down is making documentation snippets compile against the shipped types; this signature caps what that green can ever mean for metadata literals. Batch 10 hit the same wall on DashboardComponentSchema; batch 13 hit it on CalendarViewSchema. Neither is one type's defect — both inherit it.
  2. The repo already ruled on this exact shape one level down. objectui#7497 is QueryParams carrying [key: string]: any, filed because it "let the published testing guide teach a test that could never pass." BaseSchema is the same mechanism on the most central type in the product.
  3. It runs against AGENTS.md §5 #0.1 and Fix documentation deployment for www.objectui.org #6. #0.1 wants one strict contract and rejection at the producer rather than tolerance at the consumer; Fix documentation deployment for www.objectui.org #6 is "type safety over magic — no any". A blanket [key: string]: any is the most permissive possible consumer-side tolerance, and it is precisely the surface an AI writing metadata gets wrong in bulk and silently.
  4. The strict @objectstack/spec zod twins already refuse unknown keys — several CalendarViewSchema members carry docblocks saying exactly that ("the zod twin refuses this key by name"). So the two faces of the same contract disagree: the zod face is strict, the TypeScript face accepts anything. Whichever is right, they should not differ silently.

Scope, if this is graded for work

  1. Measure first, do not delete first: git grep every site that relies on an undeclared key riding BaseSchema's passthrough. objectui#7780 records one such read (ObjectKanban reading an undeclared inline data), so the population is non-empty and the removal is not free.
  2. Decide the direction deliberately per AGENTS.md §5 #0.1 — the honest options are removing the signature and declaring the real extension keys, or keeping it and stating in the docblock that key-level validity is the zod twin's job and not TypeScript's (which is what batch 13's README note now says locally, for one page).
  3. Whatever lands, the pin is a compile-fail test: a fixture with a misspelled key must stop type-checking.

Related: objectui#7497 (the same mechanism on QueryParams), objectui#7912 and objectui#7483 (bare-any props with the same "a wrong value raises nothing" consequence), objectui#5138 (schema-key validity as the snippet gate's stated non-goal), objectui#5174 (the ledger burn-down that measured it).


Generated by Claude Code

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions