Skip to content

[libgraphql-core-v1] Task 16.6c: Deprecation validations (2.6 + @deprecated-on-required)#107

Merged
jeffmo merged 2 commits into
mainfrom
lgcore_v1_task16.6c
Jul 8, 2026
Merged

[libgraphql-core-v1] Task 16.6c: Deprecation validations (2.6 + @deprecated-on-required)#107
jeffmo merged 2 commits into
mainfrom
lgcore_v1_task16.6c

Conversation

@jeffmo

@jeffmo jeffmo commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Task 16.6c (third of the six schema-hardening PRs): deprecation validations — the IsValidImplementation deprecation-consistency rule and the @deprecated-on-required prohibition, both entirely unenforced until now.

What & why

Plumbing — deprecation_state() everywhere it matters. New DeprecationState::from_directives() computes deprecation on demand for FieldDefinition, ParameterDefinition, and InputField (computed, not stored — so the 16.6b extension-merge construction paths are automatically covered with zero builder plumbing). Reason extraction follows spec: omitted → the default "No longer supported" (DeprecationState::DEFAULT_REASON, now the single source of truth shared with the builtin @deprecated seed); explicit null → deprecated with no reason.

IsValidImplementation step 2.6. Verified verbatim from the September2025 spec source: "If {field} is deprecated then {implementedField} must also be deprecated." An implementing field may be @deprecated only if the interface field it implements is too; the converse (deprecated interface field, non-deprecated implementer) is legal and explicitly tested. Implemented inside the same per-interface-contract loop as the sibling checks (so it flows through the transitive-interface machinery identically), replacing the long-standing TODO. Error span targets the offending @deprecated annotation, with a "defined here without @deprecated" note on the interface field.

Note: the step label is the spec's own numeric 2.6 — earlier plan/audit references to "2.f" were a paraphrase; this PR standardizes the label everywhere. Also worth knowing: graphql-js does not enforce this rule today, so v1 will reject some schemas graphql-js accepts — with the spec text on our side.

§3.13.3 — @deprecated must not appear on required (non-null, defaultless) arguments or input fields. Enforced at build() time over merged types (extension-contributed params/fields covered, regression-tested): DeprecatedRequiredParameter (field args), DeprecatedRequiredInputField, and DeprecatedRequiredDirectiveParameter (directive definitions' own args, matching graphql-js's reading of the blanket "arguments" wording). Each error includes a help note about the make-it-optional escape hatch. Requiredness is exactly non-null AND no default at all three sites — a non-null arg with a default is optional and passes.

Tests (28 new; v1 crate 256 → 284)

Accessor semantics (6, incl. reason default/null), object/interface validator matrix (7: 2.6 error w/ span+notes, both-deprecated OK, converse OK, interface-implements-interface, per-contract transitive case, required-param error, optional-param OKs), input-object (2), directive-def (2), end-to-end build_from_str (8), and 16.6b extension-path regressions (3).

Verification

  • cargo test --workspace — 1389 green; cargo clippy --workspace --tests -- -Dwarnings — clean
  • Opus sub-agent review pre-PR: no blockers; its one should-fix (independent spec verification of the 2.6 rule) resolved by re-fetching the September2025 spec source and confirming the rule verbatim; label standardization + first-wins documentation applied as follow-up commit
  • Plan doc updated with [x] boxes + Completion Notes per the Execution Protocol

🤖 Generated with Claude Code

https://claude.ai/code/session_01TipQtpvuLuHjHmRsVEgwJw


Generated by Claude Code

claude added 2 commits July 8, 2026 02:08
…n-required)

Implements Task 16.6c from the v1 plan:

- DeprecationState::from_directives() computes deprecation on demand
  from a type element's directive annotations; new deprecation_state()
  accessors on FieldDefinition, ParameterDefinition, and InputField.
  Computed (not stored) so the 16.6b extension-merge construction paths
  are automatically covered with no builder plumbing. An omitted reason
  yields the spec's default "No longer supported" (now also used by the
  builtin @deprecated seed); an explicit null reason yields None.

