Skip to content

fix(server): enforce Spring correctness rules - #1517

Merged
FelixTJDietrich merged 5 commits into
mainfrom
1493-spring-correctness-rules
Aug 24, 2026
Merged

fix(server): enforce Spring correctness rules#1517
FelixTJDietrich merged 5 commits into
mainfrom
1493-spring-correctness-rules

Conversation

@FelixTJDietrich

@FelixTJDietrich FelixTJDietrich commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds a single ArchUnit suite that makes silent Spring correctness failures fail in CI. It enforces proxy-safe transaction boundaries, constructor injection, request validation, safe lifecycle usage, and Spring Data collection return contracts.

The change also fixes every existing violation uncovered by the new rules—there are no baselines, allowlists, or suppressed findings. Transaction boundaries were preserved explicitly: same-transaction delegation now uses private non-annotated methods, independent work uses focused collaborators or TransactionTemplate, and remote pagination/retry work remains outside database transactions.

The enforced contracts are:

  • @Transactional methods are public and non-final;
  • code does not call a transactional method on the same instance;
  • fields are not injected with @Autowired;
  • application @RequestBody parameters use @Valid;
  • @PostConstruct methods are not transactional;
  • repository methods do not wrap multi-result types in Optional; and
  • transaction boundaries use Spring's @Transactional, not the JTA annotation.

Request validation now uses real @NotNull constraints for required values rather than inert annotations. The resulting client-facing behavior is documented in the patch changeset, and the OpenAPI specification and generated client are updated.

Each rule was verified against a deliberate violation before the production code was corrected. Those temporary violations were removed rather than retained as test-only theatre.

These checks implement the documented behavior of Spring transaction proxies, Spring AOP proxying, Spring MVC validation, and Spring Data repository return types.

Fixes #1493

How to test

Run the same local gates used for this branch:

pnpm run format
MANAGEMENT_PORT=0 SERVER_PORT=0 pnpm run check
cd server
MANAGEMENT_PORT=0 SERVER_PORT=0 ./mvnw test -P'!quick' -q
MANAGEMENT_PORT=0 SERVER_PORT=0 ./mvnw test -P'!quick,architecture-tests' -Dtest=SpringCorrectnessArchitectureTest -q

All four commands pass locally. For a manual smoke test, temporarily add a private or final @Transactional method, a same-class call to a transactional method, or field @Autowired; the focused architecture test should report the violating member. Revert the deliberate violation afterward.

Checklist

  • My changeset summary reads as an operator/user-facing note (it becomes the changelog entry) — see .changeset/README.md
  • If the operator must act on this change (new required env var, manual migration step), the changeset summary says how (**Operators:** …) and MIGRATION.md is updated — no operator action is required

@FelixTJDietrich
FelixTJDietrich requested a review from a team as a code owner August 24, 2026 09:04
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 16 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 83c53b07-baca-4e98-8335-184eff0ce9f3

📥 Commits

Reviewing files that changed from the base of the PR and between d4d4027 and 29a79f5.

📒 Files selected for processing (77)
  • .changeset/spring-contracts-hold.md
  • server/openapi.yaml
  • server/src/main/java/de/tum/cit/aet/hephaestus/account/AccountPreferencesService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/activity/ActivityEventService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/backfill/ReviewBackfillCostEstimator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/IssueContentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/ReviewHistoryContentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/FeedbackLedgerRecorder.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/InContextDeliveryGate.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/PracticeFeedbackDeliveryPolicy.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/conversation/ConversationalDeliveryListener.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/inapp/InAppCompositionListener.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/ReviewRunSummaryQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatRequestBody.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorInFlightAccounting.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorInFlightReaper.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/jwt/HephaestusJwtIssuer.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/jwt/JwtPrincipalFactory.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/provider/LoginProviderService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/web/AccountAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/runtime/hub/auth/WorkerTokenExchangeController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/connection/ConnectionRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/connection/ConnectionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/connection/api/ConnectionController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/connection/api/InitiateConnectionRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/outline/lifecycle/OutlineWebhookRegistrar.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/connect/GithubConnectionStrategy.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/discussion/GitHubDiscussionProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/issue/GitHubIssueProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/issuedependency/GitHubIssueDependencySyncService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/lifecycle/GithubLifecycleListener.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/organization/GitHubOrganizationSyncService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/project/GitHubProjectItemProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/project/GitHubProjectProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/project/ProjectIntegrityService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/pullrequest/GitHubPullRequestProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/pullrequestreview/GitHubPullRequestReviewSyncService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/subissue/GitHubSubIssueSyncService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/team/GitHubMembershipMessageHandler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/common/BaseGitLabProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/issue/GitLabIssueProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/issuedependency/GitLabIssueDependencySyncService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/pullrequest/GitLabMergeRequestProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/workspace/GitLabWebhookService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/workspace/GitLabWorkspaceInitializationService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/workspace/GitLabWorkspaceLinkService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/slack/connect/SlackConnectionAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/slack/connect/SlackConnectionStrategy.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/slack/events/SlackIngestService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/slack/events/SlackPersonErasureService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/slack/mentor/SlackMentorIdentityResolver.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/leaderboard/LeaderboardService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/leaderboard/LeaderboardXpQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/leaderboard/tasks/LeaguePointsUpdateTask.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/mentor/ChatThreadService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeAreaService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/ReviewFeedbackQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/ReviewObservationQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/trace/ArtifactTraceQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceActivationService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceLifecycleService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceRepositoryMonitorService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceSettingsService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/adapter/WorkspaceSyncTargetProvider.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/settings/WorkspaceTeamSettingsService.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorInFlightReaperTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnPersistenceIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/SpringCorrectnessArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/workspace/GitLabWorkspaceInitializationServiceTest.java
  • webapp/src/api/types.gen.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added application-server Spring Boot server: APIs, business logic, database size:L This PR changes 100-499 lines, ignoring generated files. test Unit tests, integration tests, test infrastructure labels Aug 24, 2026
