This is the maintained model documentation of the DevTools resolution pipeline: first the big picture and the view-model boundary beneath it, then the five detailed class-diagram views of the store model. The normative contract behind it is the resolution-model specification; this document describes what the code implements today.
The whole system answers one question:
A probe records what the page really declared. From that, pure functions compute: which package lands where for which consumer — and why?
Everything else is four stages, each answering exactly one question and each adding one guarantee:
flowchart LR
subgraph observe ["1 · Observe"]
snapshot["SnapshotV1<br/>raw, sanitized facts"]
end
subgraph order ["2 · Order"]
evidence["CanonicalRegistryEvidence<br/>every raw row addressable"]
end
subgraph derive ["3 · Derive"]
resolutions["EffectiveConsumerResolution<br/>what does the map deliver?"]
claims["Claims<br/>how does each registry row explain that?"]
copies["ResolvedDependencyCopy<br/>how many material copies exist?"]
bundles["Bundle and chunk claims<br/>which chunks belong to them?"]
end
subgraph publish ["4 · Publish"]
projection["CanonicalResolutionProjection<br/>one raw-free read surface"]
end
page["Page runtime +<br/>import maps"] --> snapshot
snapshot --> evidence
evidence --> resolutions --> claims --> copies --> bundles --> projection
projection --> views["Views and any future graph"]
- Observe — the collector probe reads the Native Federation globals and
document import maps and stores them as a
SnapshotV1: sanitized, but never interpreted. What was absent stays absent. - Order — ingest normalizes the raw repositories into
CanonicalRegistryEvidence: every raw row gets a deterministic ID and provenance. Nothing is merged, de-duplicated, or decided here. - Derive — a chain of pure functions computes the answer in four
sub-questions: the import-map outcome per consumer and specifier (the
atomic truth, with honest
mapped | unmapped | blocked | unknownstates), the claim explanations of every registry row against that outcome, the materially resolved dependency copies, and the bundle/chunk attribution. Nothing at this stage guesses: ambiguity stays visible as data. - Publish — one raw-free
CanonicalResolutionProjectionon the store model is the single surface views (and any future graph) read. No view re-derives winners, copy counts, or chunk ownership.
One boundary holds everywhere: the model proves what the captured import map resolves — never that the browser requested, downloaded, cached, or executed anything.
The projection is the model's publication format, not a render format: flat,
normalized collections that reference each other only by canonical ID, and
deliberately pivot-neutral. Each view therefore keeps one thin, pure
view-model builder (for example buildPackagesVm) between the projection and
its template. That layer exists for exactly two jobs:
- Joining and pivoting. Templates cannot join by ID. The builder indexes
the three read surfaces once (
resolutionProjection,effectiveConsumerResolutions,registryEvidence) and folds them into the view's shape: Packages pivots the consumer → copy → chunk spine on the package, Remotes pivots it on the consumer, Import Map on the map entries, a future graph on all edges. Because several presentations read the one truth, the projection must stay pivot-neutral — baking any render shape into it would color canonical evidence with UI decisions. - Presentation judgements. Display vocabulary and wording
(
skipped own 1.0.0,mapped only for X), deviation-first choices (what is a chip, what is a tooltip), ordering (elected copy first), and view-level indicators such as the resolved-tag-multiplicity glyph are view decisions, not domain facts — the actual conflict judgement belongs to diagnostics.
The boundary rule is strict in both directions and testable: a view-model builder groups and labels precomputed knowledge — it derives nothing new. It never computes winners, copy counts, roles, or chunk ownership; every field it emits chains to a canonical ID, and the builders are pure functions pinned by tests without a DOM. If a builder needs a fact the projection does not state, the fact belongs in the model — not in the view.
One captured SnapshotV1 becomes one FederationModel. The Store keeps the
captured registry declarations, remote scope URLs, and import-map evidence
separate until resolveEffectiveConsumerBindings joins them. The diagrams show
the resolution-relevant part of that model in five focused views maintained
next to each other: captured registry evidence first, effective resolution
second, declaration-claim explanations third, materialized resolved copies
fourth, and the published canonical projection fifth.
| Layer | Snapshot source | Store representation |
|---|---|---|
| Registry declarations | runtime.sharedExternals and runtime.scopedExternals |
CanonicalRegistryEvidence |
| Consumer scope context | runtime.remotes |
RemoteEntity[] |
| Import-map rules | importMaps.documentMaps |
EffectiveMap |
| Canonical result | The three inputs above plus channels.domImportMaps |
EffectiveConsumerResolution[] |
| Claim explanations | Registry evidence plus canonical results | ResolutionClaimsDerivation |
| Resolved copies | Mapped canonical results plus their claims | ResolvedDependencyCopy[] |
| Package measures | Registry evidence, claims, and resolved copies | PackageResolutionMeasures[] |
| Chunk groups | runtime.sharedChunks and @nf-internal/... records |
ChunkGroupProjection[] |
| Bundle claims | Resolved copies plus chunk groups | BundleClaim[] |
| Canonical projection | Every canonical layer above | CanonicalResolutionProjection |
| Existing view contract | Registry evidence plus canonical results | SharedParticipantRow[] |
This view answers who declared what? CanonicalRegistryEvidence stores the
records as flat, ordered arrays. The arrows show their logical parent/child ID
relationships; no winner or effective file is selected here.
classDiagram
direction LR
class FederationModel {
+CanonicalRegistryEvidence registryEvidence
}
class CanonicalRegistryEvidence {
+SharedExternalRecord[] sharedExternals
+VersionRegistration[] versionRegistrations
+ParticipantDeclaration[] participantDeclarations
+PrivateRegistration[] privateRegistrations
+EntrypointCandidate[] entrypointCandidates
+RegistryEvidenceDiagnostic[] diagnostics
}
class SharedExternalRecord {
+string shareScope
+string packageName
+boolean dirty
}
class VersionRegistration {
+string tag
+string rawAction
+RegistrationAction action
+boolean host
}
class ParticipantDeclaration {
+string participant
+string requiredVersion
+boolean strictVersion
+string? pool
+string? servedBy
}
class PrivateRegistration {
+string ownerRemote
+string packageName
+string tag
}
class EntrypointCandidate {
+string specifier
+string file
+string? candidateUrl
+CandidateUrlState candidateUrlState
}
class RegistryEvidenceDiagnostic {
+string code
+string message
+string rawValue
}
class EvidenceRef {
+"snapshot" source
+PathSegment[] path
+EvidenceState state
}
FederationModel "1" *-- "1" CanonicalRegistryEvidence : registryEvidence
CanonicalRegistryEvidence "1" *-- "0..*" SharedExternalRecord : shared roots
CanonicalRegistryEvidence "1" *-- "0..*" PrivateRegistration : private roots
CanonicalRegistryEvidence "1" *-- "0..*" RegistryEvidenceDiagnostic : diagnostics
SharedExternalRecord "1" --> "0..*" VersionRegistration : ordered IDs
VersionRegistration "1" --> "0..*" ParticipantDeclaration : ordered IDs
ParticipantDeclaration "1" --> "0..*" EntrypointCandidate : ordered IDs
PrivateRegistration "1" --> "0..*" EntrypointCandidate : ordered IDs
CanonicalRegistryEvidence ..> EvidenceRef : every record cites snapshot paths
This view answers where would that declaration resolve at the consumer's
scope root? The package name, consumer, normalized remote scope URL, and
effective import map meet only in EffectiveConsumerResolution. The scope URL
is a lookup context, not an observed importer-module URL.
classDiagram
direction LR
class FederationModel {
+CanonicalRegistryEvidence registryEvidence
+EffectiveMap effectiveMap
+RemoteEntity[] remotes
+EffectiveConsumerResolution[] effectiveConsumerResolutions
+SharedParticipantRow[] sharedRows
}
class CanonicalRegistryEvidence {
+SharedExternalRecord[] sharedExternals
+ParticipantDeclaration[] participantDeclarations
}
class SharedExternalRecord {
+string packageName
}
class ParticipantDeclaration {
+string participant
}
class RemoteEntity {
+string name
+string resolvedScopeUrl
}
class EffectiveMap {
+Map imports
+Map scopes
+Map integrity
}
class EffectiveConsumerResolution {
+string id
+string scopeContextKey
+string? consumerScopeUrl
+string specifier
+ResolutionStatus status
+string[] consumerRemotes
+string? targetUrl
+boolean? hasIntegrity
+EffectiveMapEntryProvenance? mapEntry
+BlockedReason? blockedReason
+UnknownReason[]? unknownReasons
}
class EffectiveMapEntryProvenance {
+string? scope
+string specifier
+string target
+MatchKind match
}
class SharedParticipantRow {
<<compatibility projection>>
+string participant
+string packageName
+EffectiveResolution? resolution
}
FederationModel "1" *-- "1" CanonicalRegistryEvidence : registryEvidence
FederationModel "1" *-- "0..*" EffectiveConsumerResolution : canonical results
CanonicalRegistryEvidence "1" --> "0..*" SharedExternalRecord : package claims
CanonicalRegistryEvidence "1" --> "0..*" ParticipantDeclaration : consumer claims
SharedExternalRecord ..> EffectiveConsumerResolution : package specifier
ParticipantDeclaration "1..*" --> "1" EffectiveConsumerResolution : consumer claim
RemoteEntity --> EffectiveConsumerResolution : consumer scope context
EffectiveMap --> EffectiveConsumerResolution : scoped exact / prefix lookup
EffectiveConsumerResolution "1" o-- "0..1" EffectiveMapEntryProvenance : mapped or blocked
ParticipantDeclaration "1" ..> "1" SharedParticipantRow : registry context
EffectiveConsumerResolution "1" ..> "1..*" SharedParticipantRow : resolution projection
Read the first two views from evidence to result: normalizeRegistryEvidence
retains every captured declaration and its snapshot paths; mergeDocumentMaps
normalizes the document tags into one EffectiveMap; remote scope URLs provide
the scope-root lookup context. The resolver evaluates one binding per scope
context and specifier — every declaration's registry package plus each
candidate specifier, including private registrations — and records exactly one
honest outcome. Modules under a more specific map scope or outside the remote
scope can resolve differently:
mappedmeans a matching, valid import-map binding supplies the normalized target.unmappedmeans the map and consumer scope context are known, but no applicable import-map binding exists.blockedmeans a matching binding exists, but its target is unusable (for example an invalid URL or prefix expansion); the entry and exact reason are retained.unknownmeans the map channel or consumer scope evidence is missing.
SharedParticipantRow is only a one-way projection for existing views. It is
never input to canonical resolution. A mapped result describes what the
captured map would bind — not the browser's complete URL fallback behavior, nor
what it requested, downloaded, or executed. In particular, a URL-like
specifier without a matching map entry remains unmapped here even though the
browser can resolve that URL without an import-map binding. The
registry-specific rationale and invariants remain in the
Task 1 design notes, without
another copy of the diagrams.
This view answers how does each declaration explain the computed binding?
Every shared or private entrypoint candidate creates one
DeclarationResolutionClaim against the already computed
EffectiveConsumerResolution; several claims may point at the same binding
without duplicating it. The layer is a pure derivation
(deriveResolutionClaims) over the two views above; a later task publishes its
output on the store model.
classDiagram
direction LR
class VersionRegistration {
+string tag
+RegistrationAction action
}
class ParticipantDeclaration {
+string participant
+string? servedBy
}
class PrivateRegistration {
+string ownerRemote
}
class EntrypointCandidate {
+string specifier
+string? candidateUrl
}
class EffectiveConsumerResolution {
+ResolutionStatus status
+string? targetUrl
}
class RegistryServingSlotClaim {
+SlotStatus status
+ParticipantDeclarationId? declarationId
}
class DeclarationResolutionClaim {
+string consumerRemote
+string consumerRegistryPackage
+string specifier
+string? ownCandidateUrl
+boolean? ownCandidateSelected
+ClaimMappingState mappingState
+SourceAction sourceAction
+ResolvedDependencyCopyId? copyId
}
class ObservedTargetProvider {
+string? remote
+SourceMatchOutcome outcome
+AttributionRule rule
}
class SourceMatch {
+SourceMatchOutcome outcome
+ResolutionSubject? source
}
class SourceComparison {
+SourceComparisonKind kind
+QualifiedSourceClaim left
+QualifiedSourceClaim right
+ComparisonStatus status
}
VersionRegistration "1" --> "1" RegistryServingSlotClaim : stored-order basis slot
ParticipantDeclaration "1" --> "0..*" DeclarationResolutionClaim : one per candidate
PrivateRegistration "1" --> "0..*" DeclarationResolutionClaim : one per candidate
EntrypointCandidate "1" --> "1" DeclarationResolutionClaim : own candidate
DeclarationResolutionClaim "1..*" --> "1" EffectiveConsumerResolution : explains binding
EffectiveConsumerResolution "1" --> "1" ObservedTargetProvider : attribution ladder
EffectiveConsumerResolution "1" --> "1" SourceMatch : observed source
DeclarationResolutionClaim "1" *-- "1..3" SourceComparison : slot / anchor / candidate
SourceComparison ..> RegistryServingSlotClaim : left registry claim
SourceComparison ..> ObservedTargetProvider : right observed source
mappingState explains the claim with one normative precedence; the
independent ownCandidateSelected flag records exact own-candidate equality
separately:
anchored—servedByis present and the target matches an anchor candidate of that remote within the share scope, across external records;servedBy === consumerRemoteis a valid self-anchor.self-filled— askipclaim resolves to the unique matchingskipcandidate of the same shared external (its own declaration or the first filler of the entry).own-selected— the target exactly equals the claim's own candidate URL.fallback— askipclaim resolves to the unique matchingsharesource of the same shared external; the raw action is retained.not-selected— an own candidate exists, the target differs, and no rule above explains it (the visiblemfe2claim inco-declared-share).blocked— the binding is terminally blocked: nothing is selected and no source is attributed.unknown— map, consumer scope, candidate URL, source match, or explanation is missing or ambiguous.
The observed side stays qualified: exact candidate equality outranks
scope-prefix ownership, the host never outranks a matching remote, and
ambiguity retains all candidates instead of choosing one. Comparisons never
collapse the sides — only slot-vs-observed, anchor-vs-observed, and
candidate-vs-target exist, with the registry-side claim always on the left —
and a mismatch is data, not permission to overwrite either fact. No mapping
state or attribution outcome proves that anything was requested, downloaded,
or executed.
This view answers which materially resolved instances of a dependency exist,
and who uses them? Only mapped effective resolutions and their claims
materialize a ResolvedDependencyCopy — candidates and participant membership
alone never do. Sharing success reads as convergence: many registrations,
declarations, and claims may end in one copy, and one registration can yield
zero, one, or several copies. The layer is a pure derivation
(materializeResolvedCopies, aggregatePackageMeasures) over the views
above; ingest publishes its output through the canonical projection (view 5).
classDiagram
direction LR
class VersionRegistration {
+string tag
+RegistrationAction action
}
class ParticipantDeclaration {
+string participant
}
class PrivateRegistration {
+string ownerRemote
}
class EffectiveConsumerResolution {
+ResolutionStatus status
+string? targetUrl
}
class SourceMatch {
+SourceMatchOutcome outcome
+ResolutionSubject? source
}
class DeclarationResolutionClaim {
+ClaimMappingState mappingState
+ResolvedDependencyCopyId? copyId
}
class ResolvedDependencyCopy {
+ResolvedCopySource source
+string? sourcePackage
+string? resolvedTag
+ResolvedCopySourceDisposition sourceDisposition
+SourceAction[] sourceActions
+ResolvedCopyEffectiveRole[] effectiveRoles
+Map entrypoints
+SourceRegistrationRef[] sourceRegistrationRefs
+BundleClaimId[] bundleClaimIds
}
class ResolvedCopyResolutionContext {
+ResolutionDomain resolutionDomain
+string consumerRegistryPackage
+ClaimId[] claimIds
}
class PackageResolutionMeasures {
+string packageName
+int registrationCount
+int distinctDeclaredTagCount
+int resolvedCopyCount
+int distinctResolvedTagCount
+int unknownResolvedTagCopyCount
}
SourceMatch ..> ResolvedDependencyCopy : unique exact subject keys the copy
ParticipantDeclaration "1" --> "0..1" ResolvedDependencyCopy : source-record identity
PrivateRegistration "1" --> "0..1" ResolvedDependencyCopy : source-record identity
VersionRegistration ..> ResolvedDependencyCopy : resolvedTag and sourceDisposition
EffectiveConsumerResolution "1..*" --> "0..1" ResolvedDependencyCopy : mapped members only
ResolvedDependencyCopy "1" *-- "0..*" ResolvedCopyResolutionContext : consumer uses
ResolvedCopyResolutionContext "1" --> "1..*" DeclarationResolutionClaim : claim IDs
DeclarationResolutionClaim "0..*" --> "0..1" ResolvedDependencyCopy : copyId
ResolvedDependencyCopy "0..*" ..> "1" PackageResolutionMeasures : outcome counts
Copy identity is hierarchical and source-oriented. A resolution whose target
uniquely and exactly matches one source's candidate takes that source-record
identity — the shared ParticipantDeclaration or PrivateRegistration —
grouping every mapped entrypoint of that source across all consumer contexts.
Anything else groups by the normalized target URL — the copy ID namespaces
that identity by snapshot — without claiming a source. Consumer share scope,
package, and claim IDs live only in resolutionContexts; an exact source copy
is never duplicated or relabeled because another consumer context or external
record points at one of its URLs. resolvedTag comes only from the uniquely
matched source registration — a URL-identified copy keeps it null rather
than borrowing a consumer's declared tag. After materialization,
attachCopyIds completes the claim contract: a mapped claim references its
copy through copyId, and unmapped, blocked, and unknown claims carry an
explicit null.
Source provenance and effective behavior stay separate axes:
sourceDispositionsays where the copy's bytes come from —share-registration,skip-registration, orscope-registrationfrom the unique source's raw action,private-registration,unknown-registration,ambiguous-source, ortarget-onlywhen nothing but the URL is evidenced. Consumer actions never change it.effectiveRoles(sorted, coexisting) derive from the attached claim and action evidence, never from consumer counts: a selectedsharesurface contributesordinary-sharedeven with a single consumer, ascopedeclaration's own mapping contributesisolated-own, a selected skip self-fill sourceself-filled-source, selection through an explicitservedByanchor-source, a private registration's own mappingprivate-own, and a copy no closed rule explains staysunclassified.sourceActionslists evidenced source actions only, never every consumer claim's action.
PackageResolutionMeasures keeps four package-level counts deliberately
separate — shared version registrations and their distinct declared tags
describe intent (private registrations are separate canonical records and
never fold into these), resolved copies and distinct resolved tags (plus
unknown-tag copies) describe outcome — with declaration and claim counts as
supporting measures. The aggregation has
no conflict field: equal-tag copy multiplicity alone is never reported as a
version conflict; that judgement needs distinct registration and tag evidence
and belongs to diagnostics. As everywhere in this model, a copy proves what
the captured map resolves, not that the browser requested, downloaded, or
executed the target.
This view answers what do views (and any future graph) read? Ingest runs
the complete canonical pipeline — claims, copies with attached copyId
links, chunk groups, bundle claims, package measures — and publishes one
raw-free CanonicalResolutionProjection on the store model
(buildCanonicalProjection). The projection never exposes SnapshotV1, the
raw repositories, or the compatibility sharedRows; its
effectiveResolutionId references resolve against the canonical
effectiveConsumerResolutions collection of view 2.
classDiagram
direction LR
class FederationModel {
+CanonicalResolutionProjection resolutionProjection
}
class CanonicalResolutionProjection {
+RemoteProjection[] remotes
+ResolvedDependencyCopy[] copies
+ConsumerCopyRelation[] consumerRelations
+ChunkGroupProjection[] chunkGroups
+BundleClaim[] bundleClaims
+DeclarationResolutionClaim[] declarationResolutionClaims
+RegistryServingSlotClaim[] registryServingSlotClaims
+ObservedTargetProvider[] observedTargetProviders
+SourceComparison[] sourceComparisons
+PackageResolutionMeasures[] packageMeasures
+ResolutionCompleteness completeness
}
class ConsumerCopyRelation {
+string consumerRemote
+ResolvedDependencyCopyId copyId
+ResolutionId[] effectiveResolutionIds
+ClaimId[] claimIds
+ClaimMappingState[] mappingStates
}
class ChunkGroupProjection {
+string emitterRemote
+ChunkGroupOrigin origin
+string? bundleName
+string? pseudoPackage
+string[] files
}
class BundleClaim {
+ResolvedDependencyCopyId copyId
+BundleClaimSource? source
+string? sourceRemote
+string bundle
+ChunkGroupId[] chunkGroupIds
+BundleClaimStatus status
}
class ResolvedDependencyCopy {
+ResolvedCopySourceDisposition sourceDisposition
+ResolvedCopyEffectiveRole[] effectiveRoles
+BundleClaimId[] bundleClaimIds
}
class ResolutionCompleteness {
+CompletenessCounts total
+Map byConsumer
+IncompleteConsumerResolution[] consumerIssues
}
class IncompleteConsumerResolution {
+string consumerRemote
+ResolutionId effectiveResolutionId
+ConsumerResolutionIssue[] issues
+ClaimId[] ambiguousClaimIds
}
FederationModel "1" *-- "1" CanonicalResolutionProjection : published by ingest
CanonicalResolutionProjection "1" *-- "0..*" ConsumerCopyRelation
CanonicalResolutionProjection "1" *-- "0..*" ChunkGroupProjection
CanonicalResolutionProjection "1" *-- "0..*" BundleClaim
CanonicalResolutionProjection "1" *-- "1" ResolutionCompleteness
ResolutionCompleteness "1" *-- "0..*" IncompleteConsumerResolution
ConsumerCopyRelation "0..*" --> "1" ResolvedDependencyCopy : copyId
BundleClaim "0..*" --> "1" ResolvedDependencyCopy : copyId
ResolvedDependencyCopy "1" --> "0..*" BundleClaim : bundleClaimIds
BundleClaim "0..*" --> "0..*" ChunkGroupProjection : chunkGroupIds
A ConsumerCopyRelation is keyed (consumerRemote, copyId) and retains all
supporting effective-resolution IDs, claim IDs, and mapping states — several
specifier paths between one consumer and one copy never collapse into a
first-edge-wins or a single boolean. A relation exists for every consumer
that resolves to the copy, even where a candidate-less declaration left the
binding without a claim.
Chunk groups are emitter-aware: identity retains the emitter remote, the
origin (shared-chunks or scoped-pseudo-external), and the bundle or
pseudo-package, so equal chunk filenames from different emitters never merge.
Bundle attribution follows the selected source only: a copy's uniquely
evidenced declaration or private registration claims its own bundle, an
identifiable servedBy anchor is that copy's source already, and a
non-selected bundle-bearing declaration donates nothing. A claim is
mapped-source only when registered shared-chunks evidence of
(sourceRemote, bundle) backs it; a declared bundle without registered
chunks stays source-only (named-share-scope and dynamic self-fill map the
file without registering its bundle — absence there is qualified evidence,
never "missing" or "downloaded"); an ambiguous-source copy surfaces every
candidate (sourceRemote, bundle) pair as ambiguous without attributing
chunks. Structural zero-entry bundle lists (mapping-or-exposed) contribute
nothing, and legacy @nf-internal/... carriers keep their raw provenance as
pseudo-external chunk groups outside dependency attribution.
Completeness counts each unique binding once: total reports unknown,
unmapped, and blocked bindings plus ambiguous source claims without double
counting a binding shared by several consumer contexts. Both ambiguity kinds
count as ambiguous source claims — ambiguous exact candidates count each
affected declaration claim (listed in ambiguousClaimIds), and an ambiguous
scope attribution counts its single ambiguous provider claim per binding
(issued to every consumer, with no declaration claim to name). byConsumer
counts per consumer remote — every published remote appears with explicit,
possibly zero, counts — and overlaps by design; it must never be summed into
a unique-binding total. A consumer-filtered view derives its warnings from
byConsumer and consumerIssues, de-duplicating by effective-resolution and
claim ID. As everywhere in this model, the projection proves what the
captured map resolves — never wire cost, downloads, cache hits, or execution.