Skip to content

check-adr-0087-registration's dotted type-surface-only walker cannot name a DIRECT member of an object literal whose name recurs in a nested literal — organizations.create is refused as AMBIGUOUS with organizations.teams.create, and no deeper path exists for the direct one #16571

Description

@claude

Found while writing the changeset for #14314 (card 3 of the #12104 family). The gate is out of that card's scope, so recorded here rather than patched there. No assignee, no label — left for triage.

The declared grammar

scripts/check-adr-0087-registration.mjs, parseSymbolRef header (the #15627 widening): a member that is not unique in the file is named "as a DOTTED MEMBER PATH through the object-literal nesting the member sits in", and the fix text the gate prints says the same: "Name a deeper path that resolves to exactly one."

What the walker does

resolveMemberPath narrows the search to the bytes between the braces of the named nesting (organizations = { ... }), then memberDefinitionsIn(text, masked, symbol, from, to) collects EVERY definition of symbol whose name starts inside that region — at any depth. So for a container that holds both a direct member and a same-named member inside a nested literal, the direct member has NO addressable spelling:

type-surface-only packages/client/src/index.ts#organizations.create
  -> `organizations.create` is AMBIGUOUS: 2 `create` definitions sit inside `organizations`.
     Name a deeper path that resolves to exactly one.

The two definitions are organizations.create itself (direct) and organizations.teams.create (nested). The nested one IS addressable (organizations.teams.create resolves); the direct one is not, and there is no deeper path to name because it sits at the depth the path already names. Measured on PR #14314's changeset at b66530f33c: five of nineteen refs refused this way — organizations.create, organizations.update, organizations.listMembers, organizations.delete, organizations.removeMember (each has a same-named member under organizations.teams or organizations.invitations); the other fourteen resolve.

Why it matters

The category's whole content is WHICH surface moved off any, and a narrowing of a direct member with a nested namesake is a common SDK shape (create / update / delete / list* recur at every level of packages/client/src/index.ts). Today such a member can only be left out of the marker and described in prose, which is exactly the unchecked sentence #15627 widened the grammar to avoid.

Fix shape (not decided here)

Resolve a dotted path's LAST segment at the region's top depth only: a definition whose name starts inside [from, to) but inside a nested object literal opened within that region belongs to the nested path, not to this one. objectLiteralBodiesFor already knows every nested literal's [open, close], so "top depth" is "not inside any of those spans". With that rule organizations.create lands on the direct member and organizations.teams.create on the nested one, and the AMBIGUOUS refusal stays for the case it was written for (a segment that opens several literals). Needs a --self-test case pinning both directions (TSO-D12 / TSO-D13 are the neighbours).

Refs: #14314 (where it was hit), #15627 (the widening whose reader this extends).


Generated by Claude Code

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

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions