Summary
When a subgraph's SDL omits the fedspec @link (i.e. no @link(url: "https://specs.apollo.dev/federation/v2.x", ...)), composition classifies it as Federation 1 and auto-upgrades it to Fed 2 in-memory before merge. That upgrade applies @shareable to overlapping value types and non-key entity fields. Composition then succeeds with no CompositionHint emitted about any of this.
As a result, two "identical" Fed 1 subgraphs (same entity types, same fields, different subgraph names) can be published to the same variant and compose cleanly. From a user's perspective this looks like it shouldn't work — Fed 1 single-ownership rules seem to forbid it — but the upgrade-to-Fed 2 path silently rewrites the semantics. There is no surface signal to the operator that any of this happened.
Reproduction sketch
- Author two subgraphs with identical object-type definitions (including
@key types), none declaring @link(url: ".../federation/v2.x").
- Publish under two different subgraph names to the same variant.
- Compose.
- Observe: composition succeeds, no hints.
What the code does today
Confirmed against main @ 9283b4c4:
internals-js/src/federation.ts FederationMetadata.isFed2Schema() returns false when there is no @link to specs.apollo.dev/federation/v2.x.
composition-js/src/compose.ts:150 unconditionally calls upgradeSubgraphsIfNecessary(subgraphs) before merge.
internals-js/src/schemaUpgrader.ts:709 addShareable() applies @shareable to overlapping non-key entity fields (L739) and whole value types (L746). Each application is recorded as a ShareableFieldAddition / ShareableTypeAddition on upgradeResult.changes.
compose() only reads upgradeResult.subgraphs and upgradeResult.errors. upgradeResult.changes is never consumed — the audit trail is black-holed.
composition-js/src/merging/merge.ts:468 IMPLICITLY_UPGRADED_FEDERATION_VERSION is the closest existing hint, but it only fires when a linked core feature's minimum version forces a bump within Fed 2.x. A subgraph with no @link at all has no core features, so the loop never finds a featureCausingUpgrade and no hint is pushed.
Request
Two concrete gaps:
-
Emit a composition hint when a subgraph is auto-upgraded from "no fedspec" to Fed 2. The existing IMPLICITLY_UPGRADED_FEDERATION_VERSION pathway covers directive-driven Fed 2.x → 2.y bumps but misses the far more consequential case where a subgraph is classified Fed 1 purely because it never declared a fedspec. An operator should be told this happened — something like IMPLICITLY_UPGRADED_FROM_FED1 with the subgraph name and the detected version.
-
Surface ShareableFieldAddition / ShareableTypeAddition changes from the upgrade as composition hints (or otherwise expose them). Today they exist on the UpgradeResult but are never read. These are exactly the silent-semantics-change audit trail that users need when diagnosing "why did these two subgraphs compose."
Either change alone would have made the customer-facing issue here trivially diagnosable instead of requiring a trip through the composition source.
Customer context
Surfaced by an enterprise customer's platform team investigating unexpected successful composition of two subgraphs with identical entity types under different names. Their diagnosis was correct — missing fedspec @link, silent upgrade — but required reading the composition code to confirm. Their mitigation is now a pre-publish check in their internal schema-validation tooling that requires every subgraph to declare a Fed 2 @link. That works, but the predictability gap is real and general.
Summary
When a subgraph's SDL omits the fedspec
@link(i.e. no@link(url: "https://specs.apollo.dev/federation/v2.x", ...)), composition classifies it as Federation 1 and auto-upgrades it to Fed 2 in-memory before merge. That upgrade applies@shareableto overlapping value types and non-key entity fields. Composition then succeeds with noCompositionHintemitted about any of this.As a result, two "identical" Fed 1 subgraphs (same entity types, same fields, different subgraph names) can be published to the same variant and compose cleanly. From a user's perspective this looks like it shouldn't work — Fed 1 single-ownership rules seem to forbid it — but the upgrade-to-Fed 2 path silently rewrites the semantics. There is no surface signal to the operator that any of this happened.
Reproduction sketch
@keytypes), none declaring@link(url: ".../federation/v2.x").What the code does today
Confirmed against
main@9283b4c4:internals-js/src/federation.tsFederationMetadata.isFed2Schema()returns false when there is no@linktospecs.apollo.dev/federation/v2.x.composition-js/src/compose.ts:150unconditionally callsupgradeSubgraphsIfNecessary(subgraphs)before merge.internals-js/src/schemaUpgrader.ts:709addShareable()applies@shareableto overlapping non-key entity fields (L739) and whole value types (L746). Each application is recorded as aShareableFieldAddition/ShareableTypeAdditiononupgradeResult.changes.compose()only readsupgradeResult.subgraphsandupgradeResult.errors.upgradeResult.changesis never consumed — the audit trail is black-holed.composition-js/src/merging/merge.ts:468IMPLICITLY_UPGRADED_FEDERATION_VERSIONis the closest existing hint, but it only fires when a linked core feature's minimum version forces a bump within Fed 2.x. A subgraph with no@linkat all has no core features, so the loop never finds afeatureCausingUpgradeand no hint is pushed.Request
Two concrete gaps:
Emit a composition hint when a subgraph is auto-upgraded from "no fedspec" to Fed 2. The existing
IMPLICITLY_UPGRADED_FEDERATION_VERSIONpathway covers directive-driven Fed 2.x → 2.y bumps but misses the far more consequential case where a subgraph is classified Fed 1 purely because it never declared a fedspec. An operator should be told this happened — something likeIMPLICITLY_UPGRADED_FROM_FED1with the subgraph name and the detected version.Surface
ShareableFieldAddition/ShareableTypeAdditionchanges from the upgrade as composition hints (or otherwise expose them). Today they exist on theUpgradeResultbut are never read. These are exactly the silent-semantics-change audit trail that users need when diagnosing "why did these two subgraphs compose."Either change alone would have made the customer-facing issue here trivially diagnosable instead of requiring a trip through the composition source.
Customer context
Surfaced by an enterprise customer's platform team investigating unexpected successful composition of two subgraphs with identical entity types under different names. Their diagnosis was correct — missing fedspec
@link, silent upgrade — but required reading the composition code to confirm. Their mitigation is now a pre-publish check in their internal schema-validation tooling that requires every subgraph to declare a Fed 2@link. That works, but the predictability gap is real and general.