Skip to content

Conversation

@Aenimus
Copy link
Member

@Aenimus Aenimus commented Sep 16, 2025

Summary by CodeRabbit

  • New Features

    • Adds support for a new @semanticNonNull field directive: parsing LEVELS, per-subgraph level tracking, and emission into federated and client schemas; utilities to generate directive nodes and derive node coordinates; expanded handling for non-repeatable persisted directives.
  • Bug Fixes

    • Clear validation messages for NaN, out-of-bounds, non-null conflicts, and inconsistent levels across subgraphs.
  • Tests

    • New comprehensive normalization and federation tests covering parsing, validation, persistence, and cross-subgraph consistency.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

@coderabbitai
Copy link

coderabbitai bot commented Sep 16, 2025

Walkthrough

Adds support for a new @semanticNonNull(levels: [Int!]! = [0]) directive: parsing and validation during normalization, per-subgraph null-level tracking on FieldData, federation/client emission of a generated directive, new error helpers/types/constants/utilities, and tests.

Changes

Cohort / File(s) Summary
Errors & Types
composition/src/errors/errors.ts, composition/src/errors/types.ts
New exported error-message helpers and a public error constructor for semanticNonNull validation (semanticNonNullLevelsNaNIndexErrorMessage, semanticNonNullLevelsIndexOutOfBoundsErrorMessage, semanticNonNullLevelsNonNullErrorMessage, semanticNonNullArgumentErrorMessage, semanticNonNullInconsistentLevelsError) and added types SemanticNonNullLevelsIndexOutOfBoundsErrorParams and SemanticNonNullLevelsNonNullErrorParams.
Schema-building Types
composition/src/schema-building/types.ts
FieldData gains nullLevelsBySubgraphName: Map<SubgraphName, Set<number>>.
Schema-building Utils
composition/src/schema-building/utils.ts
Expose SEMANTIC_NON_NULL, add NON_REPEATABLE_PERSISTED_DIRECTIVES, emit generated semantic-non-null directive for FieldData in client schema, and widen isFieldData signature to accept `ChildData
String Constants
composition/src/utils/string-constants.ts
Added CHANNEL, CHANNELS, CONSUMER_INACTIVE_THRESHOLD, LEVELS, SEMANTIC_NON_NULL; added NON_REPEATABLE_PERSISTED_DIRECTIVES (includes INACCESSIBLE, SEMANTIC_NON_NULL); included SEMANTIC_NON_NULL in PERSISTED_CLIENT_DIRECTIVES; relocated REQUIRE_FETCH_REASONS.
General Utils
composition/src/utils/utils.ts
Added generateSemanticNonNullDirective(levels: Set<number>): ConstDirectiveNode; removed getSingleSetEntry and added generalized `getFirstEntry<K,V>(hashSet: Set
Federation (v1)
composition/src/v1/federation/federation-factory.ts
Wire SEMANTIC_NON_NULL support and definition, track nullLevelsBySubgraphName and referencedPersistedDirectiveNames: Set<DirectiveName>, validate cross-subgraph level consistency (validateSemanticNonNull), inject generated directives into flattened persisted directives, and multiple public/type signature updates to use DirectiveName/TypeName.
Normalization (v1)
composition/src/v1/normalization/normalization-factory.ts, composition/src/v1/normalization/types.ts, composition/src/v1/normalization/directive-definition-data.ts, composition/src/v1/normalization/utils.ts
Parse and validate @semanticNonNull(levels: ...) on fields, add handleSemanticNonNullDirective and HandleSemanticNonNullDirectiveParams, register SEMANTIC_NON_NULL directive-definition data, validate level entries (NaN, out-of-bounds, non-null conflicts), and store per-subgraph level sets on FieldData.
Constants (v1)
composition/src/v1/utils/constants.ts
Added SEMANTIC_NON_NULL_DEFINITION (argument levels: [Int!]! = [0]) and included SEMANTIC_NON_NULL in built-in directive names.
Utils (v1)
composition/src/v1/utils/utils.ts
Added getNodeCoords(data: NodeData): string.
Tests
composition/tests/v1/directives/semantic-non-null.test.ts, composition/tests/v1/utils/utils.ts
New test suite for normalization and federation behaviors of @semanticNonNull and added test helper directive/router definition strings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: add @semanticNonNull support" is a short, single-sentence, and directly summarizes the primary change (adding support for the @semanticNonNull directive); it is specific, readable, and uses a Conventional Commits prefix so teammates can quickly understand the main purpose of the PR.
✨ Finishing touches
  • 📝 Generate Docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Sep 16, 2025

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-6742b6f00aa9f568b464a33063d7570547bd977e

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (11)
composition/src/schema-building/types.ts (1)

111-111: Define and enforce invariants for nullLevelsBySubgraphName.

Please confirm normalization enforces:

  • levels are non‑negative integers,
  • de‑duplicated (Set helps) and within the field’s type nullability depth,
  • empty set semantics (unset vs. empty) are well‑defined.

Also verify any FieldData copy/merge paths deep‑copy this Map<SubgraphName, Set> to avoid aliasing.

composition/src/utils/utils.ts (2)

196-219: Sort numerically to avoid lexicographic mis-order (e.g., 10 before 2).

Current .sort() is string-based. Use a numeric comparator.

Apply:

-export function generateSemanticNonNullDirective(levels: Set<number>): ConstDirectiveNode {
-  const sortedLevels = Array.from(levels).sort();
+export function generateSemanticNonNullDirective(levels: Set<number>): ConstDirectiveNode {
+  const sortedLevels = Array.from(levels).sort((a, b) => a - b);

251-257: Nit: function name/param don’t match accepted types.

getSingleHashSetEntry takes a Set or Map; consider renaming to getFirstEntry (and hashSet -> collection) for clarity. No behavior change.

composition/src/errors/errors.ts (1)

1646-1657: Fix missing space and ensure numeric sort in message.

There’s no space before “is invalid…”, and .sort() is lexicographic.

Apply:

-export function semanticNonNullInconsistentLevelsError(data: FieldData): Error {
+export function semanticNonNullInconsistentLevelsError(data: FieldData): Error {
   const coords = `${data.renamedParentTypeName}.${data.name}`;
   let message =
-    `The "@semanticNonNull" directive defined on field "${coords}"` +
+    `The "@semanticNonNull" directive defined on field "${coords}" ` +
     `is invalid due to inconsistent values provided to the "levels" argument across the following subgraphs:\n`;
   for (const [subgraphName, levels] of data.nullLevelsBySubgraphName) {
-    message += ` Subgraph "${subgraphName}" defines levels ${Array.from(levels).sort()}.\n`;
+    message += ` Subgraph "${subgraphName}" defines levels ${Array.from(levels).sort((a, b) => a - b)}.\n`;
   }
composition/src/utils/string-constants.ts (1)

171-172: Include auth directives in client-exposed set or adjust comment elsewhere.

getClientPersistedDirectiveNodes comment says to include @authenticated and @requiresScopes, but PERSISTED_CLIENT_DIRECTIVES omits them.

Apply if exposure is intended:

-export const PERSISTED_CLIENT_DIRECTIVES = new Set<string>([DEPRECATED, SEMANTIC_NON_NULL]);
+export const PERSISTED_CLIENT_DIRECTIVES = new Set<string>([
+  AUTHENTICATED,
+  DEPRECATED,
+  REQUIRES_SCOPES,
+  SEMANTIC_NON_NULL,
+]);

If not intended, update the comment in getClientPersistedDirectiveNodes.

composition/src/schema-building/utils.ts (1)

458-465: Comment/behavior mismatch about client-visible directives.

Comment says include @authenticated/@requiresScopes, but the filter set currently excludes them. Align with constants or update the comment.

See suggested change in string-constants.ts.

composition/tests/v1/directives/semantic-non-null.test.ts (1)

15-15: Prefer project’s parse wrapper for consistent noLocation handling.

Use the internal parse util to align with other tests and avoid location noise: import { parse } from '../../../src';.

composition/src/v1/normalization/normalization-factory.ts (3)

692-699: Call the semanticNonNull handler once per directive, not once per argument.

Currently invoked inside the arguments loop, which re-calls the handler if future optional args are added. Move it after argument validation.

-      if (isSemanticNonNull && data.kind === Kind.FIELD_DEFINITION) {
-        this.handleSemanticNonNullDirective({
-          data,
-          directiveNode,
-          errorMessages,
-        });
-        continue;
-      }
+      // no-op here; handled once after the loop for @semanticNonNull
     for (const argumentNode of directiveNode.arguments) {
       ...
     }
+    if (isSemanticNonNull && isField) {
+      this.handleSemanticNonNullDirective({
+        data,
+        directiveNode,
+        errorMessages,
+      });
+    }

2175-2228: Don’t assume argument order; pick “levels” by name.

Indexing arguments![0] is brittle if directive arguments expand or reorder. Select by name to harden parsing.

-    const values = (directiveNode.arguments![0].value as ConstListValueNode).values as ReadonlyArray<IntValueNode>;
+    const levelsArg = directiveNode.arguments?.find((a) => a.name.value === 'levels');
+    if (!levelsArg || levelsArg.value.kind !== Kind.LIST) {
+      // types are validated earlier; if we get here, bail gracefully
+      return;
+    }
+    const values = (levelsArg.value as ConstListValueNode).values as ReadonlyArray<IntValueNode>;

162-165: Unreachable NaN branch is harmless but dead.

semanticNonNullLevelsNaNIndexErrorMessage can’t trigger because the directive arg is validated as Int. You may keep it for defense-in-depth or remove later.

composition/src/v1/federation/federation-factory.ts (1)

1731-1757: Directive emission path looks solid; minor robustness note on level sorting.

generateSemanticNonNullDirective sorts levels lexicographically. If levels ever reach double digits, prefer numeric sort.

To apply (in composition/src/v1/utils/utils.ts):

// change
const sortedLevels = Array.from(levels).sort();
// to
const sortedLevels = Array.from(levels).sort((a, b) => a - b);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33f870e and c2fd00a.

📒 Files selected for processing (16)
  • composition/src/errors/errors.ts (2 hunks)
  • composition/src/errors/types.ts (1 hunks)
  • composition/src/schema-building/types.ts (1 hunks)
  • composition/src/schema-building/utils.ts (5 hunks)
  • composition/src/types/types.ts (1 hunks)
  • composition/src/utils/string-constants.ts (7 hunks)
  • composition/src/utils/utils.ts (3 hunks)
  • composition/src/v1/federation/federation-factory.ts (19 hunks)
  • composition/src/v1/normalization/directive-definition-data.ts (4 hunks)
  • composition/src/v1/normalization/normalization-factory.ts (11 hunks)
  • composition/src/v1/normalization/types.ts (3 hunks)
  • composition/src/v1/normalization/utils.ts (3 hunks)
  • composition/src/v1/utils/constants.ts (4 hunks)
  • composition/src/v1/utils/utils.ts (2 hunks)
  • composition/tests/v1/directives/semantic-non-null.test.ts (1 hunks)
  • composition/tests/v1/utils/utils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T20:57:07.946Z
Learnt from: JivusAyrus
PR: wundergraph/cosmo#2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.

Applied to files:

  • composition/src/v1/normalization/types.ts
🧬 Code graph analysis (13)
composition/src/v1/normalization/types.ts (2)
composition/src/types/types.ts (1)
  • SubgraphName (9-9)
composition/src/schema-building/types.ts (1)
  • FieldData (97-118)
composition/src/schema-building/types.ts (1)
composition/src/types/types.ts (1)
  • SubgraphName (9-9)
composition/tests/v1/directives/semantic-non-null.test.ts (7)
composition/tests/utils/utils.ts (5)
  • normalizeSubgraphFailure (31-38)
  • federateSubgraphsSuccess (59-72)
  • schemaToSortedNormalizedString (27-29)
  • normalizeString (19-21)
  • federateSubgraphsFailure (49-57)
composition/src/router-compatibility-version/router-compatibility-version.ts (1)
  • ROUTER_COMPATIBILITY_VERSION_ONE (3-3)
composition/src/errors/errors.ts (4)
  • invalidDirectiveError (233-244)
  • semanticNonNullLevelsNonNullErrorMessage (1638-1643)
  • semanticNonNullLevelsIndexOutOfBoundsErrorMessage (1630-1636)
  • semanticNonNullInconsistentLevelsError (1645-1657)
composition/src/utils/string-constants.ts (3)
  • SEMANTIC_NON_NULL (131-131)
  • ID_SCALAR (62-62)
  • QUERY (114-114)
composition/src/types/types.ts (1)
  • SubgraphName (9-9)
composition/src/schema-building/types.ts (1)
  • FieldData (97-118)
composition/src/ast/utils.ts (1)
  • parse (272-274)
composition/src/errors/errors.ts (2)
composition/src/errors/types.ts (2)
  • SemanticNonNullLevelsIndexOutOfBoundsErrorParams (21-25)
  • SemanticNonNullLevelsNonNullErrorParams (27-30)
composition/src/schema-building/types.ts (1)
  • FieldData (97-118)
composition/src/schema-building/utils.ts (3)
composition/src/utils/string-constants.ts (3)
  • NON_REPEATABLE_PERSISTED_DIRECTIVES (189-189)
  • SEMANTIC_NON_NULL (131-131)
  • PERSISTED_CLIENT_DIRECTIVES (171-171)
composition/src/utils/utils.ts (2)
  • generateSemanticNonNullDirective (196-219)
  • getSingleHashSetEntry (251-257)
composition/src/schema-building/types.ts (4)
  • ChildData (249-249)
  • NodeData (265-265)
  • SchemaData (220-226)
  • FieldData (97-118)
composition/src/v1/utils/constants.ts (3)
composition/src/utils/string-constants.ts (4)
  • SEMANTIC_NON_NULL (131-131)
  • LEVELS (80-80)
  • INT_SCALAR (74-74)
  • FIELD_DEFINITION_UPPER (54-54)
composition/src/schema-building/ast.ts (1)
  • MutableDirectiveDefinitionNode (22-29)
composition/src/ast/utils.ts (2)
  • stringToNameNode (77-82)
  • stringToNamedTypeNode (100-105)
composition/src/v1/normalization/directive-definition-data.ts (4)
composition/src/schema-building/types.ts (2)
  • DirectiveDefinitionData (41-50)
  • ArgumentData (30-34)
composition/src/utils/string-constants.ts (4)
  • LEVELS (80-80)
  • INT_SCALAR (74-74)
  • FIELD_DEFINITION_UPPER (54-54)
  • SEMANTIC_NON_NULL (131-131)
composition/src/ast/utils.ts (1)
  • stringToNamedTypeNode (100-105)
composition/src/v1/utils/constants.ts (1)
  • SEMANTIC_NON_NULL_DEFINITION (711-742)
composition/src/v1/normalization/utils.ts (2)
composition/src/utils/string-constants.ts (1)
  • SEMANTIC_NON_NULL (131-131)
composition/src/v1/normalization/directive-definition-data.ts (1)
  • SEMANTIC_NON_NULL_DATA (522-556)
composition/src/utils/string-constants.ts (1)
composition/src/types/types.ts (1)
  • DirectiveName (3-3)
composition/src/v1/utils/utils.ts (1)
composition/src/schema-building/types.ts (1)
  • NodeData (265-265)
composition/src/v1/normalization/normalization-factory.ts (6)
composition/src/schema-building/utils.ts (2)
  • isFieldData (781-783)
  • isTypeRequired (124-126)
composition/src/utils/string-constants.ts (1)
  • SEMANTIC_NON_NULL (131-131)
composition/src/errors/errors.ts (3)
  • semanticNonNullLevelsNonNullErrorMessage (1638-1643)
  • semanticNonNullLevelsNaNIndexErrorMessage (1626-1628)
  • semanticNonNullLevelsIndexOutOfBoundsErrorMessage (1630-1636)
composition/src/types/types.ts (1)
  • SubgraphName (9-9)
composition/src/v1/normalization/types.ts (1)
  • HandleSemanticNonNullDirectiveParams (64-68)
composition/src/schema-building/ast.ts (1)
  • MutableTypeNode (205-205)
composition/src/utils/utils.ts (2)
composition/src/ast/utils.ts (1)
  • stringToNameNode (77-82)
composition/src/utils/string-constants.ts (2)
  • SEMANTIC_NON_NULL (131-131)
  • LEVELS (80-80)
composition/src/v1/federation/federation-factory.ts (7)
composition/src/types/types.ts (2)
  • TypeName (11-11)
  • DirectiveName (3-3)
composition/src/v1/utils/constants.ts (1)
  • SEMANTIC_NON_NULL_DEFINITION (711-742)
composition/src/schema-building/types.ts (2)
  • NodeData (265-265)
  • FieldData (97-118)
composition/src/utils/utils.ts (2)
  • getSingleHashSetEntry (251-257)
  • generateSemanticNonNullDirective (196-219)
composition/src/v1/utils/utils.ts (1)
  • getNodeCoords (447-460)
composition/src/schema-building/utils.ts (1)
  • isFieldData (781-783)
composition/src/errors/errors.ts (1)
  • semanticNonNullInconsistentLevelsError (1645-1657)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: image_scan
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_test
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
composition/src/types/types.ts (1)

3-4: LGTM: DirectiveName alias is fine.

Simple widening of the public type surface; no runtime impact.

composition/src/errors/types.ts (1)

21-31: LGTM: new error param types.

Shapes align with intended error helpers; no conflicts observed.

composition/src/v1/normalization/directive-definition-data.ts (1)

28-28: LGTM: @semanticNonNull definition wired.

  • levels: [Int!]! with default [0] and optional arg semantics looks correct.
  • Non‑repeatable and FIELD_DEFINITION location match intent.

Please confirm normalization de‑duplicates/sorts provided levels and validates bounds against the field type depth.

Also applies to: 70-70, 75-75, 93-93, 522-557

composition/src/v1/normalization/types.ts (1)

12-12: LGTM: stronger typing and new handler params.

Using SubgraphName for targetSubgraphName and the new HandleSemanticNonNullDirectiveParams look good.

Also applies to: 55-56, 64-68

composition/src/v1/normalization/utils.ts (1)

47-47: LGTM: registration in initializeDirectiveDefinitionDatas.

Directive is now discoverable by normalization.

Also applies to: 80-81, 414-415

composition/src/v1/utils/constants.ts (1)

64-64: Decide whether SEMANTIC_NON_NULL should be added to the base directive definitions.

  • SEMANTIC_NON_NULL_DEFINITION is present and SEMANTIC_NON_NULL was added to ALL_IN_BUILT_DIRECTIVE_NAMES and PERSISTED_CLIENT_DIRECTIVES, but it is NOT added to BASE_DIRECTIVE_DEFINITION_BY_DIRECTIVE_NAME or BASE_DIRECTIVE_DEFINITIONS (composition/src/v1/utils/constants.ts).
  • Effect: the normalization/emit path treats the name as a built‑in (so custom declarations are skipped) but, since it isn’t in the BASE_* maps, its directive definition will not be pre‑registered nor included in the persisted/directive‑definition emission.
  • Action: either add SEMANTIC_NON_NULL to BASE_DIRECTIVE_DEFINITION_BY_DIRECTIVE_NAME (and BASE_DIRECTIVE_DEFINITIONS) if the definition must be emitted, or confirm this omission is intentional (v1‑only / no emitted definition).
composition/src/utils/string-constants.ts (1)

189-190: Good call on non-repeatable persisted directives.

Deduping @inaccessible and @semanticNonNull early avoids noisy repeats.

composition/src/schema-building/utils.ts (3)

337-339: Early dedupe for non-repeatable persisted directives looks good.

Prevents duplicate accumulation before validation.


450-457: Client emission uses a single subgraph’s levels; ensure consistency upstream.

getSingleHashSetEntry(nullLevelsBySubgraphName) picks one subgraph arbitrarily. This is fine only if normalization guarantees equal sets across subgraphs (and errors otherwise).

If not already enforced, wire semanticNonNullInconsistentLevelsError into normalization before this path.


781-783: Type guard widening LGTM.

Expanding isFieldData to include SchemaData simplifies call sites.

composition/tests/v1/utils/utils.ts (1)

106-112: Fix invalid SDL: remove default from non-null levels or make it nullable

File: composition/tests/v1/utils/utils.ts Lines: 106-112 — levels: [Int!]! = [0] is invalid SDL (defaults are not allowed on non-null types) and will fail parsing.

Option A (keep non-null, drop default):

-export const semanticNonNullDefinition = `directive @semanticNonNull(levels: [Int!]! = [0]) on FIELD_DEFINITION`;
+export const semanticNonNullDefinition = `directive @semanticNonNull(levels: [Int!]!) on FIELD_DEFINITION`;

Option B (allow default, make list nullable):

-export const semanticNonNullDefinition = `directive @semanticNonNull(levels: [Int!]! = [0]) on FIELD_DEFINITION`;
+export const semanticNonNullDefinition = `directive @semanticNonNull(levels: [Int!] = [0]) on FIELD_DEFINITION`;

If a default is required choose B; otherwise choose A to keep the argument required.

⛔ Skipped due to learnings
Learnt from: Noroth
PR: wundergraph/cosmo#2108
File: protographic/tests/sdl-validation/01-basic-validation.test.ts:20-57
Timestamp: 2025-08-05T13:00:30.288Z
Learning: In the protographic SDL validation system, the validation logic for nullable list items only refers to the actual list type and not the nesting. For example, `[[String!]]` (nullable list of non-nullable lists) would not trigger a nullable list warning because the validation focuses on the immediate list structure rather than nested list structures.
composition/tests/v1/directives/semantic-non-null.test.ts (1)

29-343: Excellent test coverage for normalization and federation paths.

Covers key happy/error paths, persistence to router/client schemas, and cross‑subgraph consistency. LGTM.

composition/src/v1/normalization/normalization-factory.ts (1)

640-652: Good defaulting for @semanticNonNull with no arguments.

Correctly treats default levels as [0] and guards non‑null root types.

composition/src/v1/federation/federation-factory.ts (3)

1006-1010: Correctly flags presence of @semanticNonNull for definition emission.

Setting referencedPersistedDirectiveNames ensures the directive definition is emitted only when used. Nice.


1851-1870: Cross‑subgraph levels consistency check: LGTM.

Set equality check is correct and early‑exits on mismatch.


2192-2203: Conditional directive definition inclusion: LGTM.

Emits SEMANTIC_NON_NULL_DEFINITION only when referenced. Matches tests’ expectations.

Copy link
Contributor

@wilsonrivera wilsonrivera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
composition/src/v1/federation/federation-factory.ts (4)

702-712: Enforce single-subgraph invariant before using getSingleHashSetEntry

Comment says there should be a single entry, but we don’t assert it. Guard to prevent ambiguous state.

Apply this diff:

-    const subgraphName = getSingleHashSetEntry(incomingData.subgraphNames);
-    if (subgraphName === undefined) {
+    if (incomingData.subgraphNames.size !== 1) {
       this.errors.push(unknownFieldSubgraphNameError(incomingData.federatedCoords));
       return;
     }
+    const subgraphName = getSingleHashSetEntry(incomingData.subgraphNames)!;

1735-1739: Levels sort is lexicographic in generator; ensure numeric ordering

generateSemanticNonNullDirective sorts with default .sort(), which is lexicographic for numbers (e.g., [0,10,2]). Emit numerically sorted levels for stable output.

Update the helper in composition/src/utils/utils.ts:

 export function generateSemanticNonNullDirective(levels: Set<number>): ConstDirectiveNode {
-  const sortedLevels = Array.from(levels).sort();
+  const sortedLevels = Array.from(levels).sort((a, b) => a - b);
   const values = new Array<ConstValueNode>();
   for (const level of sortedLevels) {

1851-1870: Nit: short‑circuit validation if directive absent

Tiny win: skip the loop when the field doesn’t carry @semanticNonNull.

Apply this diff:

-  validateSemanticNonNull(data: FieldData) {
+  validateSemanticNonNull(data: FieldData) {
+    if (!data.directivesByDirectiveName.has(SEMANTIC_NON_NULL)) {
+      return;
+    }
     let comparison: Set<number> | undefined;
     for (const levels of data.nullLevelsBySubgraphName.values()) {

2193-2211: Be careful overwriting routerDefinitions; prefer additive update

Reassigning this.routerDefinitions can be brittle if call order changes later. Consider prepending missing directive definitions instead of replacing the whole array.

Example approach (conceptual):

  • Build a Set of required directive names based on isVersionTwo and referencedPersistedDirectiveNames.
  • Prepend any missing definitions while keeping existing entries intact and deduplicated by name.value.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f2b823 and 3ee2738.

📒 Files selected for processing (1)
  • composition/src/v1/federation/federation-factory.ts (19 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
composition/src/v1/federation/federation-factory.ts (8)
composition/src/types/types.ts (2)
  • TypeName (11-11)
  • DirectiveName (3-3)
composition/src/utils/string-constants.ts (1)
  • SEMANTIC_NON_NULL (131-131)
composition/src/v1/utils/constants.ts (1)
  • SEMANTIC_NON_NULL_DEFINITION (711-742)
composition/src/schema-building/types.ts (3)
  • PersistedDirectiveDefinitionData (192-199)
  • NodeData (265-265)
  • FieldData (97-118)
composition/src/utils/utils.ts (3)
  • getSingleHashSetEntry (251-257)
  • addMapEntries (245-249)
  • generateSemanticNonNullDirective (196-219)
composition/src/v1/utils/utils.ts (1)
  • getNodeCoords (447-462)
composition/src/schema-building/utils.ts (1)
  • isFieldData (781-783)
composition/src/errors/errors.ts (1)
  • semanticNonNullInconsistentLevelsError (1648-1660)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: image_scan
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_test
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
composition/src/v1/federation/federation-factory.ts (5)

889-889: Confirm merge semantics for nullLevelsBySubgraphName

addMapEntries overwrites existing keys with incoming Set<number> references. If the same subgraph key could appear across merges, you probably want a union (for level of incoming => target.add(level)), not replacement. If keys are guaranteed unique per field, please confirm.


1006-1009: Good: tracking directive usage for definition emission

Adding SEMANTIC_NON_NULL to referencedPersistedDirectiveNames on copy ensures the definition is emitted only when needed.


1731-1741: Avoid silently defaulting to levels [0] when none recorded

If a field has @semanticNonNull but nullLevelsBySubgraphName is empty, defaulting to [0] could mask a normalization gap. Prefer explicit validation error or skip emission.

Apply this localized tweak if you choose to skip emission instead of defaulting:

-        persistedDirectiveNodes.push(
-          generateSemanticNonNullDirective(
-            getSingleHashSetEntry(data.nullLevelsBySubgraphName) ?? new Set<number>([0]),
-          ),
-        );
+        const levels = getSingleHashSetEntry(data.nullLevelsBySubgraphName);
+        if (levels) {
+          persistedDirectiveNodes.push(generateSemanticNonNullDirective(levels));
+        } else {
+          // levels missing: rely on validation to report or skip emission
+          continue;
+        }

2012-2014: Good: validate before directive emission

Validating cross‑subgraph level consistency prior to building field nodes prevents emitting contradictory directives.


270-276: No change required — persistedDirectiveDefinitions is unused

rg output shows persistedDirectiveDefinitions only at its initialization in composition/src/v1/federation/federation-factory.ts (line 273). persistedDirectiveDefinitionByDirectiveName already contains SEMANTIC_NON_NULL and referencedPersistedDirectiveNames is used to drive inclusion of @semanticNonNull, so adding SEMANTIC_NON_NULL to persistedDirectiveDefinitions is unnecessary.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
composition/src/v1/federation/federation-factory.ts (1)

814-819: Ensure @semanticNonNull definition gets emitted when added by later subgraphs

referencedPersistedDirectiveNames is only updated in copyFieldData. If the first seen instance of a field lacks @semanticNonNull but a later subgraph adds it, the directive definition may be missing. Also update the flag here.

Apply this diff:

     extractPersistedDirectives(
       targetData.persistedDirectivesData,
       incomingData.directivesByDirectiveName,
       this.persistedDirectiveDefinitionByDirectiveName,
     );
+    if (incomingData.directivesByDirectiveName.has(SEMANTIC_NON_NULL)) {
+      this.referencedPersistedDirectiveNames.add(SEMANTIC_NON_NULL);
+    }
♻️ Duplicate comments (2)
composition/src/v1/federation/federation-factory.ts (2)

1034-1034: Fix shared reference: deep‑copy nullLevelsBySubgraphName to avoid cross‑mutation

Current code assigns the map by reference, causing mutations to leak between copies.

Apply this diff:

-      nullLevelsBySubgraphName: sourceData.nullLevelsBySubgraphName,
+      nullLevelsBySubgraphName: new Map(
+        Array.from(sourceData.nullLevelsBySubgraphName, ([subgraph, levels]) => [subgraph, new Set(levels)]),
+      ),

888-889: Deep‑merge null level sets instead of assigning Set references

addMapEntries copies Set references verbatim. Use a safe merge to prevent future mutations in one instance affecting others.

Apply this diff:

-    addMapEntries(incomingData.nullLevelsBySubgraphName, targetData.nullLevelsBySubgraphName);
+    for (const [subgraphName, levels] of incomingData.nullLevelsBySubgraphName) {
+      const targetLevels = getValueOrDefault(
+        targetData.nullLevelsBySubgraphName,
+        subgraphName,
+        () => new Set<number>(),
+      );
+      addIterableValuesToSet(levels, targetLevels);
+    }
🧹 Nitpick comments (2)
composition/src/v1/normalization/normalization-factory.ts (1)

2178-2236: Index derivation and validation look correct; minor nits.

  • Use radix for parseInt to avoid implicit base.
  • The fallback error "Argument "levels" validation error." is likely unreachable due to earlier type validation; consider removing or switching to the standard invalidArgumentValueErrorMessage for consistency.

Apply this diff:

-  for (const { value } of values) {
-    const int = parseInt(value);
+  for (const { value } of values) {
+    const int = parseInt(value, 10);

Optional:

-  if (!levelsArg || levelsArg.value.kind !== Kind.LIST) {
-    errorMessages.push(`Argument "${LEVELS}" validation error.`);
-    return;
-  }
+  if (!levelsArg || levelsArg.value.kind !== Kind.LIST) {
+    // Type validation should have already failed earlier; defensively bail.
+    return;
+  }
composition/src/v1/federation/federation-factory.ts (1)

1849-1868: Use a snapshot of the comparison set to guard against accidental mutations

Taking a copy avoids edge cases if the original Set is mutated elsewhere.

-  validateSemanticNonNull(data: FieldData) {
-    let comparison: Set<number> | undefined;
+  validateSemanticNonNull(data: FieldData) {
+    let comparison: Set<number> | undefined;
     for (const levels of data.nullLevelsBySubgraphName.values()) {
       if (!comparison) {
-        comparison = levels;
+        comparison = new Set(levels);
         continue;
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ee2738 and 251cdfb.

📒 Files selected for processing (6)
  • composition/src/errors/errors.ts (2 hunks)
  • composition/src/schema-building/utils.ts (5 hunks)
  • composition/src/utils/utils.ts (3 hunks)
  • composition/src/v1/federation/federation-factory.ts (19 hunks)
  • composition/src/v1/normalization/normalization-factory.ts (12 hunks)
  • composition/tests/v1/directives/semantic-non-null.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • composition/src/utils/utils.ts
  • composition/src/errors/errors.ts
  • composition/tests/v1/directives/semantic-non-null.test.ts
🧰 Additional context used
🧬 Code graph analysis (3)
composition/src/v1/federation/federation-factory.ts (8)
composition/src/types/types.ts (2)
  • TypeName (11-11)
  • DirectiveName (3-3)
composition/src/utils/string-constants.ts (1)
  • SEMANTIC_NON_NULL (131-131)
composition/src/v1/utils/constants.ts (1)
  • SEMANTIC_NON_NULL_DEFINITION (711-742)
composition/src/schema-building/types.ts (3)
  • PersistedDirectiveDefinitionData (192-199)
  • NodeData (265-265)
  • FieldData (97-118)
composition/src/utils/utils.ts (3)
  • getFirstEntry (251-257)
  • addMapEntries (245-249)
  • generateSemanticNonNullDirective (196-219)
composition/src/v1/utils/utils.ts (1)
  • getNodeCoords (447-462)
composition/src/schema-building/utils.ts (1)
  • isFieldData (779-781)
composition/src/errors/errors.ts (1)
  • semanticNonNullInconsistentLevelsError (1648-1660)
composition/src/schema-building/utils.ts (3)
composition/src/utils/string-constants.ts (3)
  • NON_REPEATABLE_PERSISTED_DIRECTIVES (189-189)
  • SEMANTIC_NON_NULL (131-131)
  • PERSISTED_CLIENT_DIRECTIVES (171-171)
composition/src/utils/utils.ts (2)
  • generateSemanticNonNullDirective (196-219)
  • getFirstEntry (251-257)
composition/src/schema-building/types.ts (4)
  • ChildData (249-249)
  • NodeData (265-265)
  • SchemaData (220-226)
  • FieldData (97-118)
composition/src/v1/normalization/normalization-factory.ts (6)
composition/src/schema-building/utils.ts (2)
  • isFieldData (779-781)
  • isTypeRequired (124-126)
composition/src/utils/string-constants.ts (2)
  • SEMANTIC_NON_NULL (131-131)
  • LEVELS (80-80)
composition/src/errors/errors.ts (3)
  • semanticNonNullLevelsNonNullErrorMessage (1641-1646)
  • semanticNonNullLevelsNaNIndexErrorMessage (1626-1628)
  • semanticNonNullLevelsIndexOutOfBoundsErrorMessage (1630-1639)
composition/src/types/types.ts (1)
  • SubgraphName (9-9)
composition/src/v1/normalization/types.ts (1)
  • HandleSemanticNonNullDirectiveParams (64-68)
composition/src/schema-building/ast.ts (1)
  • MutableTypeNode (205-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: build_push_image
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_test
  • GitHub Check: build_test
  • GitHub Check: integration_test (./events)
  • GitHub Check: image_scan
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (14)
composition/src/schema-building/utils.ts (3)

36-36: Imports align with broadened type guard and new directive utilities.

Looks good and consistent with downstream usage.

Also applies to: 61-61, 68-68, 74-80


337-339: De-duping non-repeatable persisted directives: confirm intended validation behavior.

By skipping additional instances for names in NON_REPEATABLE_PERSISTED_DIRECTIVES, duplicate occurrences won’t reach getValidFlattenedPersistedDirectiveNodeArray’s non‑repeatable check. If upstream validation already flags duplicates at extract/validate time, this is fine; otherwise you might mask errors. Please confirm the pipeline guarantees duplicates are still surfaced to users.


779-781: Broadened isFieldData signature is correct.

Accepting SchemaData avoids narrowing issues at call sites; type guard remains precise.

composition/src/v1/normalization/normalization-factory.ts (7)

13-13: Import of IntValueNode is appropriate for levels parsing.


161-164: New semanticNonNull error helpers wired correctly.


233-234: New imports (isFieldData, MutableTypeNode, SubgraphName) align with added logic.

Also applies to: 256-257, 371-371


301-301: LEVELS and SEMANTIC_NON_NULL constants imported as expected.

Also applies to: 329-329


629-633: Directive predicates are cleanly factored.

Using isFieldData avoids accidental schema/location handling.


641-652: Default no-arg handling for @semanticNonNull is sound.

  • Disallows level 0 when the field is already non‑null.
  • Defaults to [0] for nullable fields.

683-701: Inline handling dispatch for @semanticNonNull is correct.

composition/src/v1/federation/federation-factory.ts (4)

1730-1739: LGTM: generated @semanticNonNull directive emission

Selecting the first levels set after validation is correct; generateSemanticNonNullDirective sorts values.


2010-2011: LGTM: validate before field node emission

Validating consistency prior to emitting nodes is the right place.


262-262: LGTM: stronger typing for namedInputValueTypeNames

Switch to Set<TypeName> is appropriate and aligns with public types.


270-271: LGTM: register SEMANTIC_NON_NULL definition

Adding it to persistedDirectiveDefinitionByDirectiveName enables extraction/persistence.

@Aenimus Aenimus enabled auto-merge (squash) September 16, 2025 20:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
composition/src/errors/errors.ts (1)

1631-1640: Out-of-bounds message reads well for non-list types

Good handling of the maxIndex === 0 case with “the only valid index is 0.” This addresses the earlier concern about “0–0 inclusive.”

composition/src/v1/federation/federation-factory.ts (2)

1037-1038: Fix shared reference: deep‑copy nullLevelsBySubgraphName to avoid cross‑mutation

nullLevelsBySubgraphName is assigned by reference; copies can mutate each other and corrupt results.

Apply this diff:

-      nullLevelsBySubgraphName: sourceData.nullLevelsBySubgraphName,
+      nullLevelsBySubgraphName: new Map(
+        Array.from(
+          sourceData.nullLevelsBySubgraphName,
+          ([subgraph, levels]) => [subgraph, new Set<number>(levels)],
+        ),
+      ),
#!/bin/bash
# Find any remaining by-reference copies of nullLevelsBySubgraphName
rg -nP 'nullLevelsBySubgraphName\s*:\s*sourceData\.nullLevelsBySubgraphName' -C2
# Find any merges that assign Sets by reference for this map
rg -nP 'addMapEntries\s*\(\s*[^,]*nullLevelsBySubgraphName' -C2

891-892: Do not use addMapEntries for Map<SubgraphName, Set>; union into copies

addMapEntries assigns Set objects by reference. This can couple targetData to incomingData and allow accidental cross‑mutation later. Union the values into per‑key copies instead.

Apply this diff:

-    addMapEntries(incomingData.nullLevelsBySubgraphName, targetData.nullLevelsBySubgraphName);
+    for (const [subgraphName, levels] of incomingData.nullLevelsBySubgraphName) {
+      const targetLevels = getValueOrDefault(
+        targetData.nullLevelsBySubgraphName,
+        subgraphName,
+        () => new Set<number>(),
+      );
+      addIterableValuesToSet(levels, targetLevels);
+    }
🧹 Nitpick comments (6)
composition/src/errors/errors.ts (1)

1651-1663: Stabilize and clarify per-subgraph levels listing

For deterministic error messages and better readability, sort subgraph names and render levels with brackets.

Apply this diff:

-  for (const [subgraphName, levels] of data.nullLevelsBySubgraphName) {
-    message += ` Subgraph "${subgraphName}" defines levels ${Array.from(levels).sort((a, b) => a - b)}.\n`;
-  }
+  const subgraphs = [...data.nullLevelsBySubgraphName.keys()].sort();
+  for (const subgraphName of subgraphs) {
+    const levels = data.nullLevelsBySubgraphName.get(subgraphName)!;
+    const sorted = [...levels].sort((a, b) => a - b);
+    message += ` Subgraph "${subgraphName}" defines levels [${sorted.join(', ')}].\n`;
+  }
composition/src/v1/normalization/normalization-factory.ts (2)

696-703: Invoke handler only once per directive to avoid duplicate work

Currently, the handler is called for each argument node of the directive. If the directive ever gains additional arguments, this can cause duplicate validation and repeated errors.

Apply this diff to gate execution on the levels argument name:

-      if (isSemanticNonNull && isField) {
-        this.handleSemanticNonNullDirective({
-          data,
-          directiveNode,
-          errorMessages,
-        });
-        continue;
-      }
+      if (isSemanticNonNull && isField && argumentName === LEVELS) {
+        this.handleSemanticNonNullDirective({
+          data,
+          directiveNode,
+          errorMessages,
+        });
+        continue;
+      }

2179-2238: Use a single source of truth for the field type

You traverse data.node.type but print data.type. If these ever diverge, the message could reference a different type than the one examined. Use data.type for both.

Apply this diff:

-    const nonNullIndices = new Set<number>();
-    let currentType: MutableTypeNode | null = data.node.type;
+    const nonNullIndices = new Set<number>();
+    let currentType: MutableTypeNode | null = data.type;
@@
-    const values = levelsArg.value.values as ReadonlyArray<IntValueNode>;
-    const typeString = printTypeNode(data.type);
+    const values = levelsArg.value.values as ReadonlyArray<IntValueNode>;
+    const typeString = printTypeNode(data.type);
composition/src/v1/federation/federation-factory.ts (3)

1733-1758: Router emission: generate canonical @semanticNonNull once

Approach looks good. Minor safety: guard against empty/null maps explicitly to document intent.

Apply this small tweak (no behavior change):

-      if (directiveName === SEMANTIC_NON_NULL && isFieldData(data)) {
-        persistedDirectiveNodes.push(
-          generateSemanticNonNullDirective(getFirstEntry(data.nullLevelsBySubgraphName) ?? new Set<number>([0])),
-        );
+      if (directiveName === SEMANTIC_NON_NULL && isFieldData(data)) {
+        const levels = getFirstEntry(data.nullLevelsBySubgraphName);
+        persistedDirectiveNodes.push(
+          generateSemanticNonNullDirective(levels ?? new Set<number>([0])),
+        );
         continue;
       }

1852-1871: Validate only when directive is present; keep loop as-is

Current logic works; add a cheap guard to avoid doing work on fields without @semanticNonNull (and to future‑proof if the map were ever populated incorrectly).

Apply this diff:

-  validateSemanticNonNull(data: FieldData) {
+  validateSemanticNonNull(data: FieldData) {
+    if (!data.directivesByDirectiveName.has(SEMANTIC_NON_NULL)) return;
     let comparison: Set<number> | undefined;
     for (const levels of data.nullLevelsBySubgraphName.values()) {

Also applies to: 2013-2014


2195-2214: LGTM: usage‑gated definition emission for v1/v2

Emission logic and routerDefinitions resets are consistent. Consider a tiny dedup helper to DRY the SEMANTIC_NON_NULL_DEFINITION push across branches later.

Also applies to: 2217-2225

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 251cdfb and 6c9a231.

📒 Files selected for processing (4)
  • composition/src/errors/errors.ts (3 hunks)
  • composition/src/v1/federation/federation-factory.ts (20 hunks)
  • composition/src/v1/normalization/normalization-factory.ts (12 hunks)
  • composition/tests/v1/directives/semantic-non-null.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • composition/tests/v1/directives/semantic-non-null.test.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T20:57:07.946Z
Learnt from: JivusAyrus
PR: wundergraph/cosmo#2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.

Applied to files:

  • composition/src/v1/federation/federation-factory.ts
🧬 Code graph analysis (3)
composition/src/errors/errors.ts (3)
composition/src/errors/types.ts (2)
  • SemanticNonNullLevelsIndexOutOfBoundsErrorParams (21-25)
  • SemanticNonNullLevelsNonNullErrorParams (27-30)
composition/src/utils/string-constants.ts (1)
  • LEVELS (80-80)
composition/src/schema-building/types.ts (1)
  • FieldData (97-118)
composition/src/v1/federation/federation-factory.ts (8)
composition/src/types/types.ts (2)
  • TypeName (11-11)
  • DirectiveName (3-3)
composition/src/utils/string-constants.ts (1)
  • SEMANTIC_NON_NULL (131-131)
composition/src/v1/utils/constants.ts (1)
  • SEMANTIC_NON_NULL_DEFINITION (711-742)
composition/src/schema-building/types.ts (3)
  • PersistedDirectiveDefinitionData (192-199)
  • NodeData (265-265)
  • FieldData (97-118)
composition/src/utils/utils.ts (3)
  • getFirstEntry (251-257)
  • addMapEntries (245-249)
  • generateSemanticNonNullDirective (196-219)
composition/src/v1/utils/utils.ts (1)
  • getNodeCoords (447-462)
composition/src/schema-building/utils.ts (1)
  • isFieldData (779-781)
composition/src/errors/errors.ts (1)
  • semanticNonNullInconsistentLevelsError (1651-1663)
composition/src/v1/normalization/normalization-factory.ts (6)
composition/src/schema-building/utils.ts (2)
  • isFieldData (779-781)
  • isTypeRequired (124-126)
composition/src/utils/string-constants.ts (4)
  • OVERRIDE (106-106)
  • REQUIRES_SCOPES (121-121)
  • SEMANTIC_NON_NULL (131-131)
  • LEVELS (80-80)
composition/src/errors/errors.ts (4)
  • semanticNonNullLevelsNonNullErrorMessage (1642-1647)
  • semanticNonNullArgumentErrorMessage (1649-1649)
  • semanticNonNullLevelsNaNIndexErrorMessage (1627-1629)
  • semanticNonNullLevelsIndexOutOfBoundsErrorMessage (1631-1640)
composition/src/types/types.ts (1)
  • SubgraphName (9-9)
composition/src/v1/normalization/types.ts (1)
  • HandleSemanticNonNullDirectiveParams (64-68)
composition/src/schema-building/ast.ts (1)
  • MutableTypeNode (205-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: image_scan
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: build_test
  • GitHub Check: build_push_image
🔇 Additional comments (8)
composition/src/v1/normalization/normalization-factory.ts (3)

642-653: Default semantics for missing arguments look correct

Defaulting to [0] only when the field is nullable, and erroring when index 0 is already non-null, matches the directive’s intent.


1151-1155: FieldData gains nullLevelsBySubgraphName

Good place to store per-subgraph levels for later cross-subgraph consistency checks.


2179-2238: ```shell
#!/bin/bash
set -euo pipefail

echo "== git status (brief) =="
git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git rev-parse --abbrev-ref HEAD || echo "not a git repo"

echo "== Searching repository for semanticNonNull occurrences =="

Broad search for directive name, handler name, related constants and error message identifiers

rg -n --hidden --no-ignore -S '@semanticNonNull|semanticNonNull|handleSemanticNonNullDirective|semanticNonNullArgumentErrorMessage|semanticNonNullLevelsNaNIndexErrorMessage|semanticNonNullLevelsIndexOutOfBoundsErrorMessage|semanticNonNullLevelsNonNullErrorMessage|LEVELS' || true

FILE='composition/src/v1/normalization/normalization-factory.ts'
if [ -f "$FILE" ]; then
echo "== Showing $FILE (lines 2160-2250) =="
sed -n '2160,2250p' "$FILE"
else
echo "File $FILE not found"
fi

echo "== Searching composition/tests for semanticNonNull occurrences =="
rg -n --hidden --no-ignore -S 'semanticNonNull|@semanticNonNull' composition/tests || true

echo "== Summary counts (by file) =="
rg -n --hidden --no-ignore -S 'semanticNonNull|@semanticNonNull' -c || true


</blockquote></details>
<details>
<summary>composition/src/v1/federation/federation-factory.ts (5)</summary><blockquote>

`56-74`: **LGTM: wiring and type-surface for @semanticNonNull**

Imports, constants, and added types (DirectiveName/TypeName) are correctly wired for the new directive.



Also applies to: 89-101, 117-119, 153-170, 209-216, 218-231, 239-240

---

`702-713`: **LGTM: stable subgraph source for subscriptionFilter**

Using getFirstEntry(Set) with an explicit undefined guard keeps behavior deterministic and error‑aware.

---

`806-809`: **LGTM: usage‑based gating for directive definitions**

Recording SEMANTIC_NON_NULL in referencedPersistedDirectiveNames in both upsert and copy paths ensures correct emission in v1/v2.



Also applies to: 1009-1012

---

`1024-1024`: **LGTM: stricter typing for inheritedDirectiveNames**

Switch to Set<DirectiveName> improves clarity and future refactors.

---

`1765-1766`: **LGTM: single source for persisted directive assembly**

Delegating to getValidFlattenedPersistedDirectiveNodeArray keeps router/client paths symmetrical and maintainable (router side here).

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@Aenimus Aenimus merged commit 933d9f7 into main Sep 16, 2025
33 checks passed
@Aenimus Aenimus deleted the david/eng-7305-implement-semantic-non-null branch September 16, 2025 20:56
@Noroth Noroth mentioned this pull request Sep 30, 2025
5 tasks
@coderabbitai coderabbitai bot mentioned this pull request Oct 1, 2025
5 tasks
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.

4 participants