@github-actions github-actions Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Aug 24, 2026
@FelixTJDietrich
FelixTJDietrich force-pushed the 1493-spring-correctness-rules branch from 4a9305c to 804f05b Compare August 24, 2026 12:09
@FelixTJDietrich FelixTJDietrich changed the title test(server): enforce Spring correctness rules fix(server): enforce Spring correctness rules Aug 24, 2026
@github-actions github-actions Bot added the bug Something isn't working label Aug 24, 2026
@FelixTJDietrich
FelixTJDietrich force-pushed the 1493-spring-correctness-rules branch from 804f05b to 6855b8b Compare August 24, 2026 13:20
@github-actions github-actions Bot added the webapp React app: UI components, routes, state management label Aug 24, 2026
@FelixTJDietrich
FelixTJDietrich force-pushed the 1493-spring-correctness-rules branch from bbdba03 to 591a093 Compare August 24, 2026 13:44
@FelixTJDietrich
FelixTJDietrich merged commit f581a6a into main Aug 24, 2026
47 checks passed
@FelixTJDietrich
FelixTJDietrich deleted the 1493-spring-correctness-rules branch August 24, 2026 14:24
@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation Preview

Preview has been removed (PR closed)

FelixTJDietrich added a commit that referenced this pull request Aug 24, 2026
#1517 added a rule that a @transactional method may not be reached by
self-invocation, because the call never passes the proxy and the inner annotation
is decorative. Both classes this branch adds broke it seven times: the convenience
overloads of the coverage service and the delivery policy delegated to each other,
so only the outermost annotation ever took effect.

Each public entry point now opens its own read-only transaction and goes straight
to a private implementation that assumes one. Behaviour is unchanged — the reads
were already running inside the outer transaction — but the annotations now say
what actually happens.

The same merge made the two policy entry points public, which is what makes their
@transactional apply at all.
FelixTJDietrich added a commit that referenced this pull request Aug 24, 2026
#1517 added a rule that a @transactional method may not be reached by
self-invocation, because the call never passes the proxy and the inner annotation
is decorative. Both classes this branch adds broke it seven times: the convenience
overloads of the coverage service and the delivery policy delegated to each other,
so only the outermost annotation ever took effect.

Each public entry point now opens its own read-only transaction and goes straight
to a private implementation that assumes one. Behaviour is unchanged — the reads
were already running inside the outer transaction — but the annotations now say
what actually happens.

The same merge made the two policy entry points public, which is what makes their
@transactional apply at all.
FelixTJDietrich added a commit that referenced this pull request Aug 25, 2026
#1517 added a rule that a @transactional method may not be reached by
self-invocation, because the call never passes the proxy and the inner annotation
is decorative. Both classes this branch adds broke it seven times: the convenience
overloads of the coverage service and the delivery policy delegated to each other,
so only the outermost annotation ever took effect.

Each public entry point now opens its own read-only transaction and goes straight
to a private implementation that assumes one. Behaviour is unchanged — the reads
were already running inside the outer transaction — but the annotations now say
what actually happens.

The same merge made the two policy entry points public, which is what makes their
@transactional apply at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

application-server Spring Boot server: APIs, business logic, database bug Something isn't working size:XXL This PR changes 1000+ lines, ignoring generated files. test Unit tests, integration tests, test infrastructure webapp React app: UI components, routes, state management

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enforce Spring correctness rules with ArchUnit

1 participant