- IsValidImplementation step 2.6 (verified verbatim from the Sep2025
  spec text: "If {field} is deprecated then {implementedField} must
  also be deprecated"): an implementing field may be @deprecated only
  if the interface field it implements is also deprecated. New
  DeprecatedFieldImplementingNonDeprecatedInterfaceField error with the
  span on the offending @deprecated annotation and a note pointing at
  the non-deprecated interface field. Replaces the long-standing TODO
  in object_or_interface_type_validator. The converse (deprecated
  interface field, non-deprecated implementer) is legal and tested.

- @deprecated must not appear on required (non-null, defaultless)
  arguments or input fields: DeprecatedRequiredParameter,
  DeprecatedRequiredInputField, and DeprecatedRequiredDirectiveParameter
  enforced at build() time over merged types, so extension-contributed
  params/fields are covered. Help notes explain the make-it-optional
  escape hatch.

28 tests added (accessor semantics incl. reason defaults, validator
matrices incl. transitive-interface and converse-OK cases, extension-
path regressions, end-to-end build_from_str cases). Plan doc updated
with completion notes per the Execution Protocol.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TipQtpvuLuHjHmRsVEgwJw
…ns doc

- Standardize the IsValidImplementation step label to the spec's own
  numeric "2.6" everywhere (code comments, tests, plan doc) -- the
  September2025 source text numbers the steps 2.1-2.6; "2.f" was the
  audit's paraphrase. Rule existence and direction re-verified verbatim
  against the September2025 tag: "If {field} is deprecated then
  {implementedField} must also be deprecated."
- Document find_deprecated_annotation's first-wins behavior for
  (spec-invalid) duplicate @deprecated annotations, with a pointer to
  Task 16.6f's 5.7.3 validation that will reject duplicates outright.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TipQtpvuLuHjHmRsVEgwJw

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens libgraphql-core-v1 schema validation around deprecations by (1) making deprecation state queryable on-demand from directive annotations, (2) enforcing IsValidImplementation step 2.6 deprecation consistency for implementing fields, and (3) prohibiting @deprecated on required (non-null, defaultless) arguments and input fields (including directive definition parameters).

Changes:

  • Add DeprecationState::from_directives() (+ DEFAULT_REASON) and deprecation_state() accessors on FieldDefinition, ParameterDefinition, and InputField.
  • Enforce IsValidImplementation step 2.6: an implementing field marked @deprecated must not implement a non-deprecated interface field, with spans targeted to the @deprecated annotation.
  • Enforce @deprecated prohibition on required arguments/input fields at build/validation time across objects/interfaces, input objects, and custom directive definitions, plus extensive new tests (including extension-merge regressions).

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated no comments.

Show a summary per file
File Description
libgraphql-core-v1-plan.md Updates plan checklist/notes to reflect 16.6c completion and the standardized “2.6” label.
crates/libgraphql-core-v1/src/validators/object_or_interface_type_validator.rs Implements IsValidImplementation 2.6 enforcement and required-parameter @deprecated prohibition with targeted spans + notes.
crates/libgraphql-core-v1/src/validators/input_object_type_validator.rs Adds required input-field @deprecated prohibition with targeted spans + notes.
crates/libgraphql-core-v1/src/validators/directive_definition_validator.rs Adds required directive-parameter @deprecated prohibition for custom directives.
crates/libgraphql-core-v1/src/types/deprecation_state.rs Adds directive-driven deprecation computation and a helper to locate @deprecated annotations.
crates/libgraphql-core-v1/src/types/field_definition.rs Adds FieldDefinition::deprecation_state() accessor.
crates/libgraphql-core-v1/src/types/parameter_definition.rs Adds ParameterDefinition::deprecation_state() accessor.
crates/libgraphql-core-v1/src/types/input_field.rs Adds InputField::deprecation_state() accessor.
crates/libgraphql-core-v1/src/types/mod.rs Re-exports find_deprecated_annotation for validator span targeting.
crates/libgraphql-core-v1/src/schema/type_validation_error.rs Adds new TypeValidationErrorKind variants for the new deprecation validations.
crates/libgraphql-core-v1/src/schema/schema_builder.rs Centralizes the builtin @deprecated default reason via DeprecationState::DEFAULT_REASON.
crates/libgraphql-core-v1/src/types/tests/deprecation_state_tests.rs Adds unit tests for accessor semantics and default/null reason handling.
crates/libgraphql-core-v1/src/validators/tests/object_or_interface_type_validator_tests.rs Adds validator-level tests for 2.6 enforcement and required/optional parameter rules.
crates/libgraphql-core-v1/src/validators/tests/input_object_type_validator_tests.rs Adds validator-level tests for required/optional input-field rules.
crates/libgraphql-core-v1/src/validators/tests/directive_definition_validator_tests.rs Adds validator-level tests for required/optional directive-parameter rules.
crates/libgraphql-core-v1/src/schema/tests/schema_builder_tests.rs Adds end-to-end build-from-str tests covering the new validations and deprecation-state extraction.
crates/libgraphql-core-v1/src/schema/tests/schema_builder_extension_tests.rs Adds extension-merge regression tests to ensure merged validation covers extension-contributed deprecated items.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jeffmo jeffmo merged commit b8b6ffd into main Jul 8, 2026
8 checks passed
@jeffmo jeffmo deleted the lgcore_v1_task16.6c branch July 8, 2026 02:40
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.

3 participants