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:

entry unnameable type(s)
@objectstack/spec NavigationItem, ObjectStackRawIssue
@objectstack/spec/data DerivationRule, FormField, IScopedContext
@objectstack/spec/system BaseValidationRuleShape, FilterCondition, FormField, FormFieldInput
@objectstack/spec/kernel NavigationItem, NavigationItemInput
@objectstack/spec/ai FilterCondition, FormField, StateNodeConfig
@objectstack/spec/automation FormField
@objectstack/spec/api BaseValidationRuleShape, FilterCondition, FormField, FormFieldInput, NavigationItem, NavigationItemInput, QueryAST, QueryInput
@objectstack/spec/ui FilterCondition
@objectstack/spec/security FormField
@objectstack/spec/identity FormField
@objectstack/spec/shared StrictObjectOptions

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.

Activity

  1. os-zhuang commented on Aug 27, 2026

    @os-zhuang
    Contributor

    Maintainer ruling recorded — Option A: the reference shape stays out of the invariant's scope

    Provenance: maintainer, 2026-08-27, PM chat decision-inbox batch 8 (adjudication session session_01DKWDdUJ2XNRESVVWUvcpnh), verbatim: 「以上同意」 — accepting recommendation A (2026-08-26T04:55Z facet block).

    Ruled: the entry-nameability invariant remains scoped to the CALL shape (the class with nine real build failures and three rulings). The reference shape is NOT adopted: zero consumer reports, the failure is already a loud compile-time error (TS2883, TS-native), a reference-shape gate is structurally half-buildable (five entries TS7056-unmeasurable), and closing the 27 pairs would widen six entries' public type surface for zero pull. ⛔ B (second gate + ledger) and ⛔ C (repair without a gate) declined.

    Reopen criterion, recorded: a single real consumer reporting a reference-shape TS2883 on a spec re-export reopens this card — the 27-pair census, the TS7056 boundary, and the "second gate with its own ledger, never a widening of #11986's" shape are all on file and reusable.

    Decision delivered; card closed (not planned — reopen free).


    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

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions