Skip to content

JSpecify big wave 3#4274

Draft
dondonz wants to merge 76 commits intomasterfrom
claude/agent-team-jspecify-Frd74
Draft

JSpecify big wave 3#4274
dondonz wants to merge 76 commits intomasterfrom
claude/agent-team-jspecify-Frd74

Conversation

@dondonz
Copy link
Member

@dondonz dondonz commented Mar 1, 2026

Another wave of JSpecify changes, this time featuring Claude's agent teams.

Keeping in draft while I review.


66 classes annotated

graphql.analysis (16 classes)

QueryComplexityCalculator, QueryComplexityInfo, QueryDepthInfo, QueryReducer, QueryTransformer, QueryTraversalOptions, QueryVisitor, QueryVisitorFieldArgumentEnvironment, QueryVisitorFieldArgumentInputValue, QueryVisitorFieldArgumentValueEnvironment, QueryVisitorFieldEnvironment, QueryVisitorFragmentDefinitionEnvironment, QueryVisitorFragmentSpreadEnvironment, QueryVisitorInlineFragmentEnvironment, QueryVisitorStub, ValueTraverser

graphql.execution core (26 classes)

AbortExecutionException, AsyncExecutionStrategy, AsyncSerialExecutionStrategy, CoercedVariables, DataFetcherExceptionHandlerParameters, DataFetcherExceptionHandlerResult, DefaultValueUnboxer, ExecutionContext, ExecutionId, ExecutionStepInfo, ExecutionStrategyParameters, FetchedValue, FieldValueInfo, InputMapDefinesTooManyFieldsException, MergedSelectionSet, MissingRootTypeException, NonNullableValueCoercedAsNullException, NormalizedVariables, OneOfNullValueException, OneOfTooManyKeysException, ResultNodesInfo, ResultPath, SimpleDataFetcherExceptionHandler, SubscriptionExecutionStrategy, UnknownOperationException, UnresolvedTypeException

graphql.execution sub-packages (24 classes)

ConditionalNodeDecision, QueryAppliedDirective, QueryAppliedDirectiveArgument, QueryDirectives, FieldValidationInstrumentation, SimpleFieldValidation, InstrumentationCreateStateParameters, InstrumentationExecuteOperationParameters, InstrumentationExecutionParameters, InstrumentationExecutionStrategyParameters, InstrumentationFieldCompleteParameters, InstrumentationFieldFetchParameters, InstrumentationFieldParameters, InstrumentationValidationParameters, TracingInstrumentation, TracingSupport, PreparsedDocumentEntry, ApolloPersistedQuerySupport, InMemoryPersistedQueryCache, PersistedQueryCacheMiss, PersistedQueryIdInvalid, PersistedQueryNotFound, DelegatingSubscription, SubscriptionPublisher

Review passes

./gradlew compileJava passes. The reviewer agent also fixed cascading NullAway errors in calling code (e.g. ResultPath, ExecutionStepInfo, ExecutionContext, TracingSupport) using assertNotNull() where structural invariants guarantee non-null.

claude and others added 30 commits February 28, 2026 23:21
dondonz and others added 12 commits March 1, 2026 10:41
- QueryAppliedDirectiveArgument: remove @nonnull from Builder methods (import was dropped)
- ResultPath: fix @nullable parent/segment dereferences with assertNotNull
- ExecutionStepInfo: fix @nullable field dereference in getResultKey()
- ExecutionContext: add assertNotNull for AtomicReference.get() calls on errors
- SubscriptionExecutionStrategy: fix @nullable getField()/getDeferredCallContext() dereferences
- AsyncSerialExecutionStrategy: fix @nullable getSubField()/getFieldValueObject() issues
- DataFetcherExceptionHandlerParameters: mark getSourceLocation() @nullable
- ExceptionWhileDataFetching: accept @nullable SourceLocation in constructor
- TracingSupport/UnresolvedTypeError: fix @nullable getParent()/getFieldDefinition() dereferences
- InstrumentationFieldParameters/Complete: fix @nullable getFieldDefinition() in getField()
- ExecutionStepInfoFactory: fix @nullable getField() with assertNotNull
- GraphQL: fix @nullable getDocument() with assertNotNull
- ValueTraverser: fix @nullable newValue passed to ImmutableList.Builder.add()
- MaxQueryDepthInstrumentation: mark getPathLength() param @nullable

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* This Exception indicates that the current execution should be aborted.
*/
@PublicApi
@NullMarked
Copy link
Member Author

Choose a reason for hiding this comment

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

I abruptly ran out of credits here - more is coming

Copy link
Member Author

Choose a reason for hiding this comment

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

Given it's already 66 classes - the next wave will happen in a separate PR

Copy link
Member

Choose a reason for hiding this comment

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

Image

@github-actions
Copy link
Contributor

github-actions bot commented Mar 1, 2026

Test Results

  335 files  ±0    335 suites  ±0   5m 4s ⏱️ -2s
5 378 tests ±0  5 369 ✅  - 1  9 💤 +1  0 ❌ ±0 
5 467 runs  ±0  5 458 ✅  - 1  9 💤 +1  0 ❌ ±0 

Results for commit 0a77532. ± Comparison against base commit 739403f.

This pull request removes 196 and adds 172 tests. Note that renamed tests count towards both.
	?

	, expected: combo-\"\\\b\f\n\r\t, #4]
                __schema { types { fields { args { type { name fields { name }}}}}}
                __schema { types { fields { type { name fields { name }}}}}
                __schema { types { inputFields { type { inputFields { name }}}}}
                __schema { types { interfaces { fields { type { interfaces { name } } } } } }
                __schema { types { name} }
                __type(name : "t") { name }
                a1: __schema { types { name} }
                a1: __type(name : "t") { name }
