Skip to content

spec: decide whether the entry-nameability invariant covers plain export REFERENCES, not just factory calls (27 pairs measured) #12416

Description

@huangyiirene

Observation filed while implementing #11986's gate. Filed unassigned, finding — this is a scope question for triage, not a defect report with an agreed fix.

#11986's gate probes the call surface: for each public entry it compiles export const c = E.<fn>(…) for every callable export and asserts the declaration emitter can name everything the result mentions. That is the shape all three decided instances took (#10868#11350#11709).

A second probe shape exists and was measured before the call shape was chosen: the plain reference, export const r = E.<name>; for every value export — which is what a consumer writes as export const mySchema = SomeSpecSchema; or when re-exporting a spec value. Measured on cdbd9204b65ae4ebec63f90f61669fb498efaa55, it finds 27 (entry, type) pairs across 11 of the 17 entries:

entryunnameable type(s)
@objectstack/specNavigationItem, ObjectStackRawIssue
@objectstack/spec/dataDerivationRule, FormField, IScopedContext
@objectstack/spec/systemBaseValidationRuleShape, FilterCondition, FormField, FormFieldInput
@objectstack/spec/kernelNavigationItem, NavigationItemInput
@objectstack/spec/aiFilterCondition, FormField, StateNodeConfig
@objectstack/spec/automationFormField
@objectstack/spec/apiBaseValidationRuleShape, FilterCondition, FormField, FormFieldInput, NavigationItem, NavigationItemInput, QueryAST, QueryInput
@objectstack/spec/uiFilterCondition
@objectstack/spec/securityFormField
@objectstack/spec/identityFormField
@objectstack/spec/sharedStrictObjectOptions

The question for triage: is this in scope for the invariant? Reading the ruling literally (「a type that appears structurally in an entry's public declarations must be nameable from that same entry」) it is — these types do appear structurally, and a consumer that re-exports the value hits the same TS2883. Against that: no consumer has reported one, whereas the call class produced nine reported build failures and three maintainer decisions. Closing all 27 is 27 re-export lines across 11 barrels plus an api-surface/ regeneration, and it widens the public type surface of six entries that have no pull for it.

Two further measurements that bear on the decision, both from the same run:

  1. Five entries (., /system, /api, /ui, /cloud) produce TS7056 under the reference shape — "the inferred type of this node exceeds the maximum length the compiler will serialize". Those probes are NOT MEASURED in either direction: the printer gives up before name resolution. So a reference-shape gate could not be made total the way the call-shape gate is; part of its surface is structurally unmeasurable with this instrument.
  2. Cost: the reference shape measured ~105s across all entries versus ~42s for the call shape (~31–47s in the shipped gate).

Deliberately not decided in #11986's PR. Adding these 27 to that gate's ledger would have traded its one strong property — a ledger with a one-line repair for every row it carries — for a 27-row accommodation surface with no shrink plan, which is the debt-laundering shape the ledger exists to refuse. The gate's docblock says so explicitly, so a later author does not "extend" it by reflex.

If triage decides the class is in scope, the natural shape is a second gate (or a second probe mode) with its own ledger, not a widening of this one.

Related: #11986 (the call-shape gate), #12414 (the seven live call-shape leaks), #11709, #11350.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions