docs(test): record the traced diagnosis for the unlinked alias field edge

Traced to the end rather than left as "needs investigation", so the next
attempt starts from facts:

  1. Graph side is COMPLETE and symmetric with the interface —
     Property:...:LiveModeConfig.bookSlots is owner-qualified and carries
     HAS_PROPERTY.
  2. Resolution DOES reach resolveClassBindingForName('LiveModeConfig')
     (instrumented) and misses.
  3. It misses because the module scope binds LiveModeIface:Interface,
     renderAlias, renderIface — and not LiveModeConfig. The alias has no
     binding on the receiver's scope chain at all.
  4. The TS scope query tags aliases @declaration.type, but normalizeNodeLabel
     accepts only typealias / type_alias and has no "type" case, so it returns
     undefined. Kotlin and Dart use @declaration.type_alias; TypeScript is
     alone on the dead tag.
  5. Retagging is NECESSARY BUT NOT SUFFICIENT — tried, and the binding still
     does not appear, so a second gate exists in how a declaration anchored on
     a node that is ALSO a @scope.class anchor is attached: the alias appears
     to bind inside its own scope rather than hoisting to Module, where
     interface_declaration evidently does hoist.

An isShapeLike predicate (the nominal-vs-structural split: shapes declare
members, nominal types participate in MRO) plus a mirrored
findShapeBindingInScope were built and REVERTED along with the retag. With no
binding on the chain they never fire, and shipping inert widening is worse
than shipping none — the same standard applied to the earlier scope-index
attempt. The design is recorded here; it is worth doing once step 5 is fixed,
and it also unblocks Rust's parked union_item, which the MEMBER_OWNER_NODE_TYPES
comment documents as the same gap in another language.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ReidenXerx 2026-08-06 03:02:38 +03:00
parent 448868f891
commit 6664e5c0a5

View file

@ -77,15 +77,31 @@ describe('TypeScript type-alias and interface members (A4)', () => {
expect(readersOf('ifaceSlots')).toContain('renderIface');
});
// The ALIAS half still does not link, and the remaining blocker is now
// exact: resolving `cfg: LiveModeConfig` to its members requires the name
// `LiveModeConfig` to resolve to a CLASS-LIKE def, and `isClassLike` is
// Class|Interface|Struct|Record|Enum|Trait — no TypeAlias. That predicate is
// consulted from ~12 sites including MRO and heritage, and every language
// mints TypeAlias (Rust type_item, Kotlin/Swift/Dart typealias, C typedef),
// so widening it would enrol aliases in linearization where they do not
// belong. Widening only the scope index was tried and is NOT sufficient —
// the type-name walkers gate on it independently. Needs a deliberate
// "shape-like" concept rather than more call-site widening.
// ALIAS field -> consumer is still unlinked. Diagnosis, traced to the end so
// the next attempt starts from facts rather than from this list again:
//
// 1. The graph side is COMPLETE and symmetric with the interface:
// `Property:contracts.ts:LiveModeConfig.bookSlots` is owner-qualified
// and carries `HAS_PROPERTY LiveModeConfig->bookSlots`.
// 2. Resolution enters `resolveClassBindingForName('LiveModeConfig')`
// (verified by instrumentation) and misses.
// 3. It misses because the module scope binds `LiveModeIface:Interface`,
// `renderAlias`, `renderIface` — and NOT `LiveModeConfig`. The alias has
// no binding on the receiver's scope chain at all.
// 4. The TS scope query tags aliases `@declaration.type`, but
// `normalizeNodeLabel` accepts only `typealias` / `type_alias` and has
// no `type` case, so it returns undefined. Kotlin and Dart use
// `@declaration.type_alias`; TypeScript is alone on the dead tag.
// 5. Retagging it to `@declaration.type_alias` is NECESSARY BUT NOT
// SUFFICIENT — tried, and the binding still does not appear on the
// chain, so a second gate exists in how a declaration anchored on a
// node that is ALSO a `@scope.class` anchor gets attached (the alias
// appears to bind inside its own scope instead of hoisting to Module,
// where `interface_declaration` evidently does hoist).
//
// A widened predicate (`isShapeLike`) plus a mirrored
// `findShapeBindingInScope` were also built and REVERTED: with no binding on
// the chain they never fire, and shipping inert widening is worse than none.
// Fix step 5 first; the rest is then a small, testable change.
it.todo('links an alias field to its consumer');
});