…
graphql.AssertTest ‑ assertFalse with different number of error args but false does not throw assertions [toRun: <graphql.AssertTest$__spock_feature_0_21prov0_closure23@2f2bff16 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1, #0]
graphql.AssertTest ‑ assertFalse with different number of error args but false does not throw assertions [toRun: <graphql.AssertTest$__spock_feature_0_21prov0_closure24@75de29c0 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1 arg2, #1]
graphql.AssertTest ‑ assertFalse with different number of error args but false does not throw assertions [toRun: <graphql.AssertTest$__spock_feature_0_21prov0_closure25@fc807c1 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1 arg2 arg3, #2]
graphql.AssertTest ‑ assertFalse with different number of error args throws assertions [toRun: <graphql.AssertTest$__spock_feature_0_20prov0_closure20@733aa9d8 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1, #0]
graphql.AssertTest ‑ assertFalse with different number of error args throws assertions [toRun: <graphql.AssertTest$__spock_feature_0_20prov0_closure21@6dcc40f5 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1 arg2, #1]
graphql.AssertTest ‑ assertFalse with different number of error args throws assertions [toRun: <graphql.AssertTest$__spock_feature_0_20prov0_closure22@2b680207 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1 arg2 arg3, #2]
graphql.AssertTest ‑ assertNotNull with different number of  error args throws assertions [toRun: <graphql.AssertTest$__spock_feature_0_5prov0_closure3@6eb17ec8 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1, #0]
graphql.AssertTest ‑ assertNotNull with different number of  error args throws assertions [toRun: <graphql.AssertTest$__spock_feature_0_5prov0_closure4@48d293ee delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1 arg2, #1]
graphql.AssertTest ‑ assertNotNull with different number of  error args throws assertions [toRun: <graphql.AssertTest$__spock_feature_0_5prov0_closure5@7a0ef219 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1 arg2 arg3, #2]
graphql.AssertTest ‑ assertNotNull with different number of error args with non null does not throw assertions [toRun: <graphql.AssertTest$__spock_feature_0_6prov0_closure6@e5cbff2 delegate=inaccessible owner=inaccessible thisObject=inaccessible resolveStrategy=inaccessible directive=inaccessible parameterTypes=inaccessible maximumNumberOfParameters=inaccessible bcw=inaccessible thisType=inaccessible>, expectedMessage: error arg1, #0]
…
This pull request skips 1 test.
graphql.schema.fetching.LambdaFetchingSupportTest ‑ different class loaders induce certain behaviours

♻️ This comment has been updated with latest results.

All 66 classes annotated in Wave 1 (graphql.analysis, graphql.execution core,
and graphql.execution sub-packages) are removed from the exemption list now
that they carry @NullMarked.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
graphql.execution.reactive.DelegatingSubscription
graphql.execution.reactive.SubscriptionPublisher
```

Copy link
Member Author

Choose a reason for hiding this comment

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

Worker 4 got removed because it was covered by previous PRs.

Work from worker 5 onwards will be in another PR. This PR is already quite large

dondonz and others added 2 commits March 1, 2026 16:18
- Explicitly state that @internal classes must not be annotated
- Tighten exemption list cleanup instruction to remove only the annotated class

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* @return this builder
*/
public Builder valueLiteral(@NonNull Value<?> value) {
public Builder valueLiteral(Value<?> value) {
Copy link
Member

Choose a reason for hiding this comment

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

So you tuned OFF null marked but this is no less precise than it was. Perhaps a Object.requireNonNull ??

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good idea


@Nullable
public List<? extends GraphQLError> getErrors() {
return errors;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should change this so that errors is always non null but some times empty?

Document has to nullable of course. Quasi breaking change but nicer - eg less nullable things

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good idea, you mentioned this in another class too. I think better to make it an empty list by default, like DataFetcherResult

Copy link
Member Author

Choose a reason for hiding this comment

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

I will add a breaking change label

Copy link
Member

@bbakerman bbakerman left a comment

Choose a reason for hiding this comment

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

Image

@dondonz
Copy link
Member Author

dondonz commented Mar 7, 2026

I'm impressed, I have the correct number of fingers on both hands. Models have come a long way.

But I'm going to deduct marks for Python references creeping in!

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

Test Report

Test Results

Java Version Total Passed Failed Errors Skipped
Java 11 5518 (-190 🔴) 5509 (-143 🔴) 2 (+2 🔴) 0 (±0) 7 (-49)
Java 17 0 (-5708 🔴) 0 (-5651 🔴) 0 (±0) 0 (±0) 0 (-57)
Java 21 5518 (-190 🔴) 5508 (-143 🔴) 2 (+2 🔴) 0 (±0) 8 (-49)
Java 25 5518 (-190 🔴) 5508 (-143 🔴) 2 (+2 🔴) 0 (±0) 8 (-49)
jcstress 32 (±0) 32 (±0) 0 (±0) 0 (±0) 0 (±0)
Total 16586 (-6278 🔴) 16557 (-6080 🔴) 6 (+6 🔴) 0 (±0) 23 (-204)

Updated: 2026-03-22 07:24:46 UTC

@dondonz dondonz added the breaking change requires a new major version to be relased label Mar 22, 2026
dondonz added 2 commits March 22, 2026 17:53
# Conflicts:
#	.claude/commands/jspecify-annotate.md
#	src/main/java/graphql/execution/ExecutionContext.java
#	src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change requires a new major version to be relased

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants