Skip to content

Offer surface names to importers - #100

Open
yenda wants to merge 1 commit into
juxt:mainfrom
yenda:fix/offer-surfaces-to-importers
Open

yenda wants to merge 1 commit into
juxt:mainfrom
yenda:fix/offer-surfaces-to-importers

Conversation

@yenda

@yenda yenda commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #98.

Bug

A surface's related: clause is the documented construct for linking to another surface, and qualified names are the documented cross-module reference form — but related: merge/MergeWizard(expr) warns allium.reference.unknownName ("which imported module 'merge' does not define") while the module visibly declares surface MergeWizard. The offered-names set (collect_referenced_trigger_names) holds declared types, referenced triggers, config and deferred roots (#89/#91) — never surfaces. The checker demonstrably knows the construct: the same link written unqualified draws allium.surface.relatedUndefined locally.

Fix

Offer each surface block's name, exactly as #91 added config and deferred roots to this same function. collect_declared_names is untouched, so unqualified resolution and the ambiguous-import machinery are unaffected; the blast radius is qualified-reference membership only.

Disclosed side effects (membership is a name-existence check, not a kind check): any qualified reference to a declared surface name stops warning — including positions where a surface makes no sense, e.g. a when: subscription, and a default literal typed to an imported surface (default merge/MergeWizard fallback = { ... }), which loses its only flag with nothing replacing it, since surfaces have no field schema for the imported-default validation to check. Both are consistent with entity/config/deferred names already being offered, and both are pinned by deliberate tests so a future kind-aware refinement flips them consciously.

Tests

+6 integration tests in cross_module_lifecycle.rs (qualified related link resolves with zero unknownName/relatedUndefined; merged single-file oracle accepts the link; a link to an undeclared surface still warns; an unqualified link stays local; both disclosed side effects pinned) and +1 unit test on the offering set. Behaviour spec updated (four new rules + the offered-set prose); CHANGELOG entry under Unreleased. Full workspace suite green (668 tests); clippy warning multiset identical to main; the repo's own docs/project/specs diagnostics byte-identical before/after.

Real-world impact

In a spec family of any size, the surface worth linking to often lives in another module: a worklist that hands each row off to a wizard another spec owns, a summary view linking to a detail view. That hand-off is exactly what related: alias/Wizard(expr) expresses — and today it cannot be written without a warning that fails the gate. The available options are muting allium.reference.unknownName line by line (with a comment explaining the checker rather than the domain) or dropping the link and losing the navigation contract from the spec.

Supporting evidence from a private ~190-spec corpus we maintain: one such cross-module hand-off (a worklist surface offering an account-merge wizard owned by another module) carries exactly that suppression today, and a full-corpus diagnostic diff with this fix shows precisely that one warning disappearing and nothing else changing. This change only removes warnings; it cannot turn a previously green gate red.


Note: this PR and the dotted-member-access one both touch analysis.rs, the check behaviour spec, and the CHANGELOG - whichever merges second needs a trivial union rebase; happy to rebase promptly.

A related: entry naming an imported module's surface
(related: alias/Surface(expr)) drew a false allium.reference.unknownName
because collect_referenced_trigger_names never offered surfaces. Offer
each surface block's name, exactly as config and deferred roots already
are; collect_declared_names is untouched so unqualified resolution and
the ambiguous-import machinery are unaffected. Membership stays a
name-existence check, not a kind check: any qualified reference to a
declared surface name now resolves, related: or not.

One consequence is disclosed and pinned: a default literal typed to an
imported surface loses its only diagnostic and gets no field validation,
since surfaces carry no field schema (deferred-root-typed defaults
already behave this way).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check: a qualified related-surface link (related: alias/Surface(item)) warns unknownName because surfaces are never offered to importers

1 participant